======我的第一个symfony项目======
// 原文:http://www.symfony-project.com/tutorial/my_first_project.html //
看完对Symfony的介绍,你想要体验一下它的功能?就让我们做一个一小时可完成的全功能网站。
你可以叫它卖书的程式,或是,你也可以叫他网誌(部落格)。就让我们开始吧!
我们将假设你已经装好了apache/PHP5(如果你想快一点,你可用[[http://www.apachefriends.org/en/projects.html|xampp/lampp]])。
你也需要一个SQLite。目前预设已经和PHP5整合好了。然而,若是PHP 5.1.0,你必须要用php.ini开启PHP对SQLite的支持([[http://us3.php.net/sqlite|详情]])。
=====安装symfony并初始化一个专案=====
為了加快速度,我们用了[[http://www.symfony-project.com/get/sf_sandbox-alpha.tgz|symfony sandbox]]。它是个空的symfony专案,且包含了所有的symfony函式库,而且最基础的设定也完备了。用sandbox的最大好处,就是你可以马上体验symfony 。
先下载[[http://www.symfony-project.com/get/sf_sandbox-alpha.tgz|sandbox]]。然后在你的网站根目录解压缩。你可参考readme已得到更多的资讯。解开后的档案结构应该像这样:
www/
sf_sandbox/
apps/
frontend/
batch/
cache/
config/
data/
sql/
doc/
api/
lib/
model/
log/
test/
web/
css/
images/
js/
uploads/
这表示sf_sandbox已经有了前端程式,你可在你的网址列打上
http://localhost/sf_sandbox/web/index.php/
你将看到一个恭喜的网页:
#sym[{{symfony:first_tub:first_congrats.gif|}}]#
#note[**注意**:如果你看不到这个恭禧你安装成功的网页,请你检查php.ini这个配置文件。找这个设定值“magic_quotes_gpc”把它设定成off. 如需要更多的帮忙,请参考[[http://www.symfony-project.com/forum/index.php/f/6/|安装论坛]],那里有超多特殊的状况,被一一解决。]#
你可以安装symfony在你自己喜欢的目录下。然后设定你的网站指向它。或是设成虚拟网站或是别名网站,symfony的书有详细的章节介绍,请参考[[http://www.symfony-project.com/forum/index.php/f/6/|symfony installation]], [[http://www.symfony-project.com/content/book/page/project_creation.html|project creation]] and [[http://www.symfony-project.com/content/book/page/file_structure.html|file structure]]这几章。
=====初始化资料模型=====
当然,一般的网志,可以处理贴文的。然后你也可以在其上加些註解评论。在目录sf_sandbox/config/新增一个结构设定档schema.yml,然后贴上如下的内文。
propel:
weblog_post:
_attributes: { phpName: Post }
id:
title: varchar(255)
excerpt: longvarchar body: longvarchar
created_at: weblog_comment:
_attributes: { phpName: Comment }
id:
post_id:
author: varchar(255)
email: varchar(255)
body: longvarchar
created_at:
这个设定档用的是YAML文法,它是一个很简单的语言,用类似XML的树状阶层的架构来定义资料模型(资料表)。更进一步,它的读写比XML还快。这唯一要注意的是,缩排是有意义的,且要避免使用TAB键,所以请记住用空白键(SPACE)来表示少缩排。
你可以在[[http://www.symfony-project.com/content/book/page/configuration.html|configuration chapter]]这章,找到更多关於YAML和symfony的设定须知。
这个结构档描述在网志程式里所需要用到的两个资料表。Post和Comment是未来将会產生相对应的类别。存档,然后进入指令列。进到目录sf_sandbox/,然后键入:
$ symfony propel-build-model#note[**注意**:在*nix平台,你可能是用./symfony.sh而不是键入symfony]# 一些类别会产生在这个目录sf_sandbox/lib/model/,它们是物件/关係映射(object-relational mapping)的产物。这些类别的好处是我们可以用物件导向的方式而不是下SQL的INSERT/UPDATE指令来存取关联性资料库。Symfony使用Propel函式库来来达到此目的。我们将叫这些物件为模型(model)。(更多关于[[http://www.symfony-project.com/content/book/page/model.html|模型]]的说明) 你现在可以在指令列,输入
$ symfony propel-build-sql一个schema.sql 档產生在sf_sandbox/data/sql/。里面的SQL句子可以產生一个资料库,里面含这两资料表。你可以下指令或用WEB界面([[https://sourceforge.net/projects/phpmyadmin/|phpMyAdmin]])在MYSQL产生资料库。(在这章[[http://www.symfony-project.com/content/book/page/model.html|model]]也有讲到)。幸运地symfony sandbox 已经设定好直接可用SQLite(轻便的档案型资料库)。所以不用再做一些初始设定。预设的状况下,sf_sandbox专案将使用sf_sandbox/data/目录下的一个sandbox.db。如果你要在这个档案型资料库建资料表。你可以键入
$ symfony propel-insert-sql#note[**注意**:在这里有警告讯息也不用紧张,这是正常的,insert-sql的动作是先移除资料很再建立资料表,第一次做之前,当然没资料表可移除。]# =====创建程序棚架(scaffolding)===== 众所周知,一个基本的网志可让用户建立,检索,更新,删除贴子和留言(CRUD)。考虑到你还是Symfony的新手,你将不会要求从零开始写Symfony的代码,而是让Symfony帮你创建CRUD的棚架。棚架建好之后,你可以根据需要而修改CRUD的代码。输入:
$ symfony propel-generate-crud frontend post Post $ symfony propel-generate-crud frontend comment Comment#note[**注意**: 确定你在Symfony专案的根目录下执行这些指令]# 现在你建立了两个模块(post and comment),可让你支配Post和Comment类的对象。一个模块通常用来代表一个或一组有相似功能的网页。你新建两个模块位于sf_sandbox/apps/frontend/modules/目录里。相应的,它们可以访问这两个模块通过以下地址:
http://localhost/sf_sandbox/web/frontend_dev.php/post
http://localhost/sf_sandbox/web/frontend_dev.php/comment
点击'''create'''添加几个测试贴:
#sym[{{symfony:first_tub:first_crud.gif|}}]#
你可以在[[http://www.symfony-project.com/content/book/page/generator.html|scaffolding]]和[[http://www.symfony-project.com/content/book/page/project_creation.html|structure]]找到更多关于棚架和symfony专案构架的说明。
#note[**注意**:以上的两个地址的运行脚本 - 在Symfony里称为''front controller'' - 由index.php变为frontend_dev.php。两个脚本都执行一样的运用程序,区别是它们使用不同的执行参数。frontend_dev.php使用'''开发环境'''的参数,目的在于提供一系列的调试工具,例如'''debug toolbar'''位于屏幕的右上角。这就是为什么frontend_dev.php在处理页面的时候比index.php要慢。index.php则使用'''生产环境'''的参数。如果你在开发时需要用到用'''生产环境''',只需把frontend_dev.php改为index.php,然后清除缓存:
$ symfony clear-cache]# 更多关于[[http://www.symfony-project.com/content/book/page/configuration.html|运行环境]]的说明 =====修改模板===== 要在我们新建两个模块之间导航,我们的网页需共享超连结。打开公用模板sf_sandbox/apps/frontend/templates/layout.php,把之间的内容修改为:
kk
kk
请原谅不精心的设计和使用inline CSS,一个小时的时间并不是很充裕。
#sym[{{symfony:first_tub:first_crud_layout.gif|}}]#
你可以修改页面的标题。打开转案的配置文件sf_sandbox/apps/frontend/config/view.yml, 找出有title的一行,修改为:
default:
http_metas:
content-type: text/html; charset=utf-8
metas:
title: The best weblog ever
robots: index, follow
description: symfony project
keywords: symfony, project
language: en
你目前的主页使用默认的模版(template)和模块(module),位于Symfony框架目录下,而不在你的专案目录下。如果要覆盖(override)它,你必须建立自己的默认模块。输入:
$ cd apps/frontend/modules $ mkdir default $ cd default $ mkdir templates $ cd templates新建文件indexSucess.php,加入以下的内容:
Welcome to my swell weblog
You are the th visitor today.
导航到以下地址测试新的页面:
http://localhost/sf_sandbox/web/frontend_dev.php/
#sym[{{symfony:first_tub:first_welcome.gif|}}]#
测试:建立一个贴,然后测试回复。
更多关于[[http://www.symfony-project.com/content/book/page/view.html|模版]]和[[http://www.symfony-project.com/content/book/page/templating_configuration.html|视图设置]]的说明
=====把数据从动作传递到模版=====
这一节,我们要做的是在贴的下面显示回复。
首先,你必须编辑**动作**(action)。打开sf_sandbox/apps/frontend/modules/post/actions/actions.class.php,找到函数executeShow(),加入下面的四行代码:
public function executeShow ()
{
$this->post = PostPeer::retrieveByPk($this->getRequestParameter('id'));
$c = new Criteria();
$c->add(CommentPeer::POST_ID, $this->getRequestParameter('id'));
$c->addAscendingOrderByColumn(CommentPeer::CREATED_AT);
$this->comments = CommentPeer::doSelect($c);
$this->forward404Unless($this->post instanceof Post);
}
Criteria和Peer是Propel的对象关系映射(object-relational mapping)的一部分。总之,这四行代码将会从SQL中调出与Post相关联的Comment(Post指定在URL的id参数)。$this->comments这行主要是让与动作相对应的模版读取$comments里的数据。下一步我们就可以修改模版显示数据了。打开sf_sandbox/apps/frontend/modules/post/templates/showSuccess.php,在最后一行加入:
...
comment1) : ?>s to this post.
posted by getAuthor() ?> on getCreatedAt()) ?>
getBody()) ?>
本页使用了两个新的由Symfony提供的函数(format_date()和simple_format_text())。在Symfony里这些函数称为**助手(helpers)**,原因是它们能够提供一些很便利的功能。为你的第一个贴添加一个新的回复,然后查看:
http://localhost/sf_sandbox/web/frontend_dev.php/post/show?id=1
#sym[{{symfony:first_tub:first_comments_under_post.gif|}}]#
你可以在[[http://www.symfony-project.com/content/book/page/view.html|命名协议]]找到更多关于动作和模版的联系,或查看[[http://www.symfony-project.com/content/book/page/templating_other_helpers.html|text]]和[[http://www.symfony-project.com/content/book/page/templating_i18n_helpers.html|data]]的说明。
=====添加与另一个资料表相关联的档案=====
你可以依据Post的Id添加档案,但这不是用户界面友好设计的做法。让我们修改这一不便利之处。另外,当用户回复后,别忘了把他重定向到原来的贴。
首先,打开modules/post/templates/showSuccess.php模板,在最后一行加入:
getId()) ?>
link_to()是一个助手,它建立一个超链接指向comment模块的create动作,目的是让你可以从看贴的页直接回复。下一步,打开imodules/comment/templates/editSuccess.php,把这几行:
Post*:
'Post',
)) ?>
更替为:
hasParameter('post_id')): ?>
getParameter('post_id')) ?>
Post*:
'Post',
)) ?>
comment/create页的表单指向comment/update的动作,而且当用户提交表单之后,comment/update的动作会把页面重定向到comment/show(这是CRUDs的默认处理方式)。对我们的网志来说,这意味着在添加回复后,只有刚回复的comment显示在页面。为了达到户界面友好的设计,在用户回复后,把贴和回复显示在同一页面。打开modules/comment/actions/actions.class.php,把executeupdate()修改为:
public function executeUpdate ()
{
if (!$this->getRequestParameter('id', 0))
{
$comment = new Comment();
}
else
{
$comment = CommentPeer::retrieveByPk($this->getRequestParameter('id'));
$this->forward404Unless($comment);
}
$comment->setId($this->getRequestParameter('id'));
$comment->setPostId($this->getRequestParameter('post_id'));
$comment->setAuthor($this->getRequestParameter('author'));
$comment->setEmail($this->getRequestParameter('email'));
$comment->setBody($this->getRequestParameter('body'));
$comment->save();
return $this->redirect('post/show?id='.$comment->getPostId());
}
现在,用户能在回复后回到原来的界面。到此为止,你已完成了一个基本的网志。
你可以在[[http://www.symfony-project.com/content/book/page/controller.html|这里]]找到更多关于动作的说明
=====表单验证=====
用户可以回贴,但是,如果有谁提交空白的表单,数据库的质量将会受到殃及。避免此类情况发生,在sf_sandbox/apps/frontend/modules/comment/validate/目录下创建一个新的文件update.yml,加入:
methods:
post: [author, email, body]
get: [author, email, body]
fillin:
activate: on
names:
author:
required: Yes
required_msg: The name field cannot be left blank
email:
required: No
validators: emailValidator
body:
required: Yes
required_msg: The text field cannot be left blank
emailValidator:
class: sfEmailValidator
param:
email_error: The email address is not valid.
#note[**注意**:复制时务必保留原来的格式,文件务虚以**methods**的**m**开头,不能有空格]#
启动fillin可使表单在验证失败的情况下,自动导入用户上一次输入的数据。names定义表单的验证规则。
在默认情况下,当验证失败,控制器(controller)将会把用户转updateError.php模板。较可取的做法是当验证失败时,在同一页面显示表单和失败原因。打开modules/comment/actions/actions.class.php,加入函数:
public function handleError()
{
$this->forward('comment', 'create');
}
打开modules/comment/templates/editSuccess.php模版,在第一行插入:
hasErrors()): ?>
Please correct the following errors and resubmit:
getErrors() as $error): ?>
你完成了一个具有[[http://blog.sina.com.cn/myblog/article/article_print.php?blog_id=49e9696c010004xl | 鲁棒性(robust)]]的表单。
#sym[{{symfony:first_tub:first_form_validation.gif|}}]#
更多关于[[http://www.symfony-project.com/content/book/page/validate_form.html|表单验证]]的说明
=====修改URL=====
我们可以让我们网志的URL更加亲和用户和搜索引擎。就用贴的标题作为URL吧。
标题为URL存在一个问题 - 标题可含有特殊字符如空格。你可以转换字符,但在有空格的情况下会产生不美观的URL(例如%20)。所以最好是在Post模型(model)中加入一个清理标题的函数。打开sf_sandbox/lib/model/Post.php,添加函数:
public function getStrippedTitle()
{
$result = strtolower($this->getTitle());
// strip all non word chars
$result = preg_replace('/\W/', ' ', $result);
// replace all white space sections with a dash
$result = preg_replace('/\ /', '-', $result);
// trim dashes
$result = preg_replace('/\-$/', '', $result);
$result = preg_replace('/^\-/', '', $result);
return $result;
}
现在你能为post模块建立一个permalink动作. 打开modules/post/actions/actions.class.php,加入函数:
public function executePermalink()
{
$posts = PostPeer::doSelect(new Criteria());
$title = $this->getRequestParameter('title');
foreach ($posts as $post)
{
if ($post->getStrippedTitle() == $title)
{
break;
}
}
$this->forward404Unless($post);
$this->getRequest()->setParameter('id', $post->getId());
$this->forward('post', 'show');
}
打开模版modules/post/templates/listSuccess.php, 把显示id的部分去掉,且把
getTitle() ?>
修改为:
getTitle(), '/'.$sf_last_module.'/permalink?title='.$post->getStrippedTitle()) ?>
最后,打开sf_sandbox/apps/frontend/config/routing.yml,加入:
list_of_posts:
url: /latest_posts
param: { module: post, action: list }
post:
url: /weblog/:title
param: { module: post, action: permalink }
打开网页测试新的URL。
#sym[{{symfony:first_tub:first_routing.gif|}}]#
更多关于[[http://www.symfony-project.com/content/book/page/routing.html|智慧URL]]的说明
=====整理前端程序=====
此步主要是限制用户的权限 - 只给用户阅读的权力。
打开modules/post/templates/showSuccess.php模版,删除这行:
getId()) ?>
同样地,打开modules/post/templates/listSuccess.php模版,删除:
打开modules/post/actions/actions.class.php,删除函数:
* executeCreate
* executeEdit
* executeUpdate
* executeDelete
到此为止,用户只拥有阅读的权限了。
=====制作后端程序=====
让我们制作一个可让我们发帖的后端程序。在转案的根目录下(sf_sandbox),输入:
$ symfony init-app backend $ symfony propel-init-admin backend post Post $ symfony propel-init-admin backend comment Comment这一次,我们用[[http://www.symfony-project.com/content/book/page/generator.html|管理生成器(Admin generator)]],它比CRUD更强大且具更多的功能。 打开apps/backend/template/layout.php模版,加入共享导航链接:
#note[**注意**:Note: Because you are using a sandbox, you also need to copy the sf_sandbox/web/sf/images/sf_admin/ directory in a sf/images/sf_admin/ directory in your web root folder (this is due to image paths in CSS stylesheets).(这段不是很明确,有待翻译)]#
你能访问刚做好的后端程序 - 以调试环境(develop environment):
http://localhost/sf_sandbox/web/backend_dev.php/post
#sym[{{symfony:first_tub:first_basic_admin.gif|}}]#
使用Admin generator最大的好处是通过配置文件,你可以轻易地制作自定义方案(configuration file)。
修改backend/modules/post/config/generator.yml:
generator:
class: sfPropelAdminGenerator
param:
model_class: Post
theme: default
fields:
title: { name: Title }
excerpt: { name: Exerpt }
body: { name: Body }
nb_comments: { name: Comments }
created_at: { name: Creation date }
list:
title: Post list
layout: tabular
display: [=title, excerpt, nb_comments, created_at]
object_actions:
_edit: -
_delete: -
max_per_page: 5
filters: [title, created_at]
edit:
title: Post detail
fields:
title: { type: input_tag, params: size=53 }
excerpt: { type: textarea_tag, params: size=50x2 }
body: { type: textarea_tag, params: size=50x10 }
created_at: { type: input_date_tag, params: rich=on }
以上的Post model有一列nb_comments。我们的模型(model)没有提供这一函数。打开sf_sandbox/lib/model/Post.php模型,加入下列函数:
public function getNbComments()
{
return count($this->getComments());
}
打开页面,更新(refresh),测试刚完成的模块。
#sym[{{symfony:first_tub:first_custom_admin.gif|}}]#
=====限制访问后端程序的权限=====
目前的后端程序没有限制访问,你应该限制它的访问权限。
新建一个apps/backend/modules/post/config/security.yml文件,加入:
all:
is_secure: on
为comment模块新建同样的文件。
现在,你不能访问以上的两个模块由于还未登录。登录模块尚未建立,所以我们现在就建一个。创建登录模块,输入:
$ symfony init-module backend security新建的模块将用来处理登陆请求。打开apps/backend/modules/security/templates/indexSuccess.php模版,加入:
Authentication
hasErrors()): ?>
Identification failed - please try again
get('login')) ?>
打开apps/backend/modules/security/actions/actions.class.php,添加一个登陆的动作:
public function executeLogin()
{
if ($this->getRequestParameter('login') == 'admin' && $this->getRequestParameter('password') == 'password')
{
$this->getUser()->setAuthenticated(true);
return $this->redirect('default/index');
}
else
{
$this->getRequest()->setError('login', 'incorrect entry');
return $this->forward('security', 'index');
}
}
最后一步,把security模块设为默认的登陆模块。具体做法是,打开apps/backend/config/settings.yml,加入:
all:
.actions:
login_module: security
login_action: index
如果你现在访问Post的后端程序,你将会要求输入帐号和密码。
#sym[{{symfony:first_tub:first_login.gif|}}]#
更多关于[[http://www.symfony-project.com/content/book/page/security.html|安全]]的说明
=====结论=====
恭喜,你完成了一个网路日志。你可以在做业环境测试它的性能:
frontend: http://localhost/sf_sandbox/web/index.php/
backend: http://localhost/sf_sandbox/web/backend.php/
如果在做业环境下发生错误,大多数情况出于缓存的问题。清除缓存,输入:
$ symfony cc全文完 ======讨论/问题====== 有关于教程的问题请加在[[discuss:我的第一个symfony项目|这里]]。