423 123
发新话题
打印

深入理解symfony原理

深入理解symfony原理

深入理解symfony原理

用symfony开发的程序乍看起来有吓人。它包含很多目录和脚本,有PHP类,HTML甚至两者的混合,程序里面有些类很难找到定义的地方,目录深达6层。不过一旦你了解了这些背后的原因,你会突然发现这其实是很自然的,symfony程序的结构就应该是这样。
MVC模式 The MVC Pattern

Symfony基于MVC架构这个经典的Web设计模式,MVC架构包含三层:
模型(model)代表程序操作的信息--业务逻辑。
视图(view)将模型用网页的形式展现出来从而与用户进行交互。
控制器(controller)通过调用合适的模型或者视图来回应用户的动作。

图 2-1 解释了MVC模式
MVC架构把业务逻辑(模型)与展示(试图)分开,从而大大提高了可维护性。例如,如果你的程序需要能同时在标准web浏览器与手持设备上面运行,你只需要一个新的视图(view),而不改变原来的控制器(controller)与模型(model)。控制器(controller)把请求(request)的协议(HTTP,命令行模式,邮件等)与模型和视图分开来。模型抽象化逻辑与数据,从而独立于视图与动作(action),例如,程序使用的数据库类型。
图 2-1 - MVC模式



TOP

MVC层次 MVC Layering

为了帮助你理解MVC的好处,让我们看看如何将一个基本的PHP程序转换成一个MVC架构的程序。这里我们用一个显示blog文章的程序作例子。
普通的写法 Flat Programming

从数据库里面显示数据的一般写法跟例2-1类似

例 2-1 - 普通脚本
[php]
<?php

// 连接,选择数据库
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

// 执行SQL查询
$result = mysql_query('SELECT date, title FROM post', $link);

?>

<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
   <h1>List of Posts</h1>
   <table>
     <tr><th>Date</th><th>Title</th></tr>
<?php
// 用HTML显示结果
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
echo "\t<tr>\n";
printf("\t\t<td> %s </td>\n", $row['date']);
printf("\t\t<td> %s </td>\n", $row['title']);
echo "\t</tr>\n";
}
?>
    </table>
  </body>
</html>

<?php

// 关闭连接
mysql_close($link);

?>

这样的代码写起来很快,执行也快,但是几乎没法维护。下面是这种代码的主要问题:
没有错误检查(如果数据库连接失败怎么办?)
HTML与PHP代码混合在一起,甚至是混合在一起。
只能适用于MySQL数据库。

TOP

分离显示 Isolating the Presentation

例2-1中的'echo'与'printf'使代码难以阅读。如果要修改HTML代码来改进外观的话就需要改动PHP代码。因此代码应该分割成两部分。首先,把纯粹的包含了所有业务逻辑的PHP代码放在一个控制器(controller)脚本里,见例2-2。

例 2-2 - index.php 控制器(controller)部分 [php]

HTML代码,包括一些类似模版的PHP语法,存放在一个显示脚本里,见例2-3。

例 2-3 - view.php 显示部分
[php]
<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
    <h1>List of Posts</h1>
    <table>
      <tr><th>Date</th><th>Title</th></tr>
    <?php foreach ($posts as $post): ?>
      <tr>
        <td><?php echo $post['date'] ?></td>
        <td><?php echo $post['title'] ?></td>
      </tr>
    <?php endforeach; ?>
    </table>
  </body>
</html>

按照经验来说视图是否足够干净取决于它是否仅包括最少的PHP代码,使得没有PHP知识HTML设计师能够理解。视图里最常用的语句是 echo, if/endif, foreach/endforeach。另外,不应用PHP代码输出HTML标签。

所有的逻辑都移到了控制器(controller)脚本,并且仅包含纯PHP代码,没有HTML。事实上,你可以想象同样的控制器可以有完全不同的表现,例如PDF文件或者XML结构。

TOP

分离数据处理 Isolating the Data Manipulation

大部分控制器(controller)脚本代码专注于数据处理。但是假如你需要另一个显示文章列表的控制器,例如输出blog文章的RSS种子的控制器呢?如果你想把所有的数据库查询放在一个地方,避免代码重复呢?如果你决定改变数据模型因为'post'表名改成了'weblog_post'呢?如果你想把数据库从MySQL换成PostgreSQL呢?为了让上面这些假设成为现实,你需要把数据处理代码从控制器里面去掉并把它们放在另外的脚本里面,我们称之为模型,如例2-4所示。

例 2-4 - model.php 模型部分
[php]
<?php

function getAllPosts()
{
  // 连接数据库
  $link = mysql_connect('localhost', 'myuser', 'mypassword');
  mysql_select_db('blog_db', $link);

  // 执行SQL查询
  $result = mysql_query('SELECT date, title FROM post', $link);

  // 填充数组
  $posts = array();
  while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
  {
     $posts[] = $row;
  }

  // 关闭连接
  mysql_close($link);

  return $posts;
}

?>

修改过的控制器如例 2-5 所示。

例 2-5 - index.php 修改过的控制器
[php]
<?php

// Requiring the model
require_once('model.php');

// Retrieving the list of posts
$posts = getAllPosts();

// Requiring the view
require('view.php');

?>

这样控制器的可读性变强了。它唯一的任务是从模型中取得数据然后传给视图。在更复杂的程序里,控制器还要处理请求、用户session、身份验证等。控制器中使用的直观地函数名使得我们不用注释就能读懂。

模型脚本专注于数据访问从而组织在一起。所有与数据层无关的参数(例如请求参数)必须由控制器提供而不能直接被模型访问到。模型函数可以方便的在另一个控制器里面重用。

TOP

MVC以外的分离方式 Layer Separation Beyond MVC

所以MVC架构的原理是把代码根据类型分成三层。数据逻辑代码放在模型里,表现代码放在视图里,应用逻辑代码放在控制器里。

还有其它的设计模式甚至可以使编写代码变得更加容易。模型,视图,控制器层还可以进一步细分。
数据库抽象 Database Abstraction

模型层可以分成数据访问曾与数据库抽象层。这样,数据访问函数不使用与数据库有关的查询语句,由其它的函数执行。如果换数据库系统,只需要修改数据库抽象层。

例2-6是MySQL的数据库抽象层的例子,随后的例2-7是一个简单的数据访问层。

例 2-6 - 模型的数据库抽象层部分
[php]
<?php

function open_connection($host, $user, $password)
{
  return mysql_connect($host, $user, $password);
}

function close_connection($link)
{
  mysql_close($link);
}

function query_database($query, $database, $link)
{
  mysql_select_db($database, $link);

  return mysql_query($query, $link);
}

function fetch_results($result)
{
  return mysql_fetch_array($result, MYSQL_ASSOC);
}

例 2-7 - 模型的数据访问层



[php]
function getAllPosts()
{
  // 连接数据库
  $link = open_connection('localhost', 'myuser', 'mypassword');

  // 执行SQL查询
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);

  // 填充数组
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }

  // 关闭连接
  close_connection($link);

  return $posts;
}

?>

TOP

从上面我们可以看到数据访问层的部分没有数据库引擎有关的函数,从而不依赖于特定的数据库。另外,建立数据库抽象曾的函数可以在很多其它的模型函数中重用。

NOTE 例2-6与例2-7的例子并不十分让人满意,要完成一个完整的数据库抽象层还有很多事情要做(通过数据库无关的查询生成器抽象SQL代码,把所有的函数放到一个类,等等)。但是这本书的目的不是手把手教你怎么写一个数据库抽象层,在第8章里你会看到symfony本身是如何把这些抽象做好的。

TOP

视图元素 View Elements

视图层也可以通过分离代码来优化。应用程序中的网页往往会包含一些固定的元素:页头,图形版面设计,页脚以及全局导航。只有网页的中间部分变化。所以我们把视图分成布局(layout)与模版。布局(layout)一般是整个程序通用的,或者一组页面公用。模版只负责把控制器的变量显示出来。我们需要一些逻辑使这些零件(components)和在一起能够起作用,这就是视图逻辑。根据这些原则,例2-3的视图部分可以分成3部分,如例2-8,2-9,2-10所示。

例 2-8 - mytemplate.php 视图的模版部分
[php]
<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post['date'] ?></td>
    <td><?php echo $post['title'] ?></td>
  </tr>
<?php endforeach; ?>
</table>

例 2-9 - 视图的视图逻辑部分
[php]
<?php

$title = 'List of Posts';
$content = include('mytemplate.php');

?>

例 2-10 - 视图的布局部分
[php]
<html>
  <head>
    <title><?php echo $title ?></title>
  </head>
  <body>
    <?php echo $content ?>
  </body>
</html>

TOP

动作与前端控制器 Action and Front Controller

在上一个例子里,控制器(controller)并没有作太多事情,但是在真正的web应用程序里面,控制器要做很多事情。这些事情中的一些重要部分对于所有的控制器都要做。这些事情包括处理请求、安全处理、载入应用程序配置信息,以及一些杂事。所以控制器经常被分成整个应用程序唯一的前端控制器和只负责某个特定页面的动作。

前端控制器的一个很大的好处他是整个应用程序唯一的入口。如果你决定关闭应用程序,你只要修改前端控制器脚本。如果一个应用程序没有前端控制器,那就要单独的关掉每一个控制器。

TOP

面向对象 Object Orientation

所有前面的例子都是面向过程的。现代编程语言的面向对象特性能简化编程,因为对象可以封装逻辑,继承,以及提供干净的命名规则。

在非面向对象的语言里面实现MVC架构会引起命名空间及代码重复的问题,代码会比较难以阅读。

开发者通过面向对象的方式可以通过视图对象,控制器对象,模型对象把之前例子里面的函数转换成方法。这是MVC架构必须的。

TIP 如果你想更详细的了解面向对象环境中的web应用程序设计模式,请阅读《Patterns of Enterprise Application Architecture》(作者:Martin Fowler, 出版 Addison-Wesley, ISBN: 0-32112-742-0)。这本书里面的代码用Java或者C#写的,PHP开发者也可以读一读。

TOP

Symfony的MVC实现方式 Symfony's MVC Implementation
暂停一下,一个显示blog文章列表的页面,有多少部分组成?如图2-2所示,由下面的部分组成:
模型层
数据库抽象
数据访问
视图层
视图
模版
布局
控制器层
前端控制器
动作
7个脚本--每次修改一个页面需要打开这么多文件!可是,symfony做了些简化。虽然使用最好的MVC架构,symfony的方式使得开发程序更加快速容易。
首先,前端控制器是应用程序里所有的动作共用的。可以有多个控制器与多个布局,但是只需要一个前端控制器。前端控制器是纯MVC逻辑组件,你不必自己写一个,因为symfony会为你生成一个。
另外一个好消息是模型层的类也是根据数据结构自动生成的。这是由Propel库完成的,它有类的构架与代码生成功能。如果Propel找到外键或者日期字段,它会生成特殊的存取方法,这使得数据处理非常容易。另外,数据库抽象也是完全看不见的,它是由另外一个Creole组件处理的。所以如果你决定更换数据库引擎,你不必重写代码。你只要修改配置参数就可以了。
最后一件事情视图逻辑可以很容易的转换成以一个配置文件,不需要编写程序。
图 2-2 - symfony工作流程




这就是说在symfony里面显示文章的例子之需要3个文件,如例2-11,2-23,2-12所示。
例 2-11 - list 动作, myproject/apps/myapp/modules/weblog/actions/actions.class.php
[php]
<?php
class weblogActions extends sfActions
{
  public function executeList()
  {
    $this->posts = PostPeer::doSelect(new Criteria());
  }
}
?>
例 2-12 - list 模版, myproject/apps/myapp/modules/weblog/templates/listSuccess.php
[php]
<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post->getDate() ?></td>
    <td><?php echo $post->getTitle() ?></td>
  </tr>
<?php endforeach; ?>
</table>
例 2-13 - list 视图, myproject/apps/myapp/modules/weblog/config/view.yml
listSuccess:
  metas: { title: List of Posts }
另外,你需要定义一个布局,如例2-14,但是它可以多次重用。
例 2-14 - 布局 myproject/apps/myapp/templates/layout.php
[php]
<html>
  <head>
    <?php echo include_title() ?>
  </head>
  <body>
    <?php echo $sf_data->getRaw('sf_content') ?>
  </body>
</html>
这些就是全部的了。你只需要这些代码来显示与例2-1完全一样的页面。余下的事情(使所有的组成部分共同工作)由symfony来处理。如果你计算行数,会发现用MVC架构的symfony来实现显示文章列表花的时间和编写的代码不比写一个普通脚本要多。不过,这样做的巨大好处是,代码组织得十分清楚,可重用,灵活性还有更多的乐趣。作为奖励,你会得到XHTML兼容性,调试能力,简单的配置,数据库抽象,智能URL定向,多种环境,还有很多开发工具。

TOP

 423 123
发新话题