用
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),例如,程序使用的
数据库类型。
字串7
图 2-1 - MVC模式

字串3
字串1
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))
字串9
{
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数据库。
分离显示 Isolating the Presentation
例2-1中的'echo'与'printf'使代码难以阅读。如果要修改HTML代码来改进外观的话就需要改动PHP代码。因此代码应该分割成两部分。首先,把纯粹的包含了所有业务逻辑的PHP代码放在一个控制器(controller)脚本里,见例2-2。
例 2-2 - index.php 控制器(controller)部分 [php]
字串9
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标签。
字串8
所有的逻辑都移到了控制器(controller)脚本,并且仅包含纯PHP代码,没有HTML。事实上,你可以想象同样的控制器可以有完全不同的表现,例如
PDF文件或者
XML结构。
分离数据处理 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查询
字串6
$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、身份验证等。控制器中使用的直观地函数名使得我们不用注释就能读懂。
模型脚本专注于数据访问从而组织在一起。所有与数据层无关的参数(例如请求参数)必须由控制器提供而不能直接被模型访问到。模型函数可以方便的在另一个控制器里面重用。