`
helpbs
  • 浏览: 1154774 次
文章分类
社区版块
存档分类
最新评论

创建第一个Symfony工程

 
阅读更多
我们的第一个Symfory工程

现在我们试验一下Symfony。我们要在一个小时内构建一个全功能的网络程序。一个书店销售程序?可以!一个Weblog!这是一个好主意。让我们开始吧。

安装Symfony并且初始化工程

为了方便,我们将会使用Symfony沙盒。这是一个空的Symfony工程,在其中已经包含了所有所需要的库,并且完成了基本的配置。比起其他类型的安装,沙盒的最大优点就是我们可以立刻试验Symfony。

下载sf_sandbox.tgz,并且将其解压到我们的网络目录的根目录下。我们可以查看其中的readme文件,从而得以更为详细的信息。生成的文件结构如下所示:
www/
sf_sandbox/
apps/
frontend/
batch/
cache/
config/
data/
sql/
doc/
lib/
model/
log/
plugins/
test/
web/
css/
images/
js/

从这里我们可以看出sf_sandbox工程包含一个前端程序。我们可以通过请求下面的URL来测试沙盒:
http://localhost/sf_sandbox/web/index.php/

我们应会看到一个祝贺页面。

我们也可以将symfony安装一个定制的目录中,并且使用一个虚拟主机或是一个Alias设置我们的网络服务器。

初始化数据模型

所以Weblog应可以处理文章发布,并且我们可以进行评论。在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,所以记住要使用空格来进行缩进。我们将会在配置一章中了解更多的YAML以及symfony配置的内容。

这个概要描述了weblog所需要的两个表的结构。Post与Comment是要创建的相关类的名字。保存这个文件,打开一个命令行,进行sf_sandbox目录,并且执行下面的命令:
$ php symfony propel-build-model

注意:要保证我们在调用symfony命令时在我们工程的根目录下。

在sf_sandbox/lib/model/目录下创建了一些类。这些是对象相关映射的类,这允许我们在面向对象的代码中而不使用单一的SQL查询来访问相关的数据库。为了这个目的,Symfony使用Propel库。我们将这些对象称之为模型(model)。

现在我们执行下面的命令:
$ php symfony propel-build-sql

这会在sf_sandbox/data/sql/目录下创建一个lib.model.schema.sql文件。这个SQL查询可以用来使用相同的表结构来初始化一个数据库。我们可以使用命令行或是Web界面在MySQL中创建一个数据库。幸运的是symfony沙盒的配置使用一个简单的SQLite文件来进行工作,所以不需要数据库的初始化工作。默认情况下,sf_sandbox工程会使用sf_sandbox/data/目录中的sandbox.db进行工作。要构建基于SQL文件的表结构,可以输入下面的命令:
$ php symfony propel-insert-sql

注意:如果此时有警告并不用担心。insert-sql命令在添加我们的lib.model.schema.sql数据表之前会移除已存在的数据表,而在此时并没有需要移除的数据表。

(注:此时如果我们需要连接的是MySQL数据的话,我们做如下的一些修改:
1 修改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,其各部分分别为用户名,密码,密码以及数据库的名字
)

创建程序框架

一个Blog的基本特征是可以创建,获取,更新以及删除发表及评论。因为我们是初次接触Symfony,所以我们不会从头开始创建Symfony代码,而是让Symfony创建一个我们可以使用并且在需要时可以修改的程序框架。Symfony可以解释数据模型来自动创建CRUD接口。

$ php symfony propel-generate-crud frontend post Post
$ php symfony propel-generate-crud frontend comment Comment
$ php symfony clear-cache

On *nix systems, you will have to change some rights:
$ chmod 777 data
$ chmod 777 data/sandbox.db

现在我们就拥有了两个可以用来处理Post与Comment类的模型(post,comment)。一个模型(module)通常代表一个页面或是具有相似目的的一组页面。我们的新模型位于sf_sandbox/apps/frontedn/modules/目录下,而他们可以通过下面的URL来进行访问:

http://localhost/sf_sandbox/web/frontend_dev.php/post
http://localhost/sf_sandbox/web/frontend_dev.php/comment

此时我们可以进行一些简单的测试。

修改布局

为了在两个新模型之间进行切换,weblog需要一些全局的浏览设置。

编辑全局模板sf_sandbox/apps/frontend/templates/layout.php,修改body内容如下:

<div id="container" style="width:600px;margin:0 auto;border:1px solid grey;padding:10px">

<div id="navigator" style="display:inline;float:right">
<ul>
<li><?php echo link_to('List of posts','post/list') ?></li>
<li><?php echo link_to('List of comments','comment/list') ?>
</ul>
</div>
<div id="title">
<h1><?php echo link_to('My First symfony project','@homepage') ?></h1>
</div>

<div id="content" style="clear:right">
<?php echo $sf_data->getRaw('sf_content') ?>
</div>
</div>

当我们编辑这个工程时,我们可以改变我们页面的标题。编辑程序(sf_sandbox/apps/frontend/config/view.yml)的视图配置文件,定位到显示title关键字的行,将其改为:
metas:
title: The best weblog ever
robots: index, follow
description: symfony project
keywords: symfony, project
language: en

主页面本身需要改变。他使用默认模块的默认模板,这个默认模块保存在框架中,但是却并不在我们的程序目录中。要覆盖他,我们需要创建一个自己的main模块:

$ php symfony init-module frontend main

默认情况下,index动作会显示一个默认的欢迎页面。要移除他,我们可以编辑sf_sandbox/apps/frontend/modules/main/actions/actions.class.php文件,并且移除executeIndex()方法的内容:
public function executeIndex()
{
}

编辑sf_sandbox/apps/frontend/modules/main/templates/indexSuccess.php文件来显示我们的欢迎页面:
<h1>Welcome to my swell weblog</h1>
<p>You are the <?php echo rand(1000,5000) ?>th visitor today.</p>

现在我们需要告诉Symfony当请求主页时需要执行哪个action。为了这个目的,我们可以编辑sf_sandbox/apps/frontend/config/routing.yml文件,并且改变homepage规则:
homepage:
url: /
param: { module: main, action: index }

我们可以通过再一次请求主页来查看结果:
http://localhost/sf_sandbox/web/frontend_dev.php/

从action向template传递数据

这样的速度很快,不是吗?现在我们要将评论模块与一个文章发布想混合,从而可以在一个发布的文章下面显示评论。

首先,我们要使得发布的评论为发布显示模板可用。在Symfony中这种逻辑类型是保存在actions中。编辑动作文件sf_sandbox/apps/frontend/modules/post/actions/actions.class.php,更改executeShow()方法,并且添加下面的最后四行:
public function executeShow()
{
$this->post = PostPeer::retrieveByPk($this->getRequestParameter('id'));
$this->forward404Unless($this->post);

$c = new Criteria();
$c->add(CommentPeer::POST_ID, $this->getRequestParameter('id'));
$c->addAscendingOrderByColumn(CommentPeer::CREATED_AT);
$this->comments = CommentPeer::doSelect($c);
}

Criteria与-Peer类是Propel类型关系映射的一部分。基本来说,这四行将会处理一个到Comment表的SQL查询,从而得到与当前发布相关的评论(通过URL参数id来指定)。动作中的$this->comments行将可以访问相应的模板中的一个$comments变量。现在修改发布显示模板sf_sandbox/apps/frontend/modules/post/templates/showSuccess.php,并且在最后添加下面的代码行:
...
<?php use_helper('Text', 'Date') ?>

<hr />
<?php if ($comments) : ?>
<p><?php echo count($comments) ?> comment<?php if (count($comments) > 1) : ?>s<?php endif; ?> to this post.</p>
<?php foreach ($comments as $comment): ?>
<p><em>posted by <?php echo $comment->getAuthor() ?> on <?php echo format_date($comment->getCreatedAt()) ?></em></p>
<div class="comment" style="margin-bottom:10px;">
<?php echo simple_format_text($comment->getBody()) ?>
</div>
<?php endforeach; ?>
<?php endif; ?>

这个页面使用了Symfony提供的新的PHP函数(format_date(),simple_format_text()),并且调用'helpers',因为他们会为我们完成一些需要更多时间和代码的任务。为我们的第一个发布添加一条评论,然后检测我们的第一个发布,我们可以点击列表中前面的标号,或是直接点击列表链接本身:
http://localhost/sf_sandbox/web/frontend_dev.php/post/show?id=1

添加与另一个表相关的记录

当添加一条评论时,我们可以选择相应的发布id。这并不是用户友好的。让我们来改变这种行为,从而可以保证用户在添加一条评论之后可以返回到他正在查看的发布上。

首先,在modules/post/templates/showSuccess.php模板底部添加下面一行:
<?php echo link_to('Add a comment', 'comment/create?post_id='.$post->getId()) ?>

link_to() helper会创建一个指向评论模板中创建动作的一个超链接,所以我们可以直接由发布详细页面中添加一个评论。接下来,打开modules/comment/templates/editSuccess.php并且替换下面的代码行:
<tr>
<th>Post:</th>
<td><?php echo object_select_tag($comment, 'getPostId', array (
'related_class' => 'Post',
)) ?></td>
</tr>

<?php if ($sf_params->has('post_id')): ?>
<?php echo input_hidden_tag('post_id',$sf_params->get('post_id')) ?>
<?php else: ?>
<tr>
<th>Post*:</th>
<td><?php echo object_select_tag($comment, 'getPostId', array('related_class' => 'Post')) ?></td>
</tr>
<?php endif; ?>

comment/create页面中的表单指向一个comment/update动作,当提交成功时会重新定向到comment/show(这是生成的CRUD的默认动作)。对于weblog而言,在对于一个文章发布添加了一个评论之后,则会显示评论的详细内容。更好的解决是与评论同时显示发布文章的内容。所以打开modules/comment/actions/actions.class.php,并且找到executeUpdate()方法。注意动作中并没有定义created_at域:Symfony会知道当创建一个记录时一个名为created_at的域会设置为系统时间。动作的最后重定向必须进行修改来指向正确的动作。所以将其改为:
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());
}

现在用户可以为文章发布添加一个评论,并且在评论之后返回文章页面。我们希望一个weblog吗?现在我们已经有了一个weblog。

表单验证

访问者可以发布评论,但是如果他们所提交的表单没有任何内容时会怎么样呢?我们就会得到一个脏数据库。为了避免这样的情况,在sf_sandbox/apps/frontend/modules/comment/validate/目录下创建一个名为update.yml的文件,并且写入下面的内容:
methods:
post: [author, email, body]
get: [author, email, body]

fillin:
enabled: 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.

注意,我们不应拷贝每一行开头的额外四个空格,因为这样YAML会解析抢购。这个文件的第一行必须为methods的m。

fillin动作会保证在验证失败的情况下会使用用户以前输入的数据来填充表单。名字声明部分为每一个表单的输入域设置验证规则。

在默认情况下,当检测到一个错误时控制器会将用户重定向到一个updateError.php模板。更好的方法是重新显示带有错误信息的表单。为了达到这样的目的,在modules/comment/actions/actions.class.php文件中添加一个handleErrorUpdate方法:
public function handleErrorUpdate()
{
$this->forward('comment', 'create');
}

现在就要完成了,再一次打开modules/comment/templates/editSuccess.php模板,并且在顶部添加下面的代码行:
<?php if ($sf_request->hasErrors()): ?>
<div id="errors" style="padding:10px;">
Please correct the following errors and resubmit:
<ul>
<?php foreach ($sf_request->getErrors() as $error): ?>
<li><?php echo $error ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>

现在我们就有一个健壮的表单了。

改变URL形式

我们是否注意到了URL的形式?我们可以使得URL对于用户和搜索引擎更为的友好。对于文章发布而言,我们要使用post标题作为URL。

问题在于post标题可能包含特殊的字符,例如空格。如果我们只是进行转义,那么URL则会显示为丑陋的%20之类的内容,所以我们应扩展这个模块来添加一个新的方法,从而得到一个干净的,简洁的标题。要达到这样的目的,编辑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');
}

post列表可以调用permalink动作。在modules/post/templates/listSuccess.php,删除id表头及相应的单元,并且将Ttitle的形式
<td><?php echo $post->getTitle() ?></td>
改为下面的形式:
<td><?php echo link_to($post->getTitle(), 'post/permalink?title='.$post->getStrippedTitle()) ?></td>

还剩下另外一步:编辑位于sf_sandbox/apps/frontend/config/目录下的routing.yml文件,并且在顶部添加下面的规则:

下面我们可以浏览我们的程序并且查看URL的形式。

前端清理

当然,如果这是一个weblog,那么每一个人都可以发表。而这不是我们所希望的样子,对吧?好吧,让我们对于我们的模板进行一些小的清理。

在模板modules/post/templates/showSuccess.php中,通过移除下面的行来清除edit链接:
<?php echo link_to('edit', 'post/edit?id='.$post->getId()) ?>

对于modules/post/templates/listSuccess.php模板也执行同样的操作:
<?php echo link_to('create', 'post/create') ?>

我们同时也需要从modules/post/actions/actions.class.php文件中移除下面的方法:
* executeCreate
* executeEdit
* executeUpdate
* executeDelete

好了,现在读者再也不可以发表了。

生成后端

对于我们来说要发表文章,我们可以通过在命令行输入下面的命令来生成一个后端程序:
$ php symfony init-app backend
$ php symfony propel-init-admin backend post Post
$ php symfony propel-init-admin backend comment Comment

此时,我们使用admin生成器。也基本的CURD生成器相比,他提供了更多的特性与自定义。

正如我们在前端程序所做的,编辑layout(apps/backend/templates/layout.php)来添加全局浏览:
<div id="navigation">
<ul style="list-style:none;">
<li><?php echo link_to('Manage posts', 'post/list') ?></li>
<li><?php echo link_to('Manage comments', 'comment/list') ?></li>
</ul>
</div>
<div id="content">
<?php echo $sf_data->getRaw('sf_content') ?>
</div>

在开发环境下,我们可以通过调用下面的URL来访问我们的后台程序:
http://localhost/sf_sandbox/web/backend_dev.php/post

生成的admin的最大优点就是我们可以通过编辑一个配置文件很容易的来进行定制。将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表的列中,admin将会查找nb_comments。但是此时并没有相关的方法,但是很容易在sf_sandbox/lib/model/Post.php中添加:
public function getNbComments()
{
return count($this->getComments());
}

现在我们可以刷新页面来查看改变。

限制后台访问

后台可以为任何人访问。我们需要添加访问限制。在apps/backend/modules/post/config/目录下添加一个security.yml文件,其内容为:
all:
is_secure: on
对于comment模块也要重复同样的操作。现在我们不可以访问这些模块,除非我们已经进行登陆。

但是登陆现在并不存在。好吧,我们可以很容易的添加一个。首先,创建一个安全模块框架:
$ php symfony init-module backend security
这个新的模块将用来处理登陆表单与请求。编辑apps/backend/modules/security/templates/indexSuccess.php来创建登陆表单:
<h2>Authentication</h2>
<?php if ($sf_request->hasErrors()): ?>
Identification failed - please try again
<?php endif; ?>

<?php echo form_tag('security/login') ?>
<label for="login">login:</label>
<?php echo input_tag('login', $sf_params->get('login')) ?>

<label for="password">password:</label>
<?php echo input_password_tag('password') ?>

<?php echo submit_tag('submit', 'class=default') ?>
</form>

在安全模块中添加为表单所调用的登陆动作(apps/backend/modules/security/actions.class.php):
public function executeLogin()
{
if ($this->getRequestParameter('login') == 'admin' && $this->getRequestParameter('password') == 'password')
{
$this->getUser()->setAuthenticated(true);

return $this->redirect('main/index');
}
else
{
$this->getRequest()->setError('login', 'incorrect entry');

return $this->forward('security', 'index');
}
}

作为主模块,在index动作中移除默认代码:
public function executeIndex()
{
}

最后一件事情就是要将安全模块作为默认模块来处理登陆动作。为了完成这样的任务,打开apps/backend/config/setting.yml配置文件,并且添加下面的代码行:
all:
.actions:
login_module: security
login_action: index
这样当我们试着访问文章发布管理时,我们必须输入登陆名与密码。

结论

好了,现在我们学习的时间已经到了。我们完成了我们的练习。现在我们可以在生产环境下使用这些程序。
frontend: http://localhost/sf_sandbox/web/index.php/
backend: http://localhost/sf_sandbox/web/backend.php/

此时,如果我们遇到错误,也许是因为我们在cacahe中放了一些动作后改变了模块造成的(在开发环境下,cacahe并没有激活)。要清除cacahe,我们可以简单的输入下面的命令:
$ php symfony cc

分享到:
评论

相关推荐

    创建你的第一个Symfony的页面.docx

    创建你的第一个Symfony的页面,用symfony框架创建一个页面。

    symfony3.4离线工程包

    基于最佳Web开发实践,已经有多个网站完全采用此框架开发,symfony的目的是加速Web应用的创建与维护。 同时,它还包含了很多工具和类用以缩短开发复杂的网络应用的时间。 但是它的使用需要安装composer等工具,并且...

    symfony2创建实体命令

    symfony2创建实体命令 四步骤

    Symfony

    Symfony应用程序文档生成器

    symfony介绍.pdf

    著名的PHP开发框架 symfony是什么?symfony能做什么?使用symfony需要掌握哪些知识?此文档会解答这些问题。

    symfony2建立一个完整blog的例子

    国内symfony2的框架资料较少,很多都是symfony1.x的资料,当symfony2的框架改变非常大,为方便你我,分享一下学习过程中看过的资料,这个vblog的实例非常不错。

    PHP Symfony使用教程.zip

    PHP Symfony使用教程.zip

    symfony-console-form, 为控制台命令输入使用Symfony窗体.zip

    symfony-console-form, 为控制台命令输入使用Symfony窗体 控制台窗体By Noback 这个包包含一个Symfony包和一些工具,允许你使用Symfony表单类型来定义和交互处理来自的用户输入。安装composer require ...

    Symfony 高性能php框架 v4.3.zip

    基于最佳Web开发实践,已经有多个网站完全采用此框架开发,symfony的目的是加速Web应用的创建与维护。 Symfony的系统需求不高,可以被轻易的安装在任意设置上:你只需一个Unix或Windows,搭配一个安装了PHP5的网络...

    《Symfony权威指南》

    本书介绍了symfony这个领先的PHP开发框架,展示如何利用symfony的众多功能更快更有效率地开发Web应用程序,即使你只懂一点PHP也没有关系。在本书里,你将了解如何使用symfony建立Web应用程序。本书分成五篇:“基础...

    symfony-jsonapi, 用于 Symfony 2和 Symfony 3的JSON API变压器包.zip

    symfony-jsonapi, 用于 Symfony 2和 Symfony 3的JSON API变压器包 Symfony json对于 Symfony 2和 Symfony 3 安装工具使用情况创建映射输出API响应JSON API请求JSON API响应与NelmioApiDocBund

    ClankBundle, 一个Symfony2包,用于 Ratchet web socket服务器.zip

    ClankBundle, 一个Symfony2包,用于 Ratchet web socket服务器 ClankBundleClank是一个Symfony2包,旨在在一个易于使用的应用程序架构中集成 web socket功能。socket.io 一样,它提供了服务器端和客户端代码,确保你...

    symfony_sandbox.tgz

    symfony_sandbox.tgz symfony_sandbox.tgz symfony_sandbox.tgz symfony_sandbox.tgz

    symfony1.4.9源码

    symfony1.4.9源码,symfony源代码

    使用symfony命令创建项目的方法

    这一章节描述一个Symfony项目的合理结构框架,并且用 symfony 命令初始项目结构。 介绍 在symfony里,一个项目是一个指定域名下的一组服务和有效操作,共享相同的项目模型。 在一个项目里面,应用中的操作是一组逻辑...

    第02章 探索 symfony 代码1

    第02章 探索 symfony 代码1

    symfony2.3.1

    symfony2 安装包下载,更快,更实在

    symfony API CHM手册

    symfony API CHM手册 好不容易搞到的,欢迎下载

    symfony权威指南中文版+symfony Model Criteria语句对照表

    symfony权威指南中文版 内容详细介绍了symfony,以及如何使用。配合官方的课程,可以在短期内学会使用symfony开发php的应用服务,另外附赠一个难得的symfony Model Criteria语句对照表,开发起来更是得心应手。

    symfony安装包

    symfony的安装包文件包含vendors

Global site tag (gtag.js) - Google Analytics