symfony学习笔记2—纯的PHP代码和symfony的区别
Symfony vs 纯PHP
为啥symfony比普通的php文件访问要好?
这一章我们写一个简单的php文件项目,然后组织它,你会发现为什么web应用会发展到现在这个样子。最后我们将学习symfony如何重用代码。
使用纯PHP创建一个简单博客程序
这里我们先使用纯php(flat php我擦 ,怎么翻译呢,就是php文件,但是谁不是php文件呢?)创建一个博客程序,先写一个文章列表,这段代码很直接,但是很脏。
<?php
// index.php
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
$result = mysql_query('SELECT id, title FROM post', $link);
?>
<!DOCTYPE html>
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>
<ul>
<?php while ($row = mysql_fetch_assoc($result)): ?>
<li>
<a href="/show.php?id=<?php echo $row['id'] ?>">
<?php echo $row['title'] ?>
</a>
</li>
<?php endwhile ?>
</ul>
</body>
</html>
<?php
mysql_close($link);
?>
这个很简单,也很好写,但是随着应用逻辑增多很难维护。这里有一些问题:
1.没有错误检查,如果数据库连接失败怎么办
2.没有组织,如果应用变大,逻辑增多,这个文件将会变得很大,不可维护,从那里验证输入,从那里处理请求,最终写成一团乱码
3.代码不可重用,所有代码都放在一个文件中,没法重用
还有一个问题没有提到,如何从数据库中取数据,symfony使用Doctrine(一种ORM)来获取数据很方便。
展现分离
下面做一些该进将逻辑和html展现分离,代码如下:
<?php
// index.php
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
$result = mysql_query('SELECT id, title FROM post', $link);
$posts = array();
while ($row = mysql_fetch_assoc($result)) {
$posts[] = $row;
}
mysql_close($link);
// include the HTML presentation code
require 'templates/list.php';
现在把hmtl内容放在另外一个文件中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="/read?id=<?php echo $post['id'] ?>">
<?php echo $post['title'] ?>
</a>
</li>
<?php endforeach ?>
</ul>
</body>
</html>
一般index.php那个可以叫做控制器,控制这个词在很多场合都用到,不管任何语言和框架,它指处理用户输入和返回响应的地方。在这个例子中控制器从数据库中查数据,然后包含了一个展现数据的文件。这样分离之后如果想修改展示数据的方式例如list.json.php就很容易了
业务逻辑分离
目前这个应用只包含一个页面,但是如果增加第二个页面也使用相同的数据连接,相同的传递数据,所以我们将主要的获取数据的逻辑分离出来放在一个model.php中,如下:
<?php
// model.php
function open_database_connection()
{
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
return $link;
}
function close_database_connection($link)
{
mysql_close($link);
}
function get_all_posts()
{
$link = open_database_connection();
$result = mysql_query('SELECT id, title FROM post', $link);
$posts = array();
while ($row = mysql_fetch_assoc($result)) {
$posts[] = $row;
}
close_database_connection($link);
return $posts;
}
我们把这个文件命名为model.php是应为在应用中通常将获取数据层叫做model,通常主要的业务逻辑放在model里面。
现在这个控制器index.php可易写成下面这样:
<?php
require_once 'model.php';
$posts = get_all_posts();
require 'templates/list.php';
现在这个控制器主要的功能就是从model中获取数据然后调用模板渲染数据,这是个很简单的模型-视图-控制器的例子
布局分离
到目前为止我们已经涉及到三个不同的文件,几乎复用了所有的代码,只有一个地方我们没有用到就是布局文件,下面创建一个布局文件layout.php
<!-- templates/layout.php -->
<!DOCTYPE html>
<html>
<head>
<title><?php echo $title ?></title>
</head>
<body>
<?php echo $content ?>
</body>
</html>
模板templete/list.php,现在可以继承布局了。
<?php $title = 'List of Posts' ?>
<?php ob_start() ?>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/read?id=<?php echo $post['id'] ?>">
<?php echo $post['title'] ?>
</a>
</li>
<?php endforeach ?>
</ul>
<?php $content = ob_get_clean() ?>
<?php include 'layout.php' ?>
现在可以通用这个layout了,但是还需要一些丑陋的php函数例如ob_start(),ob_get_clean()方法,下面再将symfony的处理方式。
添加博客”show”页面
博客列表页面已经重新设计,代码可以复用了。现在添加一个展示博客的页面,向这个页面传递参数ID。首先在model.php中新建一个方法,如下:
// model.php
function get_post_by_id($id)
{
$link = open_database_connection();
$id = intval($id);
$query = 'SELECT date, title, body FROM post WHERE id = '.$id;
$result = mysql_query($query);
$row = mysql_fetch_assoc($result);
close_database_connection($link);
return $row;
}
然后新建一个文件show.php,控制器
<?php
require_once 'model.php';
$post = get_post_by_id($_GET['id']);
require 'templates/show.php';
最后新建一个模板templates/show.php,渲染文件
<?php $title = $post['title'] ?>
<?php ob_start() ?>
<h1><?php echo $post['title'] ?></h1>
<div class="date"><?php echo $post['date'] ?></div>
<div class="body">
<?php echo $post['body'] ?>
</div>
<?php $content = ob_get_clean() ?>
<?php include 'layout.php' ?>
新建这个页面的时候已经很容易,没有重复代码,还是还是有些问题可能导致问题,例如丢失参数id将会使页面报错。如果这个导致404错误到还好,最坏的情况是sql注入。
另外一个问题是每个控制器文件都必须应用model.php,当我们要访问另外一个表,就要在这个控制器中添加另外一个model,这个是比较麻烦的。
前端控制器解决方案
解决这个问题的方法是前端控制器,通过这个控制器文件所有的请求都可以被处理,唯一要做的是修改url,这样更加灵活。
Without a front controller
/index.php => Blog post list page (index.php executed)
/show.php => Blog post show page (show.php executed)
With index.php as the front controller
/index.php => Blog post list page (index.php executed)
/index.php/show => Blog post show page (index.php executed)
用apache中的重写功能可以省略“index.php”这样的话访问就更加简单了,例如/show
使用前端控制器只有单独的一个index.php文件可以处理所有的请求,例如访问show页面,/index.php/show,将会最终执行index.php,这是一个很强大的功能。我怎么没看出来。
创建前端控制器
现在我们再往前走一大步,用一个文件处理所有的请求,修改index.php文件如下:
<?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('Status: 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
为了组织好代码控制器文件(index.php,show.php)现在变成了方法并且放在一个文件中,controllers.php如下:
function list_action()
{
$posts = get_all_posts();
require 'templates/list.php';
}
function show_action($id)
{
$post = get_post_by_id($id);
require 'templates/show.php';
}
作为一个前端控制器index.php中有新的规则,一是加载核心的类,可以调用到控制方法list_action()和show_action()。事实上这里的前端路由已经和symfony的机制很接近了。
现在,这个这个应用已经从一个简单的php文件重构成一个有组织的结构,最大程度的复用代码,但是还是看到一些代码不协调。为了完成这个blog我们可能需要写很多类似的代码,还要处理用户输入,验证,日志,安全等等。
初始symfony
牛逼哄哄的symfony出场了。(Symfony to the rescue)我想对手册作者说,你能不能不装逼,把要讲的东西讲清楚就好了!在使用之前我们要下载symfony,可以使用composer,这个工具可以下载正确版本的symfony和它所依赖的所有文件,提供一个自动下载器,我擦你妹 ,autoloader是个可以一个工具,用来使用类但是不显示的包含类文件,我擦啊 你他妈还玩花啊,不引用就用,构高级的。
有没有考虑windows用户的感受你!
在根目录下创建一个文件composer.json,如下:
{
"require": {
"symfony/symfony": "2.6.*"
},
"autoload": {
"files": ["model.php","controllers.php"]
}
}
然后下载Composer并安装,命令如下:
$ composer install
下载依赖文件的时候Composer创建了一个文件vendor/autoload.php,这个文件中有所有symfony frameword中所有需要的文件,就是composer.jeson中的。当初说好的symfony处理请求响应呢, 这里干吗呢?
symfony提供Request和Response两个类,他们处理请求和响应,这个不知道重复多少遍了!下面用symfony来写这个blog
<?php
// index.php
require_once 'vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$uri = $request->getPathInfo();
if ('/' == $uri) {
$response = list_action();
} elseif ('/show' == $uri && $request->query->has('id')) {
$response = show_action($request->query->get('id'));
} else {
$html = '<html><body><h1>Page Not Found</h1></body></html>';
$response = new Response($html, Response::HTTP_NOT_FOUND);
}
// echo the headers and send the response
$response->send();
控制器现在返回一个response对象,可以添加一个render_template()方法,和symfony中的模板很像,如下:
// controllers.php
use Symfony\Component\HttpFoundation\Response;
function list_action()
{
$posts = get_all_posts();
$html = render_template('templates/list.php', array('posts' => $posts));
return new Response($html);
}
function show_action($id)
{
$post = get_post_by_id($id);
$html = render_template('templates/show.php', array('post' => $post));
return new Response($html);
}
// helper function to render templates
function render_template($path, array $args)
{
extract($args);
ob_start();
require $path;
$html = ob_get_clean();
return $html;
}
好吧, 通过使用symfony我们的程序更加灵活,又来,request提供访问http请求的可靠方法,getPathInfo()方法返回一个干净的url,例如:/show,/index.php/show,这样即使访问index.php/show,应用程序可以聪明的调用show_action()方法。
返回响应的时候response对象可以灵活的返回结果。
symfony简单示例
现在这个blog已经做好,但是里面还有很多代码,但是有没有办法使用更好的代码来实现这个blog,有没有办法来代替ob_start(),和ob_get_clean()呢?可以使用symfony来简化这些,如下:
// src/AppBundle/Controller/BlogController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BlogController extends Controller
{
public function listAction()
{
$posts = $this->get('doctrine')
->getManager()
->createQuery('SELECT p FROM AcmeBlogBundle:Post p')
->execute();
return $this->render('Blog/list.html.php', array('posts' => $posts));
}
public function showAction($id)
{
$post = $this->get('doctrine')
->getManager()
->getRepository('AppBundle:Post')
->find($id);
if (!$post) {
// cause the 404 page not found to be displayed
throw $this->createNotFoundException();
}
return $this->render('Blog/show.html.php', array('post' => $post));
}
}
这两个控制器依然很重要哦,用Doctrin ORM来从数据库中来获取数据,模板组件渲染模板并返回一个Response对象,模板现在看上去比较简单,
<!-- app/Resources/views/Blog/list.html.php -->
<?php $view->extend('layout.html.php') ?>
<?php $view['slots']->set('title', 'List of Posts') ?>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="<?php echo $view['router']->generate(
'blog_show',
array('id' => $post->getId())
) ?>">
<?php echo $post->getTitle() ?>
</a>
</li>
<?php endforeach ?>
</ul>
layout布局是差不多的
<!-- app/Resources/views/layout.html.php -->
<!DOCTYPE html>
<html>
<head>
<title><?php echo $view['slots']->output(
'title',
'Default title'
) ?></title>
</head>
<body>
<?php echo $view['slots']->output('_content') ?>
</body>
</html>
妈蛋 $view从那里来的,没交代清除。
当symfony引擎启动的时候,妈蛋啊 , 说的这么高级 ,不就是访问网站的时候么。它需要通过请求信息和一个映射表知道执行那一个控制器。路由配置提供这个信息,如下:
# app/config/routing.yml
blog_list:
path: /blog
defaults: { _controller: AppBundle:Blog:list }
blog_show:
path: /blog/show/{id}
defaults: { _controller: AppBundle:Blog:show }
然后symfony处理一些简单的任务,前端控制器是很简单的,创建之后你就不需要再管它,说的轻巧,吃根灯草。还有如果使用symfony distribution根本不需要创建它,好吧,被你的装逼精神深深的折服了!
// web/app.php
require_once __DIR__.'/../app/bootstrap.php';
require_once __DIR__.'/../app/AppKernel.php';
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('prod', false);
$kernel->handle(Request::createFromGlobals())->send();
前端控制器的唯一任务就是初始化symfony引擎Kernel,然后发送一个请求对象,symfony核心然后用这个路由映射找到执行那个控制器,最后控制器方法返回最终的响应对象。(编者一再强调很简单)
symfony的优点
开启装逼模式
什么是symfony framework,symfony framework是一个php类库,包含两个主要的任务
1.提供可选的第三方的类库组件(symfony components)
2.提供直观的配置和一个可以把很多php代码片段组合起来的胶水类库
symfony的终极目标是整合很多互不影响的组件来为开发者提供一致的使用体验。symfony本身也是一个束可以被配置和替换。symfony提供一套快速开发的工具而不需要在项目中添加额外的组件。普通用户可以快速开发,高手可以任意驰骋。
symfony学习笔记2—纯的PHP代码和symfony的区别的更多相关文章
- python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息,抓取政府网新闻内容
python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI,采用Python语言编写 ...
- openstack学习笔记一 虚拟机启动过程代码跟踪
openstack学习笔记一 虚拟机启动过程代码跟踪 本文主要通过对虚拟机创建过程的代码跟踪.观察虚拟机启动任务状态的变化,来透彻理解openstack各组件之间的作用过程. 当从horizon界面发 ...
- 【学习笔记】OI玄学道—代码坑点
[学习笔记]\(OI\) 玄学道-代码坑点 [目录] [逻辑运算符的短路运算] [\(cmath\)里的贝塞尔函数] 一:[逻辑运算符的短路运算] [运算规则] && 和 || 属于逻 ...
- JPG学习笔记3(附完整代码)
#topics h2 { background: rgba(43, 102, 149, 1); border-radius: 6px; box-shadow: 0 0 1px rgba(95, 90, ...
- JPG学习笔记4(附完整代码)
#topics h2 { background: rgba(43, 102, 149, 1); border-radius: 6px; box-shadow: 0 0 1px rgba(95, 90, ...
- symfony学习笔记1—简介
1.symfony快速入门还是先看代码结构把,这个是拿到代码的第一印象,app/:整个应用的配置,模版,translations,这个可能是多语言文件什么,src/:项目php文件,vendor/:第 ...
- redis 学习笔记(7)-cluster 客户端(jedis)代码示例
上节学习了cluster的搭建及redis-cli终端下如何操作,但是更常用的场景是在程序代码里对cluster读写,这需要redis-client对cluster模式的支持,目前spring-dat ...
- [知了堂学习笔记]_纯JS制作《飞机大战》游戏_第1讲(实现思路与游戏界面的实现)
整体效果展示: 一.实现思路 如图,这是我完成该项目的一个逻辑图,也是一个功能模块完成的顺序图. 游戏界面的完成 英雄飞机对象实现,在实现发射子弹方法过程中,又引出了子弹对象并实现.在此时,英雄飞机能 ...
- LearnOpenGL学习笔记(一)——现有代码理解
首先,给出这次学习的代码原网址.------>原作者的源代码 (黑体是源码,注释是写的.) 引用的库(预编译): #include <glad/glad.h> //控制编译时函数的具 ...
随机推荐
- 安装sublime text2 for ubuntu
Add our Sublime Text 2 Ubuntu PPA using the following commands: sudo add-apt-repository ppa:webu ...
- Formtastic: Forms Made Crazy Easy for Rails Developers
Formtastic is a Rails plugin by Justin French that aims to take the headaches out of building forms ...
- Hibernate框架 hilo 方式配置MySQL 数据库的主键自增
hilo(高低位方式high low)是hibernate中最常用的一种生成方式,需要一张额外的表保存hi的值.保存hi值的表至少有一条记录(只与第一条记录有关),否则会出现错误.可以跨数据库. 创建 ...
- lua闭包与简易迭代器实现
1.什么是闭包 闭包,又称闭合函数(closure).通常,如果将一个函数写在另一个函数内,那么这个在内部的函数就可以访问到外部函数中的局部变量,这个特征就是词法域,有些资料上也叫它词法定界.闭包指的 ...
- [九省联考 2018]一双木棋chess
Description 题库链接 给出一个 \(n\times m\) 的棋盘,棋盘的每个格子有两个权值 \(A,B\) . Alice 和 Bob 轮流操作在棋盘上放棋子,一个格子能放棋子的前提条件 ...
- Feign性能优化注意事项--超时
Caused by: java.lang.IllegalStateException: PathVariable annotation was empty on param 1. at feign ...
- MVC部分视图
// 以视图名使用当前文件夹下的视图 // 如果没有找到,则搜索 Shared 文件夹 @Html.Partial("ViewName") @Html.Partial(“视图” ...
- 2Java基础语法
1.标识符 1.1 标识符以字母.下划线.美元符开头 1.2 标识符由字母.下划线.美元符或数字组成 1.3 标识符区分大小写 1.4 不能与Java关键字同名 ...
- 【转】手机web前端调试页面的几种方式
前言 PC端web页面调试比较容易,这里主要说几种移动端调试的方法,从简单到复杂.从模拟调试到远程调试,大概分为几部分: 1.Chrome DevTools(谷歌浏览器)的模拟手机调试 2.weinr ...
- UIImagePickerController本地图片视频,相机录像机使用
1.添加framework:MobileCoreServices 2.头:#import <MobileCoreServices/MobileCoreServices.h> 大致代码: U ...