Symfony回顾
在这份长长的但是却十分有趣的指南的第一天内容中,我们了解了如何安装Symfony框架,设置一个新程序以及开发环境,并且使用了源码版本控制安全的存放源码。
第二天的目标是从功能的角度来定义最终的结果应是什么样子的,设计数据模型以及编码。这包括生成一个对象关系映射,并且使用他们来创建,取出以及更新程序框架数据中的记录。
这确实是相当多的内容。让我们开始吧。
揭开程序面纱
我们希望了解什么呢?这是一个有趣的问题。对于这个问题有许多有趣的答案,如:
如何带来我的blog的流量?
什么是最好的web程序框架?
所有这些问题不只有一个答案,而最好的答案在于我们的观点。实际上,只有一个答案的问题通常是最无趣的,但是在web上要解决的只有一个。这不公平。
来askeet吧。这是一个帮助人们查找问题答案的网站。谁会回答这些问题呢?所有人。而每一个人都可以评价其他人的答案,所以最受欢迎的答案会更可见。随着问题数量的 增加,使用类与子类的方式来组织就变得不可行了,所以问题的提出者就可以使用他希望的任何单词来进行标签化。当然,标签的流行程序是由一个标签集合来表示 的。如果一个希望跟随一个答案来找到一个问题,他可以订阅这个问题RSS反馈。所有这些功能都必须是优雅和轻量级的,从而所有这些交互不必需要一个 AJAX类型式的新页。事实上,需要一个后端来组织问题与答案,或是手工添加一个管理员认为有意义的问题。
也许你会问:我还没有在web 上见到过这样的网站吗?当然,如果你确实是这样,那么我们正好,但是如果你见过如faqts,eHow,Ask leeves或是相似的内容,没有聚合答案,没有AJAX,没有RSS,没有标签,那么这些与我们的网站并不一样。我们这里在讨论一个web2.0的程 序。
askeet的目录不只是一个网站,他是一个程序,任何人都可以下载,在家里或是在公司的网站上安全,修改或是添加新的特性。源码将 会以开放源码许可证的形式发布。你的HR经理正是寻找一个知识管理系统?你希望记录你学到的关于修理汽车的技艺?你并不希望为你的网站开发一个FAQ部 分?不必再寻找了,因为有askeet。当然,他将会存在,那是我们的圣诞礼物。
从哪里开始?
那么你如何开始一个Symfony程序呢?这取决于你自己。如果你是一个XP能手,你可以写一个故事,计划一个游戏,并且找到一个合作者来进行结对编程,或者如果你是一个UML迷,你可以编写一个详细的网站需要说明,附带一个所有对象,状态以及交互的框架。
但 是这个教程并不是关于通常的程序开发的,所以我们从一个基本的关系数据模型开始,并且一步一步的添加工作特性。我们所需要的只是一个在每天结束时可用的程 序,而不是一个不会输出任何内容的巨大的程序代码。在理想的情况下,我们应为我们添加的每一个新特性编写单元测试,但是实际上我们并没有时间来这样做。如 果要进行单元测试,则需要一天的时间。所以我们还是继续阅读吧。
对于这个工程,我们将使用一个带有InonoDB表类型的MySQL数 据 库,这样可以充发利用集中控制与事务支持。在前面的步骤中,我们将会使用一个SQLite数据库,来避免设计一个实际的数据库。这需要 databases.yml文件中时行一些小的修改,我们会将这些工作作为探索的练习留给你。
数据模型
关系模型
很明显,需要有一个'question'与一个'answer'数据表。我们需要一个'user'数据表,并且我们需要一个'interest'数据表来存储对一个问题感兴趣的用户,以及在一个'relevancy'数据表中记录一个用户对于一个答案所做出的中肯的评价。
用 户需要进行确认来添加一个问题,评价答案,或者是删除对于一个问题的兴趣。用户添加答案并不需要进行确认,但是一个答案总是会链接到一个用户,这样给出最 受欢迎答案的用户可以进行区分。没有经过确认而发布的答案将会显示为一个通常用户的贡献,称之为'Anonymous Coward'。很容易理解整个关系数据表:
ERD
注意,对于每一个数据表,我们都声明了一个created_at域。Symfony会识别这样的域,并且在记录创建时会将其设置当前的系统时间。这也updated_at域相似:当记录更新时会将其设置为系统时间。
schema.xml
关系模型已经转换为一个Symfony可以理解的配置文件。这也就是schema.xml或是schema.yml文件的目的,这个文件位于askeet/config/目录下。Symfony支持XML或是YAML的格式语法。
有两种方法来编写这个文件:手写,这也我们喜欢的方法,或者由一个已存在的数据库生成。让我们看一下第一种解决方法。
首先,我们需要移除默认安装的YAML样式文件:
$ svn delete config/schema.yml
schema.yml 文件的语法是相当简单:他是一个XML文件,在其中<table>标记包含<column>,<foreign- key>以及<index>标记。一旦我们编写了一个,我们就可以编写所有的。下面是我们在前面所描述的关系模型的对应 schema.yml文件:
< ?xml version="1.0" encoding="UTF-8"?>
<database name="propel" defaultIdMethod="native" noxsd="true">
<table name="ask_question" phpName="Question">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="user_id" type="integer" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="title" type="longvarchar" />
<column name="body" type="longvarchar" />
<column name="created_at" type="timestamp" />
<column name="updated_at" type="timestamp" />
</table>
<table name="ask_answer" phpName="Answer">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="question_id" type="integer" />
<foreign-key foreignTable="ask_question">
<reference local="question_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="body" type="longvarchar" />
<column name="created_at" type="timestamp" />
</table>
<table name="ask_user" phpName="User">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="nickname" type="varchar" size="50" />
<column name="first_name" type="varchar" size="100" />
<column name="last_name" type="varchar" size="100" />
<column name="created_at" type="timestamp" />
</table>
<table name="ask_interest" phpName="Interest">
<column name="question_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_question">
<reference local="question_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="created_at" type="timestamp" />
</table>
<table name="ask_relevancy" phpName="Relevancy">
<column name="answer_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_answer">
<reference local="answer_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="score" type="integer" />
<column name="created_at" type="timestamp" />
</table>
</database>
注意,在这个文件中将数据名字设置为propel,而无论实际的数据库名字是什么。这个是一个用来连接Propel层与Symfony框架的参数。数据库的实际名字将会在databases.yml配置文件中定义。
如 果我们有一个数据库,我们还有另外的一个方法来创建schema.yml文件。也就是说,如果我们熟悉一个图形化的数据库设计工具,我们会更喜欢由生成的MySQL数据库来构建schema。在我们做这项工作之前,我们只需要编辑位于askeet/config/目录下的propel.ini文件,输入到 我们数据库的连接字符串:
propel.database.url = mysql://username:password@localhost/databasename
这里的username,password,localhost以及databasename是我们数据库的实际连接设置。现在我们可以调用propel-build-schema命令(在askeet/目录下)来由数据库生成schema.xml:
$ symfony propel-build-schema
除了创建一个schema.xml文件,我们也可以使用YAML的语法格式来创建一个schema.yml文件:
[yml] propel: _attributes: { noXsd: false, defaultIdMethod: none, package: lib.model }
ask_question:
_attributes: { phpName: Question, idMethod: native }
id: { type: integer, required: true, primaryKey: true, autoIncrement: true }
user_id: { type: integer, foreignTable: ask_user, foreignReference: id }
title: { type: longvarchar }
body: { type: longvarchar }
created_at: ~
updated_at: ~
ask_answer:
_attributes: { phpName: Answer, idMethod: native }
id: { type: integer, required: true, primaryKey: true, autoIncrement: true }
question_id: { type: integer, foreignTable: ask_question, foreignReference: id }
user_id: { type: integer, foreignTable: ask_user, foreignReference: id }
body: { type: longvarchar }
created_at: ~
ask_user:
_attributes: { phpName: User, idMethod: native }
id: { type: integer, required: true, primaryKey: true, autoIncrement: true }
nickname: { type: varchar(50), required: true, index: true }
first_name: varchar(100)
last_name: varchar(100)
created_at: ~
ask_interest:
_attributes: { phpName: Interest, idMethod: native }
question_id: { type: integer, foreignTable: ask_question, foreignReference: id }
user_id: { type: integer, foreignTable: ask_user, foreignReference: id }
created_at: ~
ask_relevancy:
_attributes: { phpName: Relevancy, idMethod: native }
answer_id: { type: integer, foreignTable: ask_answer, foreignReference: id }
user_id: { type: integer, foreignTable: ask_user, foreignReference: id }
score: { type: integer }
created_at: ~ |