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