原文:http://www.symfony-project.com/askeet/10 ======用Ajax表格改变数据====== **回顾:** 在昨天对已知技术的回顾之后,你们当中的一些人开始期盼互动性了。显示富格式的问题和清单,甚至是分页,是不足以让一个应用程序生动起来的。并且askeet的核心概念是允许注册用户提出新问题,其他用户去回答现存问题。现在是我们开始去完成它的时间? =====添加新问题===== 在[[http://symfony-cn.thecodecentral.com/askeet_7|第七天]]创建的边栏已经包含了添加新问题的链接。这个链接调用还未实现的question/add动作。 ====对注册用户控制访问==== 首先,只有注册用户才能添加新问题。为了限制对question/add动作的访问,在askeet/apps/frontend/modules/question/config/目录中创建security.yml文件: 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模板:
get('title')) ?>
get('body')) ?>
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()助手一样,它打开和关闭活动指示器的可视性。