add:
is_secure: on
credentials: subscriber
all:
is_secure: off
当一个未登录用户尝试访问一个受控制动作,symfony将把他/她转向到登录动作。这个动作必须在应用程序setttings.yml中定义,必须位于login_module和login_action关键字之下:
all:
.actions:
login_module: user
login_action: login
更多关于动作访问控制的信息可以在symfony书中[[http://www.symfony-project.com/content/book/page/security.html|安全]]一章中找到。
====addSuccess.php模板====
question/add动作将被同时用在显示表格和处理表格上。这就意味着从现在开始,要显示表格,你仅仅需要一个空的动作。此外,如果在数据验证中出错,表格将会被再次显示。
public function executeAdd()
{
}
public function handleErrorAdd()
{
return sfView::SUCCESS;
}
这两个动作将输出addSuccess.php模板:
title和body控制器都有从同样名称的请求参数定义的默认值(表格助手的第二个参数)。为什么会是这样的?因为我们将要对表格添加一个验证文件。如果验证失败,表格会被再次显示,并且以前用户的输入仍然在请求参数中。他们可以被当作表格元素的默认值来使用。
{{add_question_error.gif|}}
如果验证失败,先前的输入不会丢失。这是你对用户友好应用程序的最少期待了。
但是为了实现这个目标,你需要一个表格验证文件。
====表单验证====
在question模块中创建一个validate/目录,并且添加一个add.yml验证文件:
methods:
post: [title, body]
names:
title:
required: Yes
required_msg: You must give a title to your question
body:
required: Yes
required_msg: You must provide a brief context for your question
validators: bodyValidator
bodyValidator:
class: sfStringValidator
param:
min: 10
min_error: Please, give some more details
如果你需要更多关于表单验证的信息,请回到[[http://symfony-cn.thecodecentral.com/askeet_6|第六天]]或是阅读symfony书中[[http://www.symfony-project.com/content/book/page/validate_form.html|表单验证]]一章。
====处理表单递交====
现在再次编辑question/add动作来处理表单递交:
public function executeAdd()
{
if ($this->getRequest()->getMethod() == sfRequest::POST)
{
// create question
$user = $this->getUser()->getSubscriber();
$question = new Question();
$question->setTitle($this->getRequestParameter('title'));
$question->setBody($this->getRequestParameter('body'));
$question->setUser($user);
$question->save();
$user->isInterestedIn($question);
return $this->redirect('@question?stripped_title='.$question->getStrippedTitle());
}
}
记住->setTitle()方法将也设置stripped_title,并且->setBody()方也将设置html_body域。这是因为我们重载了Question.php模型类中这些方法。创建一个问题的用户将被声明对此有兴趣。这是有意阻止零兴趣问题的情况,否则会让人比较失落。
动作的结尾处包含了一个到被建立问题细节的->redirect()。较之->forward(),这样做的主要好处是如果用户过后刷新了问题细节页面,表格不会被再次递交。此外, ‘返回’按钮如同期待的那样工作。这是一条通用规则:你不应该用->forward()结束一个递交处理动作。
如果请求不是POST模式,最好的办法是动作仍然显示表单。它就表现得和之前写的空动作一摸一样,返回默认的sfView::SUCCESS调用addSuccess.php模板。
不要忘了在User模型中创建isInterestedIn()方法:
public function isInterestedIn($question)
{
$interest = new Interest();
$interest->setQuestion($question);
$interest->setUserId($this->getId());
$interest->save();
}
就像一个小的重构一样,你可以使用user/insterested动作中这个方法置换有同样功能的代码段。
再进一步,现在就测试它。使用一个测试用户帐号,你可以添加新问题了。
=====添加一个新问题=====
问题的添加将用一个稍微不同的方法实现。这里并没有必要通过重新定向用户到一个新的表单页,然后再到另外一个页面去显示问题。因此新问题表单将采用AJAX,并且新问题将会在问题细节页立即呈现。
====添加AJAX表单====
改写modules/question/templates/showSuccess.php模板的结束处为:
...
getAnswers() as $answer): ?>
$answer)) ?>
'@add_answer',
'update' => array('success' => 'add_answer'),
'loading' => "Element.show('indicator')",
'complete' => "Element.hide('indicator');".visual_effect('highlight', 'add_answer'),
)) ?>
isAuthenticated()): ?>
getNickname() ?>
get('body')) ?>
getId()) ?>
====一点点重构====
link_to_login()方法必须被添加到UserHelper.php助手:
function link_to_login($name, $uri = null)
{
if ($uri && sfContext::getInstance()->getUser()->isAuthenticated())
{
return link_to($name, $uri);
}
else
{
return link_to_function($name, visual_effect('blind_down', 'login', array('duration' => 0.5)));
}
}
这个函数实现了我们在其他User助手中已经看到的功能:如果用户通过验证它将显示一个到动作的链接;如果没有,连接将会被指向AJAX登录表单。因此置换在link_to_user_insterested()和link_to_user_relevancy函数中对link_to_function()的调用为link_to_login()。不要忘了在modules/sidebar/templates/defaultSuccess.php中对@add_question的链接。是的,这是重构。
====处理表单递交====
尽管它仍旧牵涉到了片段(fragment),在这里用到的处理AJAX请求的方法是与[[http://symfony-cn.thecodecentral.com/askeet_8|第八天中]]讲述的那种有着轻微的不同。这是因为我们想使表单递交的结果完全取代表单的位置。这就是为什么form_remote_tag()助手的update参数指向了表单自己的容器而不是一个外部区域。_answer.php片段将被包含在问题添加动作的结果中,从而使最终的结果看起来像:
...
...
你很可能已经猜到了form_remote_tage()javascript助手是怎样工作的:它通过XMLHttpRequest对象处理表单递交到在url参数指定的动作。动作的结果置换在update参数中指定的元素。并且,就像第八天的link_to_remote()助手一样,它打开和关闭活动指示器的可视性。