乍一看,symfony驱动应用程序背后的代码似乎相当令人生畏。ob娱乐下载它由许多目录和脚本组成,文件混合了PHP类、HTML,甚至是两者的混合。您还将看到对应用程序文件夹中其他地方找不到的类的引用,目录深度扩展到六层。但是,一旦您理解了所有这些看似复杂的背后的原因,您就会突然觉得它是如此自然,以至于您不会将symfony应用程序结构换成任何其他结构。ob娱乐下载这一章解释了那种害怕的感觉。
ob娱乐下载Symfony基于经典的网页设计模式,即MVC架构,由三个层次组成:
MVC模式如图2-1所示。
MVC体系结构分离了业务逻辑(模型)和表示(视图),从而提高了可维护性。例如,如果你的应用程序应该在标准的web浏览器和手持设备上运行,你只需要一个新的视图;您可以保留原来的控制器和模型。控制器有助于从模型和视图中隐藏请求所使用的协议的细节(HTTP、控制台模式、邮件等等)。模型抽象了数据的逻辑,这使得视图和操作独立于应用程序使用的数据库类型。
图2-1 - MVC模式
为了帮助您理解MVC的优点,让我们看看如何将一个基本的PHP应用程序转换为MVC架构的应用程序。博客应用程序的帖子列表就是一个很好的例子。
在平面PHP文件中,显示数据库条目列表可能类似于清单2-1所示的脚本。
清单2-1 -一个平面脚本
<?php//连接,选择数据库美元的链接=首先(“localhost”,“myuser”,“我的密码”);mysql_select_db(“blog_db”,美元的链接);//执行SQL查询美元的结果=mysql_query('SELECT date, title FROM post',美元的链接);?> 职位列表 职位列表
这样写起来很快,执行起来也很快,而且不可能维护。以下是这段代码的主要问题:
的回声而且printf清单2-1中的调用使代码难以阅读。修改HTML代码以增强表示,对于当前的语法来说是一个麻烦。所以代码可以分成两部分。首先,包含所有业务逻辑的纯PHP代码放在控制器脚本中,如清单2-2所示。
回声
printf
清单2-2 -控制器部分,在index . php
index . php
<?php//连接,选择数据库美元的链接=首先(“localhost”,“myuser”,“我的密码”);mysql_select_db(“blog_db”,美元的链接);//执行SQL查询美元的结果=mysql_query('SELECT date, title FROM post',美元的链接);//填充视图的数组美元的帖子=数组();而(行美元=mysql_fetch_array(美元的结果, MYSQL_ASSOC)){美元的帖子[]=行美元;}//关闭连接mysql_close(美元的链接);//需要视图需要(“view.php”);?>
包含类似模板的PHP语法的HTML代码存储在一个视图脚本中,如清单2-3所示。
清单2-3 -视图部分,在view.php
view.php
职位列表 职位列表
判断视图是否足够干净的一个好的经验法则是,它应该只包含最少数量的PHP代码,以便没有PHP知识的HTML设计人员也能理解。视图中最常见的语句是echo, if/endif, foreach/endforeach,仅此而已。此外,不应该有PHP代码与HTML标记相呼应。
所有的逻辑都被转移到控制器脚本中,并且只包含纯PHP代码,其中没有HTML。事实上,您应该想象相同的控制器可以用于完全不同的表示,可能是在PDF文件或XML结构中。
大多数控制器脚本代码专门用于数据操作。但是,如果需要为另一个控制器(比如输出weblog文章的RSS提要)提供文章列表,该怎么办呢?如果您希望将所有数据库查询保存在一个地方,以避免代码重复,该怎么办?如果您决定更改数据模型以使帖子表被重命名weblog_post?如果你想切换到PostgreSQL而不是MySQL呢?为了实现这一切,您需要从控制器中删除数据操作代码,并将其放入另一个称为模型的脚本中,如清单2-4所示。
帖子
weblog_post
清单2-4 -模型部分,在model.php
model.php
<?php函数getAllPosts(){//连接,选择数据库美元的链接=首先(“localhost”,“myuser”,“我的密码”);mysql_select_db(“blog_db”,美元的链接);//执行SQL查询美元的结果=mysql_query('SELECT date, title FROM post',美元的链接);//填充数组美元的帖子=数组();而(行美元=mysql_fetch_array(美元的结果, MYSQL_ASSOC)){美元的帖子[]=行美元;}//关闭连接mysql_close(美元的链接);返回美元的帖子;}?>
修改后的控制器如清单2-5所示。
清单2-5 -控制器部分,修订,在index . php
<?php//需要模型require_once(“model.php”);//检索帖子列表美元的帖子= getAllPosts();//需要视图需要(“view.php”);?>
控制器变得更容易阅读。它的唯一任务是从模型中获取数据并将其传递给视图。在更复杂的应用程序中,控制器还处理请求、用户会话、身份验证等。模型函数的显式名称的使用甚至使控制器中的代码注释变得不必要。
模型脚本专门用于数据访问,并可以相应地组织。所有不依赖于数据层的参数(如请求参数)必须由控制器给出,并且不能由模型直接访问。模型函数可以很容易地在另一个控制器中重用。
所以MVC架构的原理是根据代码的性质将其分为三层。数据逻辑代码放在模型中,表示代码放在视图中,应用程序逻辑放在控制器中。
其他额外的设计模式可以使编码更加容易。模型、视图和控制器层可以进一步细分。
模型层可以分为数据访问层和数据库抽象层。这样,数据访问函数就不会使用依赖于数据库的查询语句,而是调用一些其他函数来执行查询。如果稍后更改数据库系统,则只需要更新数据库抽象层。
清单2-6给出了一个示例数据库抽象层,清单2-7给出了一个特定于mysql的数据访问层。
清单2-6 -模型的数据库抽象部分
<?php函数open_connection(美元的主机,$ user,美元的密码){返回首先(美元的主机,$ user,美元的密码);}函数close_connection(美元的链接){mysql_close(美元的链接);}函数query_database(美元的查询,美元的数据库,美元的链接){mysql_select_db(美元的数据库,美元的链接);返回mysql_query(美元的查询,美元的链接);}函数fetch_results(美元的结果){返回mysql_fetch_array(美元的结果, MYSQL_ASSOC);}
清单2-7 -模型的数据访问部分
函数getAllPosts(){//连接到数据库美元的链接= open_connection(“localhost”,“myuser”,“我的密码”);//执行SQL查询美元的结果= query_database('SELECT date, title FROM post',“blog_db”,美元的链接);//填充数组美元的帖子=数组();而(行美元= fetch_results(美元的结果)){美元的帖子[]=行美元;}//关闭连接close_connection(美元的链接);返回美元的帖子;}?>
您可以检查在数据访问层中找不到依赖于数据库引擎的函数,从而使其与数据库无关。此外,在数据库抽象层中创建的函数可以被许多其他需要访问数据库的模型函数重用。
请注意
清单2-6和清单2-7中的示例仍然不是很令人满意,要实现完整的数据库抽象还有一些工作要做(通过独立于数据库的查询构建器抽象SQL代码,将所有函数移到一个类中,等等)。但是本书的目的并不是向您展示如何手工编写所有这些代码,您将在第8章中看到symfony本身就很好地完成了所有的抽象。ob娱乐下载
视图层还可以从一些代码分离中获益。在整个应用程序中,网页通常包含一致的元素:页眉、图形布局、页脚和全局导航。只有页面的内部部分发生了变化。这就是为什么视图被分离成一个布局和一个模板。布局通常是应用程序或一组页面的全局布局。模板只将控制器提供的变量放入形状中。需要一些逻辑来使这些组件协同工作,这个视图逻辑层将保留name视图。根据这些原则,清单2-3的视图部分可以分为三个部分,如清单2- 8,2 - 9,2 -10所示。
清单2-8 -视图的模板部分,在mytemplate.php
mytemplate.php
<标题>文章列表< / h1 > <表> < tr > < th >日期< / th > < th >标题< / th > < / tr ><?phpforeach(美元的帖子作为美元的帖子):?>< tr > < td > < ?php回声美元的帖子[“日期”]? > < / td > < td > < ?php回声美元的帖子[“标题”]? > < / td > < / tr ><?phpendforeach;?>表> < /
清单2-9 -视图的视图逻辑部分
<?php美元的标题=“职位名单”;美元的帖子= getAllPosts();
清单2-10 -视图的布局部分
回声美元的标题?> .<?php包括(“mytemplate.php”);?>< /身体> < / html >
在前面的例子中,控制器没有做太多的工作,但是在真实的web应用程序中,控制器有很多工作要做。这项工作的一个重要部分对应用程序的所有控制器都是通用的。常见的任务包括请求处理、安全处理、加载应用程序配置以及类似的杂务。这就是为什么控制器通常分为前端控制器和动作,前者对整个应用程序是唯一的,后者只包含特定于一个页面的控制器代码。
前端控制器的最大优势之一是它为整个应用程序提供了一个独特的入口点。如果您决定关闭对应用程序的访问,您只需要编辑前端控制器脚本。在没有前端控制器的应用程序中,每个单独的控制器都需要关闭。
前面的所有示例都使用了过程式编程。现代语言的面向对象功能使编程更加容易,因为对象可以封装逻辑,彼此继承,并提供干净的命名约定。
用一种非面向对象的语言实现MVC架构会引发命名空间和代码复制问题,而且整个代码难以阅读。
面向对象允许开发人员处理视图对象、控制器对象和模型类,并将前面示例中的所有函数转换为方法。这对于MVC架构来说是必须的。
提示
如果你想了解更多关于面向对象环境下web应用程序的设计模式,请阅读Martin Fowler (Addison-Wesley, ISBN: 0-32112-742-0)的《企业应用程序架构模式》。Fowler书中的代码示例是用Java或c#编写的,但对于PHP开发人员来说仍然是相当可读的。
等一下。对于列出weblog中的文章的单个页面,需要多少组件?如图2-2所示,我们有以下部分:
七个脚本——每次创建新页面时都要打开和修改大量文件!然而,symfonob娱乐下载y使事情变得简单。在充分利用MVC体系结构的同时,symfony实现它的方式使应用程序开发快速而轻松。ob娱乐下载
首先,前端控制器和布局对于应用程序中的所有操作都是通用的。你可以有多个控制器和布局,但你只需要其中一个。前端控制器是纯MVC逻辑组件,您永远不需要单独编写一个,因为symfony将为您生成它。ob娱乐下载
另一个好消息是,模型层的类也是欧宝平台是合法的吗基于数据结构自动生成的。这是Propel库的工作,它提供类骨架和代码生成。如果Propel发现外键约束或日期字段,它将提供特殊的访问器和突变方法,使数据操作变得非常简单。数据库抽象对您来说是完全不可见的,因为它是由另一个称为Creole的组件处理的。因此,如果您决定在某一时刻更改数据库引擎,则无需重写任何代码。您只需要更改一个配置参数。
最后一点是,视图逻辑可以很容易地转换为一个简单的配置文件,不需要编程。
图2-2 - Symfonob娱乐下载y工作流程
这意味着我们示例中描述的帖子列表只需要三个文件就可以在symfony中工作,如清单2-11、清单2-12和清单2-13所示。ob娱乐下载
清单2-11 -列表行动,在myproject /应用程序/ myapp /模块/博客/动作/ actions.class.php
列表
myproject /应用程序/ myapp /模块/博客/动作/ actions.class.php
<?php类weblogActions扩展sfActions{公共函数executeList(){这个美元->的帖子= PostPeer::doSelect(新标准());}}?>
清单2-12 -列表的模板,myproject /应用程序/ / listSuccess.php myapp /模块/博客/模板
myproject /应用程序/ / listSuccess.php myapp /模块/博客/模板
<标题>文章列表< / h1 > <表> < tr > < th >日期< / th > < th >标题< / th > < / tr ><?phpforeach(美元的帖子作为美元的帖子):?>< tr > < td > < ?php回声美元的帖子->获取当前日期()? > < / td > < td > < ?php回声美元的帖子->getTitle()? > < / td > < / tr ><?phpendforeach;?>表> < /
清单2-13 -列表看来,在myproject /应用程序/ myapp /模块/博客/ config / view.yml
myproject /应用程序/ myapp /模块/博客/ config / view.yml
listSuccess: metas: {title:文章列表}
此外,您还需要定义一个布局,如清单2-14所示,但它将被重用多次。
清单2-14 -布局,在myproject /应用程序/ myapp /模板/ layout.php
myproject /应用程序/ myapp /模板/ layout.php
< html > < >头<?php回声include_title()?>身体头部< / > < ><?php回声sf_data美元->getRaw(“sf_content”)?>< /身体> < / html >
这就是你所需要的。这正是显示与清单2-1中所示的平面脚本相同的页面所需的代码。其余部分(使所有组件一起工作)由symfony处理。ob娱乐下载如果数一下行数,就会发现使用symfony在MVC架构中创建文章列表并不比编写平面文件需要更多的时间或代码。ob娱乐下载尽管如此,它给你带来了巨大的优势,尤其是清晰的代码组织、可重用性、灵活性和更多的乐趣。此外,您还拥有XHTML一致性、调试功能、简单配置、数据库抽象、智能URL路由、多个环境和更多开发工具。
symfony中的MVC实现使用了一些你在本书中经常ob娱乐下载会遇到的类:
sfController
sfRequest
sfResponse
sfContext: getInstance ()
你将在第6章学到更多关于这些对象的知识。
可以看到,所有symfony类都使用ob娱乐下载科幻小说前缀,模板中的symfony核心变量ob娱乐下载也是如此。这将避免与您自己的类和变量的名称冲突,并使核心框架类易于社交和识别。
科幻小说
在symfony中使用的编码标准中,UpperCamelCase是类和ob娱乐下载变量命名的标准。存在两个例外:开头的核心symfony类ob娱乐下载科幻小说,小写字母,模板中的变量使用下划线分隔的语法。
现在您已经了解了symfony应用程序的不同组件,您可能想知道它们是如何组织的。ob娱乐下载ob娱乐下载Symfony在项目结构中组织代码,并将项目文件放入标准树结构中。
在syob娱乐下载mfony中,项目是在给定域名下可用的一组服务和操作,它们共享相同的对象模型。
在项目内部,操作按逻辑分组到应用程序中。应用程序通常可以独立于同一项目的其他应用程序运行。在大多数情况下,一个项目将包含两个应用程序:一个用于前台办公室,一个用于后台办公室,它们共享相同的数据库。但是您也可以让一个项目包含许多迷你站点,每个站点作为一个不同的应用程序。注意,应用程序之间的超链接必须是绝对形式。
每个应用程序都是一个或多个模块的集合。模块通常表示具有类似目的的一个页面或一组页面。例如,您可能拥有这些模块首页,文章,帮助,shoppingCart,账户等等。
首页
文章
帮助
shoppingCart
账户
模块包含操作,这些操作表示可以在模块中执行的各种操作。例如,shoppingCart模块可以有添加,显示,更新行动。一般来说,动作可以用动词来描述。处理操作几乎就像处理经典web应用程序中的页面,尽管两个操作可以产生相同的页面(例如,在weblog中添加评论将重新显示带有新评论的帖子)。
添加
显示
更新
如果这对于一个开始的项目来说代表了太多的级别,那么很容易将所有操作分组到一个单独的模块中,这样文件结构就可以保持简单。当应用程序变得更加复杂时,就需要将操作组织到单独的模块中。正如在第1章中提到的,重写代码以改善其结构或可读性(但保留其行为)称为重构,在应用RAD原则时,您将经常这样做。
图2-3显示了weblog项目的示例代码组织,采用项目/应用程序/模块/操作结构。但是请注意,项目的实际文件树结构将不同于图中所示的设置。
图2-3代码组织示例
所有的web项目通常共享相同类型的内容,例如:
ob娱乐下载Symfony提供了一个标准的文件树结构,以符合体系结构选择(MVC模式和项目/应用程序/模块分组)的逻辑方式组织所有这些内容。这是在初始化每个项目、应用程序或模块时自动创建的树结构。当然,您可以完全自定义它,以便在您方便的时候重新组织文件和目录,或者匹配您客户端的要求。
下面是symfony项目的根目录:ob娱乐下载
Apps /前端/后端/ batch/ cache/ config/ data/ sql/ doc/ lib/ model/ log/ plugins/ test/ unit/ functional/ web/ css/ images/ js/ uplogins /
各目录内容如表2-1所示。
表2-1 -根目录
应用程序/
前端
后端
批处理/
缓存/
配置/
数据/
文档/
lib /
模型/
日志/
插件/
测试/
web /
所有应用程序目录的树状结构是相同的:
Apps /[应用程序名称]/ config/ i18n/ lib/ modules/ templates/ layout.php error.php error.txt
应用子目录说明如表2-2所示。
表2-2 -应用子目录
i18n /
模块/
模板/
layout.php
的i18n /,lib /,模块/新应用程序的目录为空。
应用程序的类不能访问同一项目的其他应用程序中的方法或属性。还要注意,同一项目的两个应用程序之间的超链接必须是绝对形式。在初始化时,当您选择如何将项目划分为应用程序时,您需要记住最后一个约束。
每个应用程序包含一个或多个模块。的子目录中每个模块都有自己的子目录模块目录,该目录的名称是在安装过程中选择的。
模块
这是一个模块的典型树形结构:
apps/[应用程序名]/ modules/[模块名]/ actions/ actions.class.php config/ lib/ templates/ indexSuccess.php validate/
各模块子目录说明如表2-3所示。
表2-3 -模块子目录
行为/
actions.class.php
indexSuccess.php
验证/
的配置/,lib /,验证/新模块的目录为空。
很少有约束条件网络目录,这是公共可访问文件的目录。遵循一些基本的命名约定将在模板中提供默认行为和有用的快捷方式。这里有一个例子网络目录结构:
网络
Web / css/ images/ js/ uploads/
通常,静态文件分布在表2-4所示的目录中。
表2-4典型Web子目录
css /
. css
图像/
jpg
. png
gif
js /
. js
上传/
尽管强烈建议您维护默认的树结构,但是可以根据特定的需要对其进行修改,例如允许项目在具有不同树结构规则和编码约定的服务器中运行。有关修改文件树结构的更多信息,请参阅第19章。
在symfony中反复使用了一些技术,在本书和您自己的项目中,您将经常遇到它们。ob娱乐下载这些包括参数持有者、常量和类自动加载。
许多symfony类都包ob娱乐下载含一个参数holder。这是一种使用干净的getter和setter方法封装属性的方便方法。例如,sfResponse类包含一个参数holder,您可以通过调用getParameterHolder ()方法。每个参数holder以相同的方式存储数据,如清单2-15所示。
getParameterHolder ()
清单2-15 -使用sfResponse参数持有人
美元的反应->getParameterHolder()->集(“foo”,“酒吧”);回声美元的反应->getParameterHolder()->得到(“foo”);=>“酒吧”
大多数使用参数holder的类都提供了代理方法来缩短get/set操作所需的代码。这是sfResponse对象,因此您可以使用清单2-16的代码执行与清单2-15相同的操作。
清单2-16 -使用sfResponse参数持有者代理方法
美元的反应->setParameter(“foo”,“酒吧”);回声美元的反应->getParameter(“foo”);=>“酒吧”
参数持有者getter接受一个默认值作为第二个参数。这提供了一种有用的回退机制,比使用条件语句简洁得多。示例请参见清单2-17。
清单2-17 -使用属性持有者Getter的默认值
// foobar参数没有定义,所以getter返回一个空值回声美元的反应->getParameter(“foobar”);=>零//可以通过将getter放在条件中来使用默认值如果(美元的反应->hasParameter(“foobar”)){回声美元的反应->getParameter(“foobar”);}其他的{回声“默认”;}=>默认的但是使用第二个getter参数要快得多回声美元的反应->getParameter(“foobar”,“默认”);=>默认的
参数持有者甚至支持名称空间。如果您为setter或getter指定了第三个参数,则它将用作名称空间,并且参数将仅在该名称空间内定义。清单2-18给出了一个示例。
清单2-18 -使用sfResponse参数持有者命名空间
美元的反应->setParameter(“foo”,“bar1”);美元的反应->setParameter(“foo”,“bar2”,“我的/名称/空间”);回声美元的反应->getParameter(“foo”);=>“bar1”回声美元的反应->getParameter(“foo”,零,“我的/名称/空间”);=>“bar2”
当然,您可以在自己的类中添加参数holder,以利用其语法功能。清单2-19展示了如何定义带有参数holder的类。
清单2-19 -向类添加参数Holder
类MyClass{受保护的parameter_holder美元=零;公共函数初始化(美元的参数=数组()){这个美元->parameter_holder=新sfParameterHolder();这个美元->parameter_holder->添加(美元的参数);}公共函数getParameterHolder(){返回这个美元->parameter_holder;}}
您将在symfony中找不到任何常数,因为根据它们的本质,一旦定义了它ob娱乐下载们,您就不能更改它们的值。ob娱乐下载Symfony使用自己的配置对象sfConfig,用来替换常量。它提供了从任何地方访问参数的静态方法。清单2-19演示了的使用sfConfig类方法。
sfConfig
清单2-20 -使用sfConfig类方法而不是常量
// PHP常量定义(“SF_FOO”,“酒吧”);回声SF_FOO;// ob娱乐下载Symfony使用sfConfig对象sfConfig::集(“sf_foo”,“酒吧”);回声sfConfig::得到(“sf_foo”);
sfConfig方法支持默认值,您可以在同一个参数上多次调用sfConfig::set()方法来更改其值。第五章讨论sfConfig方法更详细。
通常,当你在PHP中使用一个类方法或创建一个对象时,你需要先包含类定义:
包括“类/ MyClass.php”;myObject美元=新MyClass();
在具有许多类和深层目录结构的大型项目中,跟踪要包含的所有类文件及其路径可能很耗时。通过提供spl_autoload_register ()功能,symfonyob娱乐下载使包括语句不需要,你可以直接写:
spl_autoload_register ()
包括
myObject美元=新MyClass();
ob娱乐下载Symfony将会寻找一个MyClass所有以。结尾的文件中的定义php在其中一个项目中lib /目录。如果找到类定义,它将被自动包含。
MyClass
php
所以如果你把所有的类都存储在lib/目录中,你就不需要再包含类了。这就是symfony项目通常不ob娱乐下载包含任何内容的原因包括或需要语句。
需要
为了获得更好的性能,symfony自动加载在第一次请求期ob娱乐下载间扫描目录列表(在内部配置文件中定义)。然后,它注册这些目录包含的所有类,并将类/文件通信作为关联数组存储在PHP文件中。这样,以后的请求就不需要再执行目录扫描了。方法在项目中添加或移动类文件时,都需要清除缓存ob娱乐下载清除缓存后命令。你将在第12章学习更多关于缓存的知识,在第19章学习关于自动加载配置的知识。
ob娱乐下载清除缓存后
使用MVC框架迫使您根据框架约定划分和组织代码。表示代码进入视图,数据操作代码进入模型,请求操作逻辑进入控制器。这使得MVC模式的应用既很有帮助,又很有限制。
ob娱乐下载Symfony是一个用PHP 5编写的MVC框架。它的结构是为了充分利用MVC模式而设计的,而且非常易于使用。由于它的多功能性和可配置性,symfony适用于所有的web应用程序项目。ob娱乐下载
现在您已经理解了symfony背后的基本理论,您几乎已经准备好开发您的第一个应用程序了。ob娱乐下载但在此之前,您需要在开发服务器上安装并运行symfonyob娱乐下载。
本作品在GFDL许可下获得许可。