symfony表单与页面实现技巧
《PHP实例:symfony表单与页面实现技巧》要点: 本篇章节讲解symfony表单与页面实现技巧.分享给大家供大家参考.具体如下:PHP实战 symfony开发很简洁,但是功能的数量仍然很缺乏.现在是时候进行一些askeet站点与用户之间的交互了.而HTML交互的根本--除了起链接--就是表单了.PHP实战 这里我们的目标是允许用户登陆,并在主页的问题列表中进行翻阅.这对于开发而言是很快的,并且可以让我们回忆起前面的内容.PHP实战 登陆表单PHP实战 在测试数据中存在用户,但是程序却没有方法来进行验证.下面我们要在程序的每一个页面添加一个登陆表单.打开全局的布局文件askeet/apps/frontend/templates/layout.php,并且在到about的连接之前添加下面的代码行:
代码如下:
<li><?php echo link_to('sign in','user/login') ?></li>
当前的布局将这些链接放在web调试工具栏之后.要看到这些链接,点击'Sf'图标折叠起调试工具栏就可以看到了.PHP实战 现在需要创建user模块.而question模块是在第二天生成的,这一次我们只是叫symfony来创建模块框架,而我们将会自己来编写这些代码.
代码如下:
$ symfony init-module frontend user
这个框架包含一个默认的index动作与一个indexSuccess.php模板.删除他们,因为我们并不需要他们.PHP实战 创建user/login动作
代码如下:
在user/actions/action.class.php文件中,添加下面的登陆动作:
public function executeLogin() 这个动作将referer保存在哀求属性中.然后这个属性可为模块所用存放在一个隐藏区域中,从而这个表单的目的动作可以在成功登陆后重定向到原始的referer.PHP实战 语句return sfView::SUCCESS将动作执行结果传递到loginSuccess.php模块.这条语句是在一个不包含返回语句的动作中实现的,这也就是一个动作的默认模块被称之为actionnameSuccess.php的原因.PHP实战 在动作上开始更多的工作之前,我们先来看一下模块.PHP实战 创建loginSuccess.php模块PHP实战 web上的许多人机交互使用表单,而Symfony通过提供一个form赞助器集合来组织表单的创建与管理.PHP实战 在askeet/apps/frontend/modules/user/templates/目录下,创建下面的loginSuccess.php模块:
代码如下:
<?php echo form_tag('user/login') ?>
? ? <fieldset> ? ? <div class="form-row"> ??? <label for="nickname">nickname:</label> ??? <?php echo input_tag('nickname',$sf_params->get('nickname')) ?> ? </div> ? ? <div class="form-row"> ??? <label for="password">password:</label> ??? <?php echo input_password_tag('password') ?> ? </div> ? ? </fieldset> ? ? <?php echo input_hidden_tag('referer',$sf_request->getAttribute('referer')) ?> ? <?php echo submit_tag('sign in') ?> ? </form> 这个模块是我们第一次使用表单赞助器.这些Symfony函数可以赞助我们自动化编写表单标签.form_tag()打开一从此标签,使用POST作为默认的动作,并且指向作为参数传递的动作.input_tag()赞助器产生一个<input>标签,并且依据所传递的第一个参数自动添加一个id属性;而默认值则是由第二个参数得到.我们可以在Symfony一书的相关章节查找到更多的关于表单赞助器与他们所产生的HTML代码的内容.PHP实战 这里的实质是当表单提交时则会调用这个动作.所以我们返回来看一下这个动作.PHP实战 处理表单提交PHP实战 用下面的代码来替换我们刚才所编写的登陆动作:
代码如下:
public function executeLogin()
{ ? if ($this->getRequest()->getMethod() != sfRequest::POST) ? { ??? // display the form ??? $this->getRequest()->setAttribute('referer',$this->getRequest()->getReferer()); ? } ? else ? { ??? // handle the form submission ??? $nickname = $this->getRequestParameter('nickname'); ? ??? $c = new Criteria(); ??? $c->add(UserPeer::NICKNAME,$nickname); ??? $user = UserPeer::doSelectOne($c); ? ??? // nickname exists? ??? if ($user) ??? { ????? // password is OK? ????? if (true) ????? { ??????? $this->getUser()->setAuthenticated(true); ??????? $this->getUser()->addCredential('subscriber'); ? ??????? $this->getUser()->setAttribute('subscriber_id',$user->getId(),'subscriber'); ??????? $this->getUser()->setAttribute('nickname',$user->getNickname(),'subscriber'); ? ??????? // redirect to last page ??????? return $this->redirect($this->getRequestParameter('referer','@homepage')); ????? } ??? } ? } } 登陆动作可以同时用来显示登陆表单并且进行处理.相应的,他必须知道所调用的环境.如果这个动作并没有在POST模式下调用(因为是由一个链接来哀求的):而这正是我们在前面所讨论的情况.如果是在POST模式下哀求的,那么则会由表单调用这个动作并进行相应的处理.PHP实战 这个动作会由哀求参数得到nickname域的值,并且查询User表来查看在数据库是否存在此用户.PHP实战 将来一个密码控制将会为用户分配凭证.但是现在,这个动作所做的只是在一个会话属性中存储用户的id与nickname属性.最后,这个动作重定向到表单中隐藏中的原始referer域,这是作为一个哀求参数传递的.如果这个域是空的,则会使用默认值.PHP实战 这里我们需要注意这个例子中两种类型的属性集合之间的区别:request attributes($this->getRequest()->setAttribute())是为模板所保存的,而且只要答案发送到referer则会被忘记.session attributes($this->getUser()->setAttribute())是在整个用户会话生命期被保存的,而且在将来其他的动作也可以拜访他们.如果我们希望了解更多的关于属性的内容,我们可以查看Symfony一书的参数保存器一节.PHP实战 分配权限PHP实战 用户可以登陆进askeet网站是一件好事,但是用户并不仅是因为好玩而登陆.颁发一个新问题,对某一个问题表示兴趣,评价一个评论都需要登陆.而其他的动作将会向非登陆用户开放.PHP实战 要将一个用户设置为经过验证的,我们需要调用sfUser对象的->setAuthenticated()方法.这个对象同时提供了一个证书机制(->addCredential()),来通过配置限制拜访.Symfony一书的用户证书一节对此进行了详细的解释.PHP实战 这就是下面两行的目的:
代码如下:
$this->getContext()->getUser()->setAuthenticated(true);
$this->getContext()->getUser()->addCredential('subscriber'); 当nickname被识别后,不仅用户数据被存放在会话属性中,而且这个用户也会被分配网站限制部分的拜访权限.在明天我们将会看到如何限制验证用户的程序拜访.PHP实战 添加user/logout动作PHP实战 关于->setAttribute()办法还有最后一个窍门:最后一个参数(上面例子中的subscriber)定义了属性存放的名字空间.一个名字空间不仅允许一个在另一个名字空间存在的名字指定给一个属性,而且可以使用一个命令快速移除所有这些属性:
代码如下:
public function executeLogout()
{ ? $this->getUser()->setAuthenticated(false); ? $this->getUser()->clearCredentials(); ? ? $this->getUser()->getAttributeHolder()->removeNamespace('subscriber'); ? ? $this->redirect('@homepage'); } 使用名字空间可以省去我们一个一个移除这些属性的麻烦:这只是一行语句.PHP实战 更新布局PHP实战 当前这个布局即使用户已经登陆仍然显示一个'login'链接.让我们来修正这一点.在askeet/apps/frontend/templates/layout.php文件中,修改我们在今天的指南开始时所修改的代码:
代码如下:
<?php if ($sf_user->isAuthenticated()): ?>
? <li><?php echo link_to('sign out','user/logout') ?></li> ? <li><?php echo link_to($sf_user->getAttribute('nickname','','subscriber').' profile','user/profile') ?></li> <?php else: ?> ? <li><?php echo link_to('sign in/register','user/login') ?></li> <?php endif ?> 现在是时候进行测试了,我们可以显示程序的任何一页,点击'login'链接,输入一个可用的昵称('anonymous'为例)并且进行验证.如果窗口顶部的'login'变为'sign out',则我们所做的一切都是正确的.最后,试着注销来查看'login'链接是否再次出现.PHP实战 问题组织PHP实战 随着数以千计的Symfony爱好者访问askeet网站,在主页上显示的问题就会逐渐变多.为了避免变慢的哀求速度,问题列的随意翻阅就成为必须解决的问题.PHP实战 Symfony为这一目的提供了一个对象:sfPropelPager.他会封装到数据的哀求,从而只会查询当前页面所显示的记录.例如,如果一个页面初始化时每页只显示10个问题,则到数据的哀求只会限制为10个结果,并且会设置偏移来在页面中进行匹配.PHP实战 修改question/list动作PHP实战 在前面的练习中,我们看到了问题模块的显示动作:
代码如下:
public function executeList ()
{ ? $this->questions = QuestionPeer::doSelect(new Criteria()); } 我们将会修改这个动作来向模板传递一个sfPropelPager而不是传递一个数组.同时,我们会依据感兴趣的数量来对问题进行排序:
代码如下:
public function executeList ()
{ ? $pager = new sfPropelPager('Question',2); ? $c = new Criteria(); ? $c->addDescendingOrderByColumn(QuestionPeer::INTERESTED_USERS); ? $pager->setCriteria($c); ? $pager->setPage($this->getRequestParameter('page',1)); ? $pager->setPeerMethod('doSelectJoinUser'); ? $pager->init(); ? ? $this->question_pager = $pager; } sfPropelPager对象的初始化指明了他包含哪个对象类,以及在一个页面中可以放置的对象的最大数目(在这个例子中为2).->setPage()方法使用一个哀求参数来设置当前页面.例如,如果这个页面参数的值为2,sfPropelPager将会返回3到5的结果.页面哀求参数的值变为1,则页面默认会返回1到2的结果.我们可以在Symfony一书的页面章节中了解到关于sfPropelPager对象及其方法的更多信息.PHP实战 使用一个默认参数PHP实战 将常量放在我们所使用的配置文件中是一个好主意.例如,每页的结果(在这个例子为2)可以由一个在我们自定义的程序配置中的参数来代替.用下面的代码来改变上面的sfPropelPager行:
代码如下:
..
? $pager = new sfPropelPager('Question',sfConfig::get('app_pager_homepage_max')); 这里的pager关键字是作为名字空间使用的,这也就是为什么在参数名字中出现的原因.我们可以在Symfony一书的配置一节中查看到更多的关于自定义配置与命名自定义参数规则的更多的内容.PHP实战 修改listSuccess.php模板PHP实战 在listSuccess.php模板中,将下面的代码行:
代码如下:
<?php foreach($questions as $question): ?>
替换为
代码如下:
<?php foreach($question_pager->getResults() as $question): ?>
从而页面显示存储在页面中的结果列表.PHP实战 添加页面浏览PHP实战 在这个模板中还需要做另外一件事:页面浏览.现在,模板所做的只是显示前两个问题,但是我们应添加到下一个页面的功能,以及回到前一个页面的功能.要完成添加这些功能,我们需要在模板后面添加下面的代码:
代码如下:
<div id="question_pager">
<?php if ($question_pager->haveToPaginate()): ?> ? <?php echo link_to('?','question/list?page=1') ?> ? <?php echo link_to('<','question/list?page='.$question_pager->getPreviousPage()) ?> ? ? <?php foreach ($question_pager->getLinks() as $page): ?> ??? <?php echo link_to_unless($page == $question_pager->getPage(),$page,'question/list?page='.$page) ?> ??? <?php echo ($page != $question_pager->getCurrentMaxLink()) ? '-' : '' ?> ? <?php endforeach; ?> ? ? <?php echo link_to('>','question/list?page='.$question_pager->getNextPage()) ?> ? <?php echo link_to('?','question/list?page='.$question_pager->getLastPage()) ?> <?php endif; ?> </div> 这段代码利用了sfPropelPager对象的各种方法,以及->haveToPaginate(),这个函数只有在哀求的结果数目超过了页面尺寸时才会返回真;而->getPreviousPage(),->getNextPage(),->getLastPage()都具有明显示的意义;->getLinks()函数提供了一个页面号的数组;而->getCurrentMaxLink()函数返回最后的页面号.PHP实战 这个例子同时显示了一个Symfony链接赞助器:link_to_unless()会在作为第一个参数的测试为假的情况下输出一个常规link_to(),否则会输出一个非链接的文本,并使用简单的<span>包装.PHP实战 我们测试这个页面了吗?我们应进行测试.直到我们用我们自己的眼睛来验证,这个修改才算结束.要进行测试,打开在第三天所创建的测试数据文件,并且为要显示的页面浏览添加一些问题.重新运行导入数据批处理文件,然后再一次哀求主页.PHP实战 为子页添加路由规则PHP实战 默认情况下,页面规则如下:PHP实战 http://askeet/frontend_dev.php/question/list/page/XXPHP实战 现在我们利用路由规则使用这些页面更易于理解:PHP实战 http://askeet/frontend_dev.php/index/XXPHP实战 打开apps/frontend/config/routing.yml文件并且在顶部添加下面内容:
代码如下:
popular_questions:
? url:?? /index/:page ? param: { module: question,action: list } 并且为登陆页面添加另外的路由规则:
代码如下:
login:
? url:?? /login ? param: { module: user,action: login } 重构PHP实战 模型PHP实战 question/list动作执行与模型相关的代码,这也就是我们为什么要将这些代码移动到模块中的原因.用下面的代码来代替question/list动作:
代码如下:
public function executeList ()
{ ? $this->question_pager = QuestionPeer::getHomepagePager($this->getRequestParameter('page',1)); } 并且在lib/model中的QuestionPeer.php类中添加下面的办法:
代码如下:
public static function getHomepagePager($page)
{ ? $pager = new sfPropelPager('Question',sfConfig::get('app_pager_homepage_max')); ? $c = new Criteria(); ? $c->addDescendingOrderByColumn(self::INTERESTED_USERS); ? $pager->setCriteria($c); ? $pager->setPage($page); ? $pager->setPeerMethod('doSelectJoinUser'); ? $pager->init(); ? ? return $pager; } 同样的想法也适用于我们昨天编写的question/show动作:Propel对象由其剥离的标题取回问题的用法应属于这个模块.所以用下面的代码来变更question/show动作代码:
代码如下:
public function executeShow()
{ ? $this->question = QuestionPeer::getQuestionFromTitle($this->getRequestParameter('stripped_title')); ? ? $this->forward404Unless($this->question); } 在QuestionPeer.php文件中添加下面的代码:
代码如下:
public static function getQuestionFromTitle($title)
{ ? $c = new Criteria(); ? $c->add(QuestionPeer::STRIPPED_TITLE,$title); ? ? return self::doSelectOne($c); } 模板PHP实战 在question/templates/listSuccess.php中显示的问题列表在将来的某些地方还会用到.所以我们将显示问题列表的模板代码放在一个_list.php片段中,并且用下面的简单代码来代替listSuccess.php的内容:
代码如下:
<h1>popular question</h1>
<?php echo include_partial('list',array('question_pager'=>$question_pager)) ?>PHP实战 希望本文所述对大家的symfony框架程序设计有所赞助.PHP实战 《PHP实例:symfony表单与页面实现技巧》是否对您有启发,欢迎查看更多与《PHP实例:symfony表单与页面实现技巧》相关教程,学精学透。编程之家 52php.cn为您提供精彩教程。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |