+ -
当前位置:首页 → 问答吧 → symfony框架简单流程

symfony框架简单流程

时间:2008-06-05

来源:互联网

1建立一个专案目录    symfony init-project 专案名

2创建一个程式        init-app 程式名

3创建一个模块框架    init-module 程式名 模块名 (需手动添加动作和摸版)

3设置虚拟主机


<VirtualHost 127.0.0.1>
    ServerAdmin website1@domain
    DocumentRoot "C:\www/danfei/web"
    ServerName www.danfei.com
    ErrorLog logs/www.sandbox.com-error_log
    CustomLog logs/www.sandbox.com_log common
    Alias /sf "C:\program files/php/PEAR/pear/data/symfony/web/sf"
    <Directory "C:\www/danfei/web">
      AllowOverride All
      Order deny,allow
      Allow from all
    </Directory>
</VirtualHost>
<Directory "C:\program files/php/PEAR/pear/data/symfony/web/sf">
      AllowOverride All
      Order deny,allow
      Allow from all
</Directory>


5数据库连接结构    databases.yml

连接数据库的配置文件
修改config/databases.yml,其param:下的参数部分分别为:
phptype:    要使用的数据库类型
hostspec:    要用的数据库所在的域名
database:    要使用的数据库的名字
username:    连接数据库所用的用户名
password:    连接数据库所用的密码

2 修改config/propel.ini,所需要修改的部分如下:
propel.database:                需要使用的数据库类型
propel.database.createUrl:            需要使用的数据库所在的主机地址,其格式为mysql://username:password@domain/,分别为用户名,密码以及域名
propel.database.url:                需要使用的数据库的地址,其格式为:mysql://username:password@domain/database,分别为用户名,密码以及数据库的名字


all:
  propel:
    class:          sfPropelDatabase
    param:
    phptype:      mysql
    host:         localhost
    database:     danfei
    username:     root
    password:     root

  
6数据表模结构    schema.yml

propel:
  question:
    _attributes: { phpName: Question }
    id:         
    user_id:     { type: integer, foreignTable: user, foreignReference: id  }
    title:       { type: longvarchar, required: true }
    body:        { type: longvarchar, required: true }
    created_at:
    updated_at:
  answer:
    _attributes: { phpName: Answer }
    id:         
    question_id: { type: integer, foreignTable: question, foreignReference: id  }
    user_id:     { type: integer, foreignTable: user, foreignReference: id  }
    body:        { type: longvarchar }
    created_at:
  user:
      _attributes: { phpName: User }
      id:         
      nickname:    { type: varchar(50), required: true, index: true }
      first_name:  { type:varchar(100) }
      last_name:   { type:varchar(100) }
      created_at:
  interest:
      _attributes: { phpName: Interest }
      question_id: { type: integer, foreignTable: question, foreignReference: id }
      user_id:     { type: integer, foreignTable: user, foreignReference: id }
      created_at:  
  relevancy:
      _attributes: { phpName: Relevancy }
      answer_id:   { type: integer, foreignTable: answer, foreignReference: id }
      user_id:     { type: integer, foreignTable: user, foreignReference: id }
      score:       { type: integer }
      created_at:  

7物件模型建立    propel-build-model

8数据SQL生成    propel-build-sql

9数据表插入    propel-insert-sql

10动作程式框架生成    propel-generate-crud front question Question(自动生成一个默认的动作和摸版)

11外观界面    apps/frontend/templates/layout.php

12页面修改与CSS加载    frontend/config/view.yml

13清除缓存    symfony cc

14重制定默认主页    apps/frontend/config/routing.yml     homepage:

15动作数据传递模版    apps/frontend/modules/程式/actions/actions.class.php

16表单验证
methods:
   post:           [author, email, body]
   get:            [author, email, body]
   
fillin:
   enabled:        on

names:
   author:         
      required:       Yes
      required_msg:   作者不能为空

   email:
      required:       No
      validators:     emailValidator
   
   body:              
      required:       No
      validators:     emailValidator
      
emailValidator:
   class:             sfEmailValidator
   param:
      email_error:    电子邮件地址不正确      

17动作页面        Action结尾    摸版页面    Success结尾

18由一个CRUD生成器创建的动作列表如下:

名字        描述
list        显示一个数据表的所有记录
index       转向到list
show        显示一个指定记录的所有数据域
edit        显示一个表单来创建一个新的记录或是编辑一个已存在的记录
update      通过在请求中指定的参数来修改一个记录,然后转向到show
delete      从数据表中删除一个指定的记录

19数据导入
在askeet/data/fixtures/下创建新test data file(你需要手动创建目录)
在askeet/batch/下建立一个文件load_data.php:
define('SF_ROOT_DIR', realpath(dirname(__FILE__).'/..'));
define('SF_APP', 'frontend');
define('SF_ENVIRONMENT', 'dev');
define('SF_DEBUG', true);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
$databaseManager = new sfDatabaseManager();
$databaseManager->initialize();
$data = new sfPropelData();
$data->setDeleteCurrentData(false);        //不加此项将在导入时先删除之前的数据
$data->loadData(sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.'fixtures');

20动作方法的名字总是execute'Xxx'()的形式,其中名字的第二部分是动作的名字,并且第一个字母大写。

21.app下的setting.yml
   修改apps\frontend\config\settings.yml里的内容参数为off:
   prod:
     .settings:
     no_script_name: off

作者: 单飞雪莲   发布时间: 2008-06-04

symfony是我看到的好评最多的框架  可是只支持PHP5 还有中文的文档教程非常少 所以暂时没有打算学它

作者: tillgogo   发布时间: 2008-06-04

为啥不多翻译一些呢。

作者: 大鲨鱼   发布时间: 2008-06-05

Symfony能做什么? 使用Symfony需要掌握哪些知识? 读完这一章你就知道答案了。

通过自动化完成一些特定的开发模式,框架能简化应用程序开发。 框架还可以为程序代码增加结构,从而促使程序员编出更好、更易读的、更容易维护的代码。最终,框架大大简化了程序编写,因为它把复杂的操作封装成了简单的声明。

     Symfony是一个完整的web应用开发框架,它为加速开发提供了几个关键功能。首先,它把web应用的业务规则,服务器逻辑还有表现页面分割开来。它为减少开发复杂web应用提供了大量的工具、类。另外,它把一些常用的任务变成了自动化的方式从而使开发人员能够完全专注于一项应用任务的细节。这些优点综合起来的结果就是我们每次开发新的web应用的时候不用重新发明轮子了!

     Symfony完全由PHP 5编写。它经过了广泛的测试并被用于很多实际的项目中,一些高访问量的电子商务网站正在使用Symfony。Symfony与包括MySQL、PostgreSQL、Oracle还有Microsoft SQL Server在内的大多数数据库系统兼容。Symfony能够在*nix与Windows平台运行。我们现在来看一下Symfony具体有哪些功能。

作者: 单飞雪莲   发布时间: 2008-06-05

Symfony的功能

>Symfony是为了实现下面的功能而开发的:

>能够在在大多数平台上很容易的安装配置 (保证能在标准*nix和Windows平台)

>不依赖某种特定的数据库

>多数情况下容易使用,并且有足够的弹性来实现更复杂的功能

>遵循惯例重于配置原则(convention over configuration)--开发人员只需要配置与惯例不同的部分

>能够适应大多数web最优方法(best practices)与设计模式(design patterns)

>可供企业使用--能够适应现有的IT策略与体系,并且很稳定适合长期项目

>非常易读的代码、带有phpDocumentor注释,维护轻松

>易于扩展,允许与非Symfony的库进行整合

作者: 单飞雪莲   发布时间: 2008-06-05

Symfony自动化Web项目功能

>Symfony能够自动化web开发的大部分要素:

>内建的国际化层能够实现数据与界面翻译,还有内容本地化。

>表现部分由于使用了模版和布局,不懂任何框架知识的网页设计师也可以掌握。辅助函数(helpers)封装了大量的代码从而减少了表现代码的数量。

>表单支持自动验证和重新提交,这确保了数据库里的数据的质量,用户体验也更好。

>输出转义能够保护程序不受到通过错误数据进行的攻击。

>缓存管理功能减轻了带宽和服务器消耗。

>使用验证与证书功能能很容易的实现受限制区域还有用户权限管理。

>路由与漂亮的URL使网页的网址容易被搜索引擎接受(search-engine friendly)。

>内建的e-mail与API管理使web程序超越传统的浏览器交互(browser interactions)。

>方便的数据列表提供了自动的分页、排序还有筛选功能。

>Factories, plug-ins, and mixins provide a high level of extensibility.

>Ajax交互很容易实现,以一行代码(辅助函数helper)去封装跨浏览器支持的Javascript

作者: 单飞雪莲   发布时间: 2008-06-05

symfony开发环境与工具

symfony可以完全的定制以满足有自己代码规范与项目管理规则的企业。

它自带了一些开发环境还有多种工具来实现自动化软件工程任务:

>代码生成工具非常适合于原形设计与一键式后台管理(one-click back-end administration)。

>内建的单元测试与功能测试框架为测试驱动的开发提供了完美的工具。

>测试工具条能够把当前页面的所有开发者需要的信息显示出来从而加快调试速度。

>命令行借口使两台服务器之间部署自动化。

>能够有效地即时更新配置。

>记录功能让管理员能够掌握程序的一举一动。

作者: 单飞雪莲   发布时间: 2008-06-05

当Symfony的网站(http://www.symfony-project.com/)推出后,全世界许许多多的开发者下载、安装、阅读了在线文档,随后去开发了他们的第一个Symfony的程序,社区开始热闹起来。

中文支持社区:PHP开发资源网(http://bbs.phpres.com)论坛 > Symfony开发框架专区
                     Symfony中国: http://symfony-project.cn/ 翻译和创作关于Symfony应用。

当时web应用程序开发框框架正在变得流行,开发者们对全功能的PHP开发框架的需求很高。Symfony由于代码质量还有文档的数量成为一个引人注目的解决方案,这也是它对其他框架的优势。很快就有志愿者参与进来,提出修改或增强的意见,校对文档,还有参与到其他一些必要的工作中来。

我们欢迎所有愿意参与的人加入symfony开发,我们提供公开的源代码仓库还有ticket系统。目前symfony的代码主干主要还是Fabien在维护,这保证了代码的质量。

目前,symfony的论坛,邮件列表,还有IRC频道为整个社区提供了很优秀的支持,平均每个问题会有4个回复之多。每天都有人安装symfony,wiki与代码片断部分汇集了大量的用户提交的文档。每个星期平均会出现5新的个symfony项目,这个数字还在不断地增加。

symfony社区是这个框架的第三个力量,我们希望读了本书后你能加入到这个社区中来

作者: 单飞雪莲   发布时间: 2008-06-05

Symfony本身是由PHP 5 (http://www.php.net/) 开发的,symfony专注于利用PHP 5制作网络应用程序。所以,想要彻底地了解symfony框架必须要对PHP 5有十分深刻的理解。

建议熟悉PHP 4但刚刚接触或者不熟悉PHP 5的开发者专注于PHP 5语言的面向对象模型部分。
面向对象程序设计 (OOP)

我们在本章不去详细解释面向对象程序设计(OOP),因为这个话题可以写一本书了。由于symfony大量运用了PHP 5中的面向对象机制,面向对象程序设计(OOP)是学习symfony的先决条件。

维基百科这样解释OOP:

面向对象程序设计(OOP)可以被视作一种在程序中包含各种独立而又互相调用的单位和对象的思想,这与传统的思想刚好相反:传统的面向过程程序设计主张将程序看作一系列函数的集合,或者直接就是一系列对电脑下达的指令。

PHP 5实现了面向对象中的类、对象、方法、继承等。如果你对这些概念不熟悉,建议阅读相关的PHP文档,网址如下 http://www.php.net/manual/zh/language.oop5.basic.php.
魔术方法

PHP对象的一个优势是可以使用魔术方法。 这些个方法可以不需要修改外部代码而重写一个类的默认行为。这使得PHP语法有更少的冗余性和更具有扩展性。这些方法很好识别,为们他们都是以双下划线(__)开始的。

例如, 当显示一个对象的时候, PHP会去暗中去寻找看是否开发者定义过__toString()方法: [php] $myObject = new myClass(); echo $myObject; // Will look for a magic method echo $myObject->__toString();

Symfony使用了魔术方法, 所以你必须完全了解这些概念。 这些在PHP文档中有描述(http://www.php.net/manual/en/language.oop5.magic.php).
PEAR

PEAR是"一个PHP可重用代码的框架和发布系统"。PEAR可以下载、安装、升级及删除PHP脚本。使用PEAR包的时候,不用为了脚本的位置或者怎么找到他们而担心,扩展命令行借口(CLI)也很容易。

PEAR是一个由社区推动的PHP项目,官方发布的PHP中就包含了PEAR。

提示 PEAR网站, http://pear.php.net/, 有PEAR文档与分类的PEAR包下载。

PEAR是最专业的安装PHP库的方法。建议使用PEAR来管理一个由多个项目共用的symfony框架。Symfony的插件(plug-ins)是一种有特殊设置的PEAR包。Symfony本身也可以通过PEAR安装。

使用symfony并不需要懂PEAR命令的语法。你只要知道PEAR的用途还有确定它已经安装好就行了。你可以通过在你的电脑的命令行(CLI)下输入下面面的命令来检查PEAR是否安装:
> pear info pear

这个命令会返回安装在你的电脑上的PEAR的版本。

Symfony项目有自己的PEAR仓库(或频道)。仓库功能只有PEAR 1.4.0以上版本才支持,所以如果你的版本比较老就需要升级。升级PEAR,只要在命令行输入下面的命令就可以了:
> pear upgrade PEAR

作者: 单飞雪莲   发布时间: 2008-06-05

对象关系映射(ORM)

数据库是关系型数据库。 PHP5和Symfony是面向对象的。 为了用面向对象的方法访问数据库, 必须用一个接口来表示对象之间的逻辑关系。 这个接口就叫作对象关系映射或者ORM。

ORM是由对象组成,用来访问数据和保持事务关系。 An ORM is made up of objects that give access to data and keep business rules within themselves.

对象/关系的抽象层的其中一个优点是可以不用直接去访问数据库。 它会使用经过优化的模型对象来访问当前的数据库。

这就意味着在项目中期换一套数据库将是很简单的事情。想象一下当你必须为程序写一个原型的时候,客户并不能确定哪种数据库最适合他们。 你可以先使用SQLite来开发程序, 当客户决定使用MySQL, PostgreSQL或者Oracle的时候,我们只要在配置文件中稍作修改就可以正常工作了。

抽象层封装了数据逻辑。 其他程序并不需要了解SQL的查询语句,却依旧能轻松的访问数据库。 那些对数据库开发者专家也很清楚的知道该做些什么。

使用对象而非记录, 用类而非表,还有其他益处: 你能为你的表增加一些新的存取方法。 例如, 你有一个名叫Client的表,有两个字段,分别是FisrstName和LastName, 你也许想直接获得一个完整的姓名。 在面向对象中, 为一个Client类添加一个访问方法,就像这样:
[php]
public function getName()
{
  return $this->getFirstName.' '.$this->getLastName();
}

所有重复数据访问功能和数据的业务逻辑都可以在对象中维护。 例如,有一个用来生成对象的ShoppingCart类。 我们在结帐时想获得一个总价, 你可以加一个 getTotal() 方法,就像这样:
[php]
public function getTotal()
{
  $total = 0;
  foreach ($this->getItems() as $item)
  {
    $total += $item->getPrice() * $item->getQuantity();
  }
  return $total;
}

就这好了,想象一下要写多长的SQL语句才能完成同样的事情!

Propel,另一个开源项目,是当前最好的基于PHP5的对象/关系抽象层。 Symfony框架无缝集成了Propel, 所以本书大多数的数据处理描述都使用了Propel的语法。 本书将描述如何使用Propel对象, 但是更详细的资料可以参考Propel的网站(http://propel.phpdb.org/trac/)。

作者: 单飞雪莲   发布时间: 2008-06-05

YAML

来自YAML官方网站 (http://www.yaml.org/) 的定义: YAML是一种直观的能够被电脑识别的的数据数据序列化格式,它并且容易被人类阅读,容易与脚本语言交互的。换种说法,YAML是一种非常简单的类似于XML的数据描述语言,语法比XML简单很多。他在描述可以被转化成数组或者hash的数据是非常有用,例如:
[php]
$house = array(
  'family' => array(
    'name'     => 'Doe',
    'parents'  => array('John', 'Jane'),
    'children' => array('Paul', 'Mark', 'Simone')
  ),
  'address' => array(
    'number'   => 34,
    'street'   => 'Main Street',
    'city'     => 'Nowheretown',
    'zipcode'  => '12345'
  )
);

解析这个YAML将会自动创建下面的PHP数组:
house:
  family:
    name:     Doe
    parents:
      - John
      - Jane
    children:
      - Paul
      - Mark
      - Simone
  address:
    number: 34
    street: Main Street
    city: Nowheretown
    zipcode: 12345

在YAML里面,结构通过缩进来表示,连续的项目通过减号"-"来表示,map结构里面的key/value对用冒号":"来分隔。YAML也有用来描述好几行相同结构的数据的缩写语法,数组用'[]'包括起来,hash用'{}'来包括。因此,前面的这个YAML可以缩写成这样:

house:
  family: { name: Doe, parents: [John, Jane], children: [Paul, Mark, Simone] }
  address: { number: 34, street: Main Street, city: Nowheretown, zipcode: 12345 }
YAML是"Yet Another Markup Language(另一种标记语言)"的缩写,读音"yamel",或者"雅梅尔"。这种格式大约是2001年出现的,目前为止已经有多种语言的YAML解析器。

     提示 YAML格式的详细规格可以在YAML官方网站http://www.yaml.org/找到。

     如你所见,写YAML要比XML快得多(不需要关闭标签或者引号),并且比'.ini'文件功能更强(ini文件不支持层次)。所以symfony选择YAML作为配置信息的首选格式。在本书你会看到很多YAML文件,不过它很直观你用不着更深入地研究YAML。
总结

      Symfony是一个PHP 5 web应用程序开发框架。他在PHP语言的基础上增加了一个新层,为加速开发复杂的web应用程序提供了工具。本书全面介绍symfony的使用,你只需要熟悉并且理解现代编程的基本概念 -- 面向对象程序设计(OOP)、对象关系映射(ORM)还有快速程序开发(RAD)。唯一需要的技术背景是PHP 5的知识。

作者: 单飞雪莲   发布时间: 2008-06-05

深入理解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模式








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数据库。
分离显示 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结构。
分离数据处理 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、身份验证等。控制器中使用的直观地函数名使得我们不用注释就能读懂。

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

作者: 单飞雪莲   发布时间: 2008-06-05

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;
}

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

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

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

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

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

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

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

作者: 单飞雪莲   发布时间: 2008-06-05

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定向,多种环境,还有很多开发工具。
symfony核心类 Symfony Core Classes

在本书里你会经常碰到symfony的MVC核心的几个类:
sfController 控制器类。它解析请求并交给动作处理。
sfRequest 保存所有的请求元素(参数,cookie,请求的头 等)。
sfResponse 包含回应的头和内容。它的内容最终会转化为HTML传给用户。
context singleton (由sfContext::getInstance()取得) 保存所有核心对象还有当前的配置的引用,它可以从任何地方访问到。

在第6章你会了解到更多这些对象的信息。

如你所见,所有的symfon类都有一个sf前缀,很多symfony模版中的核心变量也是这样。这样可以避免与你的类名与变量名重复,并使框架核心类更像是一家人,更好辨认。

NOTE symfony的编码规范中,开头字母大写的驼峰字(UpperCamelCase)是变量名与类名的标准。只有两个例外:核心symfony类以小写的sf开头,模版里面的变量使用小写下划线的方式。
代码组织 Code Organization

现在你了解了symfony应用程序的不同组成部分,你可能会想知道它们是怎么组织的。symfon按照项目组织代码,项目文件放在标准的树结构里。
项目结构: 应用程序,模块与动作 Project Structure: Applications, Modules, and Actions
一个symfony项目由一个域名下的服务与操作组成,它们共享同样的对象模型。
在一个项目里,操作逻辑上组成应用程序。同一个项目里面的不同应用程序相互独立。大多数情况,一个项目会包含两个应用程序:一个是前台,一个后台,它们共享同一个数据库。不过一个项目也可以包含很多小网站,每一个站点是一个不同的应用程序。注意应用程序间的链接必须用绝对形式。
每个应用程序由一个或更多模块组成。模块就是功能相近的一个页面或者一组页面。例如,模块 home ,articles, help, shoppingCart, account 等。
模块包含动作,也就是说一个模块可以包含多个动作。例如,shoppingCart模块也许会有add,show与update`等动作。一般来说,动作的名字是动词。动作就好像一般的web应用程序的页面一样,尽管两个动作可能显示同样的页面(例如,在给文章留言后还会把文章显示出来)。
TIP 如果你认为这么做对于一个刚开始的项目来说层次太多了,你可以很方便的把所有的动作集中到一个模块里,这样文件结构就简单了。当应用程序越来越复杂,你就需要把这些动作分开放到不同的模块。本书第1章提到,通过重写代码来改善结构与可读性(同样保留功能)被称为重构,当你应用RAD原则的时候经常需要这么做。
图 2-3 是一个blog项目的代码组织结构图,按照项目/应用程序/模块/动作来划分。 但注意项目的实际文件结构可能会与图里面的不一样。
图 2-3 - 代码组织结构例子







目录结构

所有的web项目都有这些内容:
一个数据库,例如MySQL或者PostgreSQL
静态文件(HTML, 图片, JavaScript文件,样式表等)
网站管理员与用户上传的文件
PHP类与函数库
外部库(第三方脚本)
批处理文件 (用于命令行或者cron的脚本)
日志文件 (应用程序或者服务器的留下的脚印)
配置文件

symfony用一种合理的目录结构组织所有这些内容,这种树形结与symfony的架构(MVC模式与应用程序/项目/模块分组)想符合。这个目录结构是在项目,应用程序,模块初始化的时候自动生成的。当然,为了满足客户的需求你可以完全自定义这个结构。
根目录结构

这些是一个symfony项目根目录下的文件:
apps/
  frontend/
  backend/
batch/
cache/
config/
data/
  sql/
doc/
lib/
  model/
log/
plugins/
test/
  unit/
  functional/
web/
  css/
  images/
  js/
  uploads/

表 2-1 介绍这些目录的内容

表 2-1 - 根目录目录        描述
apps/        包含此项目内所有应用程序(一般情况, frontend 与 backend 分别代表前台与后台).
batch/        包含命令行下运行的PHP脚本或者定期执行的脚本
cache/        包含了配置文件的缓存,如果你开了动作和模版,还有这两个部分的缓存。缓存机制(详见第12章)把这些信息存在文件里面加快响应web请求的速度。每个应用程序都会有一个子目录,包含了预处理的PHP与HTML文件。
config/        存放项目的配置信息。
data/        这里是你可以存放项目的数据文件,例如数据库schema,包含了建立数据表的SQL文件,或者一个SQLite数据库文件。
doc/        存放项目文档,包括你自己的文档和PHPdoc生成的文档。
lib/        主要用来存放外部类或者库。这里的内容整个项目都能访问到。'model/`子目录存放项目的对象模型(详见第8章)。
log/        存放symfony生成的应用程序的日志文件。也可以放web服务器的日志文件,数据库日志文件,或者项目的任何地方的日志文件。symfony自动为项目的每一个应用程序的每一个环境生成一个日志文件(l日志文件详见第16章).
plugins/        存放安装在项目里的插件(插件详见第17章)。
test/        包含PHP写的与symfony测试框架兼容的单元与功能测试(详见第15章)。 项目初始化的时候,symfony会自动建立一些基本的测试。
web/        web服务器的根目录。所有从因特网能够直接访问的文件都在这个目录里。
应用程序目录结构 Application Tree Structure

所有应用程序的目录结构都是一样的:
apps/
  [应用程序名]/
    config/
    i18n/
    lib/
    modules/
    templates/
      layout.php
      error.php
      error.txt

表 2-2 介绍应用程序的子目录

表 2-2 - 应用程序的子目录目录        描述
config/        包含一些YAML格式的配置文件。大部分应用程序的配置信息都在这里,symfony框架自己的默认配置除外。 注意需要的话默认值可以修改。详见第5章。
i18n/        包含应用程序的国际化文件--大部分的界面翻译文件(详见第13章)。如果你用数据库存放翻译信息可以忽略这个目录。
lib/        包含应用程序用到的类与库。
modules/        存放应用程序的所有功能模块。
templates/        包含应用程序的全局模版--所有模块公用的模版。默认情况,这个目录会有一个layout.php文件,这是模块模版默认主布局。


NOTE 新应用程序的i18n/, lib/, 与 modules/ 目录是空的。

一个应用程序的类的方法或属性不能被同一个项目的其他应用程序访问到。另外,同一项目的两个应用程序之间的超链接必须用绝对形式。开始把项目分成不同的应用程序的时候,这个这个限制就存在了。
模块目录结构 Module Tree Structure

每个应用程序包括一个或更多的模块。在modules每个模块都有它自己的子目录,这个目录的名字是模块初始化的时候确定的。

这是一个典型的模块目录结构:
apps/
  [应用程序名]/
    modules/
      [模块名]/
          actions/
            actions.class.php
          config/
          lib/
          templates/
            indexSuccess.php
          validate/

表 2-3 介绍模块子目录

表 2-3 - 模块子目录目录        描述
actions/        一般只有一个文件actions.class.php,这个文件里面包含了模块的所有动作。模块的不同动作也可以分开写在不同的文件里。
config/        可以存放与模块的配置信息。
lib/        存放模块的类与库。
templates/        存放模块里所有动作的模版。模块初始化的时候,会建立一个默认模版indexSuccess.php。
validate/        用户存放表单验证配置信息(详见第10章)。


NOTE 新模块的config/, lib/, 与validate/ 目录是空的
web目录结构

web目录的限制很少,这里存放的是互联网可以访问得到的文件。模版的默认行为还有helper里包含了几个基本的命名规则。下面是一个web目录的结构的例子:
web/
  css/
  images/
  js/
  uploads/

表 2-4 介绍web目录的内容

表 2-4 - 典型的web目录的子目录目录        描述
css/        存放.css结尾的样式表文件
images/        存放.jpg,.png与.gif扩展名的图片文件。
js/        存放.js扩展名的JavaScript文件
uploads/        只能存放用户上传的文件。虽然这个目录通常会存放图片我们还是把这个目录与图片目录分开,这样同步开发服务器与正式服务器的时候不会影响上传的文件。


NOTE 虽然强烈建议维持默认的目录结构,你还是可以作出修改,例如一个项目要运行在不同的目录结构与命名规则的服务器上。修改目录结构详见第19章。
常用工具 Common Instruments

有些技巧在symfony里面很常用,在项目中你会经常碰到他们。这包括参数holder,常量,还有类自动加载。

作者: 单飞雪莲   发布时间: 2008-06-05

参数 holder Parameter Holders

很多symfony类都包含一个参数holder。参数holder用简便的方式封装了getter与setter方法。例如,sfResponse类包含了一个可以通过执行getParameterHolder()方法获得的参数holder。每一个参数holder都用同样的方式存取数据,如例2-15所示。

例 2-15 - 使用 sfResponse 参数holder
[php]
$response->getParameterHolder()->set('foo', 'bar');
echo $response->getParameterHolder()->get('foo');
=> 'bar'

大部分类通过使用参数holder的proxy方法来减少get/set操作的代码量。这是sfResponse对象的例子,例2-16可以达到例2-15同样效果。

Listing 2-16 - Using the sfResponse Parameter Holder Proxy Methods
[php]
$response->setParameter('foo', 'bar');
echo $response->getParameter('foo');
=> 'bar'

参数holder的getter方法可以有第二个参数作为默认值。这样在取值失败的时候比较简洁。见例2-17。

例 2-17 - 使用参数holder的get方法的默认值
[php]
// 'foobar' 参数没有定义, 所以getter返回空值
echo $response->getParameter('foobar');
=> null

// 利用条件判断给一个默认值
if ($response->hasParameter('foobar'))
{
  echo $response->getParameter('foobar');
}
else
{
  echo 'default';
}
=> default

// 但是使用第二个默认值参数快的多
echo $response->getParameter('foobar', 'default');
=> default

参数holder还支持命名空间。如果你给setter或者getter指定第三个参数,这个参数代表命名空间,那么这个参数就只会在这个命名空间里定义或者取值。见例2-18

例 2-18 - sfResponse 参数 Holder 的命名空间
[php]
$response->setParameter('foo', 'bar1');
$response->setParameter('foo', 'bar2', 'my/name/space');
echo $response->getParameter('foo');
=> 'bar1'
echo $response->getParameter('foo', null, 'my/name/space');
=> 'bar2'

当然,你可以给你自己的类增加参数holder来获得这些好处。例2-19告诉我们如何定一个有参数holder的类。

例 2-19 - 给类增加参数holder
[php]
class MyClass
{
  protected $parameter_holder = null;

  public function initialize ($parameters = array())
  {
    $this->parameter_holder = new sfParameterHolder();
    $this->parameter_holder->add($parameters);
  }

  public function getParameterHolder()
  {
    return $this->parameter_holder;
  }
}
常量 Constants

symfony里的常量少得出奇。这是因为PHP的一大缺点:常量定义后就不能改变了。所以symfony使用自己的配置对象,称作sfConfig,用来取代常量。它提供了在任何地方存取参数的静态方法。例2-20演示了sfConfig类的方法。

例 2-20 - 使用 sfConfig 类方法取代常量
[php]
// PHP常量
define('SF_FOO', 'bar');
echo SF_FOO;
// symfony 使用sfConfig对象
sfConfig::set('sf_foo', 'bar');
echo sfConfig::get('sf_foo');

sfConfig方法支持默认值,并且sfConfig::set()方法可以多次调用来设置同一个参数的值。第5章详细讨论了sfConfig方法。
类自动载入 Class Autoloading

一般来说,当你在PHP中要用一个类来创建一个对象的时候,你需要首先包含这个类的定义。
[php]
include 'classes/MyClass.php';
$myObject = new MyClass();

但是大的项目包含了很深的目录结构,包含所有这些文件还有路径很浪费时间。由于有__autoload()函数(或者spl_autoload_register()函数),symfony使得我们不需要写包含语句,你可以直接这么写:
[php]
$myObject = new MyClass();

symfony会在项目的lib目录里的所有php文件里寻找MyClass的定义。如果找到,就自动包含它。

所以你可以把所有的类放在lib目录,你再也不必包含他们。所以symfony项目通常没有include或者require语句。

NOTE 为了提高效率,第一次symfony自动在一个目录列表(在配置文件里面定义)里寻找。然后symfony把这些目录里的所有类和文件的关联存放在一个PHP数组里。这样,以后的自动载入就不需要扫描整个目录了。所以你每次在项目里面增加一个类都需要通过symfony clear-cache命令清空symfony缓存。缓存详见第12章,自动载入配置文件详见第19章。
总结

使用MVC框架迫使你按照框架的规定把代码分开。显示的代码归到视图里,数据处理的代码归到模型,请求处理逻辑归到控制器。这对MVC模式的应用程序很有用,也是一个约束。

symfony是一个PHP5写的MVC框架。它的结构充分发挥了MVC模式的好处,但也非常容易使用。这要感谢他的全面性与可配置性。

现在你已经了解了symfony背后的原理,差不多该是开发你的第一个应用程序的时候了。但是在这之前,你需要在你的开发服务器上安装一套symfony并跑起来。

作者: 单飞雪莲   发布时间: 2008-06-05

1- Symfony的Project目录说明

myfirstsymfony/ -----symfony项目目录
  apps/ -----Application程序目录
  batch/ -----用于存放调用命令行或调度程序运行批处理操作的PHP文件
  cache/ ----项目缓存目录,用于加速WEB应答与请求
  config/ ----项目常规配置文件
  data/ ----存储项目数据资料,例如,数据库规划资料,创建表的SQL文件(或SqLite数据库)
  doc/ ----存放项目文档,包括开发文档及phpdoc()
  lib/ ----该目录是专门用于存放外部类和库。可以在这里加入你所需要共享的某个程序的代码,其中的model子目录用于存放项目的对象模型。
  log/ ----用于存放symfony直接产生的日志文件,也可以包括网站的日志文件,通常每个类别应对应一个日志文件,如:myapp_prod.log
  test/ ----该目录用于存放PHP所写的单元测试文件。在项目设置初,Symtony会自动加入一些基础测试的结果。
  web/ ----该目录是网站的根目录,这里只存放需要从internet可以访问到的文件。

2- Symtony的Application目录说明

myfirstsymfony/ ----symfony项目目录
  apps/ ----Application程序目录
    myfirstapp/ ----Application的主目录
    config/ ----存放一套YAML相关的配置文件。这是一个大多数应用程序的配置,从默认的参数分离开始能够被框架自己创建。
    i18n/ ----国际化用文件组
    lib/ ----Application专用Class(library用目录),一般为扩展类库
    modules/ ----模块目录
    templates/----Application,Module用模板目录

3- Symtony的Module目录说明

该目录主要结构如下:
  myfirstsymfony/--------项目目录
      apps/--------------------程序目录
        myfirstapp/--------myfirstapp程序目录
            modules/-------myfirstapp程序的模块目录
               helloworld/―被建立的myfirstapp下的helloworld模块
                  actions/-----动作用目录
                  config/------模块设置目录
                  lib/-----------模块用类及类库目录(class&library)
                  templates/----模块用临时文件目录
                  validate/-------验证设置文件存放目录

作者: 单飞雪莲   发布时间: 2008-06-05