为什么使用Symfony?
为什么不使用原生的PHP写程序, 而使用诸如symfony这样的框架呢?
如果你没有使用其它PHP框架的经验, 或者不太清楚MVC是个什么东东. 那这篇文章就将告诉你!
一个超级简单的博客
用原生的PHP写出来的感觉可能是这样的:
<?php
// index.php
$link = new PDO("mysql:host=localhost;dbname=blog_db", 'myuser', 'mypassword');
$result = $link->query('SELECT id, title FROM post');
?>
<!DOCTYPE html>
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php while ($row = $result->fetch(PDO::FETCH_ASSOC)): ?>
<li>
<a href="/show.php?id=<?php echo $row['id'] ?>">
<?php echo $row['title'] ?>
</a>
</li>
<?php endwhile ?>
</ul>
</body>
</html>
<?php
$link = null;
?>
飞快地把敲完上面的代码, 执行. 这样写当然没有任何问题! 但是, 随着网站规模的扩大, 这样的写法带来了几个问题:
- 没有错误检测, 比如上面的连接数据库代码, 如果连不上了怎么办?
- 糟糕的代码组织, 网站规模扩大的时候,单个文件变的没有办法维护, 你在哪里写代码来接收表单数据? 你怎么验证数据? 你在哪里发送邮件?
- 代码重用性为零, 当所有的东西都在一个文件里面时, 博客的其它部分没有办法使用当前的代码
其实还有其它的问题没有列举出来
分离HTML
以下的代码显示了怎么把逻辑和HTML分开
// index.php
$link = new PDO("mysql:host=localhost;dbname=blog_db", 'myuser', 'mypassword');
$result = $link->query('SELECT id, title FROM post');
$posts = array();
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
$posts[] = $row;
}
$link = null;
// include the HTML presentation code
require 'templates/list.php';
<!DOCTYPE html>
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/show.php?id=<?php echo $post['id'] ?>">
<?php echo $post['title'] ?>
</a>
</li>
<?php endforeach ?>
</ul>
</body>
</html>
按照惯例, index.php 文件包含了所有的逻辑代码, 被称为”控制器”. 不管你用什么编程语言, 什么框架.,“控制器”这个术语你可以听了N遍了. 它处理所有的输入和预处理所有的输出. (预处理的意思是绑定数据和指定输出模板)
在这个例子里面, 连接数据库和获取数据的逻辑和HTML分开了. 有的时候你可能要返回的是其它格式的数据, 比如说json格式的和xml格式的, 你只需要更换模板就可以了. 而不用动控制器里的逻辑代码.
分离应用逻辑
你的博客肯定不止index.php这一个页面, 其它的页面也要用到数据库连接怎么办? 也要用到查询出来的数据怎么办? 复制粘贴? 不, 尝试分离这些公共的东西, 把它们放到另一个文件中.
// model.php
function open_database_connection()
{
$link = new PDO("mysql:host=localhost;dbname=blog_db", 'myuser', 'mypassword');
return $link;
}
function close_database_connection(&$link)
{
$link = null;
}
function get_all_posts()
{
$link = open_database_connection();
$result = $link->query('SELECT id, title FROM post');
$posts = array();
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
$posts[] = $row;
}
close_database_connection($link);
return $posts;
}
这里我们分离出了model.php文件, 因为数据库连接和逻辑代码应该放在model层. 在一个代码组织良好的应用里面, 你的业务逻辑代码应该放在model层.
现在你的控制器将变得十分简洁:
require_once 'model.php';
$posts = get_all_posts();
require 'templates/list.php';
现在, 控制器的唯一任务就是从model层获取数据, 然后调用模板(view层)来渲染数据, 这就是简单的MVC实现.
分离模板
所有分离的目的, 都是为了代码的重用性! 在view层中, 把高度重用的部分抽离出来, 用是模板(layout)
<!-- templates/layout.php -->
<!DOCTYPE html>
<html>
<head>
<title><?php echo $title ?></title>
</head>
<body>
<?php echo $content ?>
</body>
</html>
<?php $title = 'List of Posts' ?>
<?php ob_start() ?>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/show.php?id=<?php echo $post['id'] ?>">
<?php echo $post['title'] ?>
</a>
</li>
<?php endforeach ?>
</ul>
<?php $content = ob_get_clean() ?>
<?php include 'layout.php' ?>
增加显示页面
经过上面一系列的分离, 增加一个页面变得十分方便!
把下面的代码加入到model.php
// model.php
function get_post_by_id($id)
{
$link = open_database_connection();
$id = intval($id);
$result = $link->query('SELECT created_at, title, body FROM post WHERE id = '.$id);
$row = $result->fetch(PDO::FETCH_ASSOC);
close_database_connection($link);
return $row;
}
新建一个show.php 的控制器, 里面的代码也非常地简洁
require_once 'model.php';
$post = get_post_by_id($_GET['id']);
require 'templates/show.php';
新增一个templates/show.php, 继承上面我们分离出来的layout
<?php $title = $post['title'] ?>
<?php ob_start() ?>
<h1><?php echo $post['title'] ?></h1>
<div class="date"><?php echo $post['created_at'] ?></div>
<div class="body">
<?php echo $post['body'] ?>
</div>
<?php $content = ob_get_clean() ?>
<?php include 'layout.php' ?>
就如上面所示, 创建第二个页面非常非常地方便和灵活. 但是, 一个框架能做的事情远不止如此.
所以上面的架构还有些问题, 就是所有的控制器文件必须包含model.php, 如果我要多加一个文件呢? 比如我要多加一个安全过滤用户输入数据的文件来预防sql注入, 假设这个文件名叫security.php, 那所有的控制器都必须包含这个文件, 一个一个控制器加? 如果其中一个文件你忘了加呢?
前端控制器 (单入口模式)
单入口模式, 所有的请求都通过这个入口进
没有用前端控制器的时候
/index.php => Blog post list page (index.php executed)
/show.php => Blog post show page (show.php executed)
使用index.php作为前端控制器
/index.php => Blog post list page (index.php executed)
/index.php/show => Blog post show page (index.php executed)
你可以通过apache 重写功能来隐藏index.php
当你使用前端控制器, 所有的请求都由index.php来处理, 然后经过路由分发到不同的控制器.
创建一个前端控制器
// index.php
// load and initialize any global libraries
require_once 'model.php';
require_once 'controllers.php';
// route the request internally
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if ('/index.php' === $uri) {
list_action();
} elseif ('/index.php/show' === $uri && isset($_GET['id'])) {
show_action($_GET['id']);
} else {
header('HTTP/1.1 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
这样所有的控制器变成了一个个PHP函数, 把它放在一个独立的文件中controller.php.
前端控制器的优势还在于它灵活的路由, 你只需要更改一个位置就够了, 在Symfony框架中, 路由功能更加强大灵活
结语: 如果你是PHP的初学者, 你可能对”路由”, 控制器, 模型, 视图这些术语不太清楚, 没有关系, 不要灰心. 随着你代码的积累, 这些概念会逐渐在你的脑海当中形成.
学习框架不仅仅是学习一个框架, 而是学习WEB开发的原理. 使用框架是为了让我们能更好地组织代码, 而一个框架里面用到的各种设计方法都是为了提高代码的重用性和扩展性,或者安全性。一个设计良好的框架,能让你事半功倍.
任何一个新的编程工具被开发出来都是为了解决一些问题或者更好地解决问题的. 框架亦是如此!