回顾在我们第八天的学习中,我们很容易的为askeet添加了AJAX交互。现在程序已经相当有用了,但是还需要大量的小修改。在问题体中应允许丰富的文本,而主键不应出现在URI中。在Symfony中修正这些问题并不难,今天将是我们练习我们所学东西的一个好机会,并且可以检测我们是否已经知道如何操作MVC结构的所有层。
在question与answer上允许丰富的文本格式Markdownquestion与answer现在只允许接受普通文本。要允许基本的格式,粗体,斜体,超链接,图片,等,我们将会使用一个外部库,而不是重新发明轮子。
如果我们阅读过文本格式的Symfony文档,那么你也许知道我们是Markdown的fans。Markdown是一个文本到HTML的转换工具,以及一个文本格式化语法。例如,与Wiki或是论坛语法相比,Markdown的最大好处就是普通的markdown文本文件仍然具有可读性:
Test Markdown text
------------------
This is a **very simple** example of [Markdown][1].
The best thing about markdown is its _auto-escape_ feature for code chunks:
<a href="http://www.symfony-project.com">link to symfony</a>
>The `<` and `>` are properly escaped as `<` and `>`,
>and are not interpreted by any browser
[1]: http://daringfireball.net/projects/markdown/ "Markdown"
Markdown的渲染结果如下:
Test Markdown text
This is a very simple example of Markdown. The best thing about markdown is its auto-escape feature for code chunks:
<a href="http://www.symfony-project.com">link to symfony</a>
The < and > are properly escaped as < and >, and are not interpreted by any browser
Markdown库尽管最初是使用Perl语言编写的,但是现在Markdown已经可以作为一个PHP库来下载了。而这正是我们要使用的。下载markdown.php文件,并将其放置在askeet工程的lib目录下(askeet/lib)。就是这样:如果在我们的文件中首先进行请求,那么现在askeet程序的所有类都可以访问这个库了:
require_once('markdown.php');
我们可以在每次要显示消息体时调用Markdown转换器,但是这会给我们的服务器造成沉重的负载。我们会在问题创建时将文本体转换为HTML,并且在Question表中存储内容的HTML版本。也许我们已经熟悉这样了,所以模块扩展并不惊奇。
扩展模块首先在schema.xml中的Question表部分添加一列:
column name="html_body" type="longvarchar" />
然后,生成模块并且更新数据库:
$ symfony propel-build-model
$ symfony propel-build-sql
$ symfony propel-insert-sql
覆盖setBody方法当调用Question类的->setBody()方法时,html_body列必须使用文本内容的Markdown版本进行更新。打开askeet/lib/model/Question.php模块文本,并且编写下面的函数:
public function setBody($v)
{
parent::setBody($v);
require_once('markdown.php');
// strip all HTML tags
$v = htmlentities($v, ENT_QUOTES, 'UTF-8');
$this->setHtmlBody(markdown($v));
}
在设置HTML内容之前使用htmlentities()函数可能保护askeet免受跨站点脚本(cross-site-scripting XSS)攻击,因为所有的<script>标记都会转义。
更新测试数据我们会在问题的测试数据中添加一些Markdown格式(askeet/data/fixtures/test_data.yml),来检测转换可以正常工作:
Question:
q1:
title: What shall I do tonight with my girlfriend?
user_id: fabien
body: |
We shall meet in front of the __Dunkin'Donuts__ before dinner,
and I haven't the slightest idea of what I can do with her.
She's not interested in _programming_, _space opera movies_ nor _insects_.
She's kinda cute, so I __really__ need to find something
that will keep her to my side for another evening.
q2:
title: What can I offer to my step mother?
user_id: anonymous
body: |
My stepmother has everything a stepmother is usually offered
(watch, vacuum cleaner, earrings, [del.icio.us](http://del.icio.us) account).
Her birthday comes next week, I am broke, and I know that
if I don't offer her something *sweet*, my girlfriend
won't look at me in the eyes for another month.
我们现在可以重新装入数据库:
$ php batch/load_data.php
修改模板question模块的showSuccess.php模板可以进行简单的修改:
...
<div class="question_body">
<?php echo $question->getHtmlBody() ?>
</div>
...
list模板片段(_list.php)也会显示内容,但是却是截短的形式:
<div class="question_body">
<?php echo truncate_text(strip_tags($question->getHtmlBody()), 200) ?>
</div>
现在可以测试所有的修改了:显示我们修改的三个页面,并且观察由测试数据所显示的格式化文本:
http://askeet/question/list
http://askeet/recent
http://askeet/question/show/stripped_title/what-shall-i-do-tonight-with-my-girlfriend
同样的修改也适用于Answer内容:在模块中创建一个html_body列,覆盖->setBody()方法,question/show所显示的another时使用的是->getHtmlBody()方法,而不是->getBody()方法。因这些代码与上面的完全相同,所以在这里我们不再进行描述,但是我们可以在SVN代码中看到修改。
隐藏所有的id在Symfony中另一个好的习惯就是尽量避免将主键作为参数传递。这是因为我们的主键是自动增加的,这样就为破坏都提供了太多的关于数据库记录的信息。另外,这样显示的URI并没有任何意义,而且也不适用于搜索引擎。
例如,以用户配置页面为例。现在,他使用用户id作为参数。但是如果我们确保nickname是唯一的,那么他也可以是请求的参数。让我们进行修改。
更改动作编辑user/show动作:
public function executeShow()
{
$this->subscriber = UserPeer::retrieveByNickname($this->getRequestParameter('nickname'));
$this->forward404Unless($this->subscriber);
$this->interests = $this->subscriber->getInterestsJoinQuestion();
$this->answers = $this->subscriber->getAnswersJoinQuestion();
$this->questions = $this->subscriber->getQuestions();
}
更改模块在askeet/lib/model/目录下的UserPeer中添加下面的方法:
public static function retrieveByNickname($nickname)
{
$c = new Criteria();
$c->add(self::NICKNAME, $nickname);
return self::doSelectOne($c);
}
更改模板现在到用户配置的显示链接必须关注用户的nickname而不是其id。
在question/showSuccess.php,question/_list.php模板中,将下面的代码:
<?php echo link_to($question->getUser(), 'user/show?id='.$question->getUserId()) ?>
替换为:
<?php echo link_to($question->getUser(), 'user/show?nickname='.$question->getUser()->getNickname()) ?>
对answer/_answer.php模板也要做同样的修改。
添加路由规则在这个动作的转发配置中添加一条新的规则,从而url模式会显示一个nickname请求参数:
user_profile:
url: /user/:nickname
param: { module: user, action: show }
在执行Symfony的clear-cache命令之后,我们要做的最后一件事就是要测试我们的修改。
转发(routing)
除了今天的内容之外,我们到现在所编写的许多动作都使用默认的转发规则,从而模块的名字与动作会显示在浏览器的地址栏中。我们已经了解了如何进行修正,所以让我们为所有的动作定义URL模式。编辑askeet/apps/frontend/config/routing.yml:
# question
question:
url: /question/:stripped_title
param: { module: question, action: show }
popular_questions:
url: /index/:page
param: { module: question, action: list, page: 1 }
recent_questions:
url: /recent/:page
param: { module: question, action: recent, page: 1 }
add_question:
url: /add_question
param: { module: question, action: add }
# answer
recent_answers:
url: /recent/answers/:page
param: { module: answer, action: recent, page: 1 }
# user
login:
url: /login
param: { module: user, action: login }
logout:
url: /logout
param: { module: user, action: logout }
user_profile:
url: /user/:nickname
param: { module: user, action: show }
# default rules
homepage:
url: /
param: { module: question, action: list }
default_symfony:
url: /symfony/:action/*
param: { module: default }
default_index:
url: /:module
param: { action: index }
default:
url: /:module/:action/*
如果我们在生产环境中进行浏览,那么强烈建议在测试这些配置修改之前清除缓存。
Symfony转发的一个好习惯就是在一个link_to()帮助器中使用规则名,而不是module/action。不仅因为其速度快(转发引擎并不需要分析转发配置来查找合适的规则),还因为他允许我们在规则名之后修改动作。Symfony一书的转发一章进行详细的解释。
<?php link_to('@user_profile?id='.$user->getId()) ?>
// is better than
<?php link_to('user/show?id='.$user->getId()) ?>
Askeet遵循Symfony的好习惯,所以我们今天所下载的代码在链接帮助器中只有规则名。将所有模板中的action/module替换为@rule以及自定义帮助器并不是一件有趣的事情,所以关于转发的最后一条建议就是:当我们创建动作时编写转发规则,并且从开始就在链接帮助器中使用规则名。
明天见
分享到:
相关推荐
symfony-demo-mater
symfony 实战 for doctrine 中文版
symfony-json-request-transformer, 用于解码JSON编码请求内容的Symfony 2事件侦听器 symfony-json-request-transformer用于解码JSON编码请求内容的Symfony事件侦听器。 请阅读关于这里知识库的博客文章,位于 /...
Symfony4-REST-API
将Symfony的 Var-Dump Server 引入Laravel
Ajax-Symfony-3.4-Blog.zip,symfony 3.4博客,带有管理仪表板ajax和许多其他功能!,ajax代表异步javascript和xml。它是多种web技术的集合,包括html、css、json、xml和javascript。它用于创建动态网页,其中网页的小...
symfony 1.4 DI 支持 这里解释了如何在 symfony 1.4 项目中使用 Composer 的 Symfony/DependencyInjection。 (!) 注意 不建议将 symfony 1.4 用作创建新项目。 如果你想创建一个新项目,你应该使用 Symfony2 框架。 ...
symfony-console-form, 为控制台命令输入使用Symfony窗体 控制台窗体By Noback 这个包包含一个Symfony包和一些工具,允许你使用Symfony表单类型来定义和交互处理来自的用户输入。安装composer require ...
symfony4-embedsocial-project
symfony5-api-平台
Laravel开发-symfony-web-uploader 简化文件上传到外部API的PHP库
基于Symfony 3.4的演示项目安装1.克隆或下载存储库https://github.com/edlef/symfony-demo.git2.运行作曲家composer install3.运行安装脚本以创建数据库并加载固定装置sh bin/install.sh4.运行服务器并转到或设置VH ...
谢谢你的symfony:感谢Symfony-向Symfony致谢
Symfony2 安全 REST APISymfony2 Rest Api 使用 Json Web Token 进行保护 该项目是使用 和 Symfony2 的安全 Restful API 模板在 5 分钟内创建您的 REST API 克隆或分叉此存储库bash $ git clone ...
Symfony PHP CodeSniffer编码标准一种与编码标准进行对照的,最初是从-disappeared- opensky / Symfony2-coding-standard标准存储库无耻地复制而来的。安装作曲家该标准可以与依赖性管理器一起安装。将编码标准安装...
symfony-tele2-notifier Транспортчерезtele2(SMS-ТАРГЕТ)длякомпонентаSymfony通知程序 .env TELE2_DSN =tele2://login:password@default? from =AUTHOR config / packages ...
symfony2-and-google-translate 应用程序通过谷歌翻译 API 创建您的 sf2 翻译文件。如何使用获得作曲家。 获取供应商: $ php composer.phar install 将您的基本 messages.[yourlanguage].yml 文件放在 datas 目录中...
Symfony2 PHP CodeSniffer 编码标准这最初取自但在此 repo 关闭时重新上传。 用于检查安装安装phpcs: pear install PHP_CodeSniffer找到你的 PEAR 目录: pear config-show | grep php_dir将这个 repo 复制、符号...
Symfony2 PayPal IPN捆绑 概述 是一个Symfony2软件包,用于使用PayPal IPN(即时付款通知)服务。 该捆绑包充当PayPal IPN服务的侦听器,并使用Doctrine将传入的订单记录到您的数据库中。 发送订单确认电子邮件也很...
Api-symfony-flex-backend.zip,带有symfony flex的rest api这是什么,一个api可以被认为是多个软件设备之间通信的指导手册。例如,api可用于web应用程序之间的数据库通信。通过提取实现并将数据放弃到对象中,api简化...