symfony表单与页面实现技巧

本文实例讲述了symfony表单与页面实现技巧。分享给大家供大家参考。具体如下:

symfony开发很简洁,但是功能的数量仍然很缺乏。现在是时候进行一些askeet站点与用户之间的交互了。而HTML交互的根本--除了起链接--就是表单了。

这里我们的目标是允许用户登陆,并在主页的问题列表中进行翻阅。这对于开发而言是很快的,并且可以让我们回忆起前面的内容。

登陆表单

在测试数据中存在用户,但是程序却没有办法来进行验证。下面我们要在程序的每一个页面添加一个登陆表单。打开全局的布局文件askeet/apps/frontend/templates/layout.php,并且在到about的连接之前添加下面的代码行:

代码如下:

<li><?php echo link_to('sign in', 'user/login') ?></li>

当前的布局将这些链接放在web调试工具栏之后。要看到这些链接,点击'Sf'图标折叠起调试工具栏就可以看到了。

现在需要创建user模块。而question模块是在第二天生成的,这一次我们只是叫symfony来创建模块框架,而我们将会自己来编写这些代码。

代码如下:

$ symfony init-module frontend user

这个框架包含一个默认的index动作与一个indexSuccess.php模板。删除他们,因为我们并不需要他们。

创建user/login动作


代码如下:

在user/actions/action.class.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模块:

代码如下:

<?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代码的内容。

这里的实质是当表单提交时则会调用这个动作。所以我们返回来看一下这个动作。

处理表单提交

用下面的代码来替换我们刚才所编写的登陆动作:

代码如下:

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模式下请求的,那么则会由表单调用这个动作并进行相应的处理。

这个动作会由请求参数得到nickname域的值,并且查询User表来查看在数据库是否存在此用户。

将来一个密码控制将会为用户分配凭证。但是现在,这个动作所做的只是在一个会话属性中存储用户的id与nickname属性。最后,这个动作重定向到表单中隐藏中的原始referer域,这是作为一个请求参数传递的。如果这个域是空的,则会使用默认值。

这里我们需要注意这个例子中两种类型的属性集合之间的区别:request attributes($this->getRequest()->setAttribute())是为模板所保存的,而且只要答案发送到referer则会被忘记。session attributes($this->getUser()->setAttribute())是在整个用户会话生命期被保存的,而且在将来其他的动作也可以访问他们。如果我们希望了解更多的关于属性的内容,我们可以查看Symfony一书的参数保存器一节。

分配权限

用户可以登陆进askeet网站是一件好事,但是用户并不仅是因为好玩而登陆。发表一个新问题,对某一个问题表示兴趣,评价一个评论都需要登陆。而其他的动作将会向非登陆用户开放。

要将一个用户设置为经过验证的,我们需要调用sfUser对象的->setAuthenticated()方法。这个对象同时提供了一个证书机制(->addCredential()),来通过配置限制访问。Symfony一书的用户证书一节对此进行了详细的解释。

这就是下面两行的目的:

代码如下:

$this->getContext()->getUser()->setAuthenticated(true);
$this->getContext()->getUser()->addCredential('subscriber');

当nickname被识别后,不仅用户数据被存放在会话属性中,而且这个用户也会被分配网站限制部分的访问权限。在明天我们将会看到如何限制验证用户的程序访问。

添加user/logout动作

关于->setAttribute()方法还有最后一个窍门:最后一个参数(上面例子中的subscriber)定义了属性存放的名字空间。一个名字空间不仅允许一个在另一个名字空间存在的名字指定给一个属性,而且可以使用一个命令快速移除所有这些属性:

代码如下:

public function executeLogout()
{
  $this->getUser()->setAuthenticated(false);
  $this->getUser()->clearCredentials();
 
  $this->getUser()->getAttributeHolder()->removeNamespace('subscriber');
 
  $this->redirect('@homepage');
}

使用名字空间可以省去我们一个一个移除这些属性的麻烦:这只是一行语句。

更新布局

当前这个布局即使用户已经登陆仍然显示一个'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'链接是否再次出现。

问题组织

随着数以千计的Symfony爱好者访问askeet网站,在主页上显示的问题就会逐渐变多。为了避免变慢的请求速度,问题列的随意翻阅就成为必须解决的问题。

Symfony为这一目的提供了一个对象:sfPropelPager。他会封装到数据的请求,从而只会查询当前页面所显示的记录。例如,如果一个页面初始化时每页只显示10个问题,则到数据的请求只会限制为10个结果,并且会设置偏移来在页面中进行匹配。

修改question/list动作

在前面的练习中,我们看到了问题模块的显示动作:

代码如下:

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对象及其方法的更多信息。

使用一个默认参数

将常量放在我们所使用的配置文件中是一个好主意。例如,每页的结果(在这个例子为2)可以由一个在我们自定义的程序配置中的参数来代替。用下面的代码来改变上面的sfPropelPager行:

代码如下:

..
  $pager = new sfPropelPager('Question', sfConfig::get('app_pager_homepage_max'));

这里的pager关键字是作为名字空间使用的,这也就是为什么在参数名字中出现的原因。我们可以在Symfony一书的配置一节中查看到更多的关于自定义配置与命名自定义参数规则的更多的内容。

修改listSuccess.php模板

在listSuccess.php模板中,将下面的代码行:

代码如下:

<?php foreach($questions as $question): ?>

替换为

代码如下:

<?php foreach($question_pager->getResults() as $question): ?>

从而页面显示存储在页面中的结果列表。

添加页面浏览

在这个模板中还需要做另外一件事:页面浏览。现在,模板所做的只是显示前两个问题,但是我们应添加到下一个页面的功能,以及回到前一个页面的功能。要完成添加这些功能,我们需要在模板后面添加下面的代码:

代码如下:

<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()函数返回最后的页面号。

这个例子同时显示了一个Symfony链接帮助器:link_to_unless()会在作为第一个参数的测试为假的情况下输出一个常规link_to(),否则会输出一个非链接的文本,并使用简单的<span>包装。

我们测试这个页面了吗?我们应进行测试。直到我们用我们自己的眼睛来验证,这个修改才算结束。要进行测试,打开在第三天所创建的测试数据文件,并且为要显示的页面浏览添加一些问题。重新运行导入数据批处理文件,然后再一次请求主页。

为子页添加路由规则

默认情况下,页面规则如下:

http://askeet/frontend_dev.php/question/list/page/XX

现在我们利用路由规则使用这些页面更易于理解:

http://askeet/frontend_dev.php/index/XX

打开apps/frontend/config/routing.yml文件并且在顶部添加下面内容:

代码如下:

popular_questions:
  url:   /index/:page
  param: { module: question, action: list }

并且为登陆页面添加另外的路由规则:

代码如下:

login:
  url:   /login
  param: { module: user, action: login }

重构

模型

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);
}

模板

在question/templates/listSuccess.php中显示的问题列表在将来的某些地方还会用到。所以我们将显示问题列表的模板代码放在一个_list.php片段中,并且用下面的简单代码来代替listSuccess.php的内容:

代码如下:

<h1>popular question</h1>

<?php echo include_partial('list',array('question_pager'=>$question_pager)) ?>

希望本文所述对大家的symfony框架程序设计有所帮助。

(0)

相关推荐

  • Symfony2框架学习笔记之表单用法详解

    本文实例讲述了Symfony2框架表单用法.分享给大家供大家参考,具体如下: 对于一个Web开发者来说,处理HTML表单是一个最为普通又具挑战的任务.Symfony2集成了一个Form组件,让处理表单变的容易起来.在这一节里,我们将 从基础开始创建一个复杂的表单,学习表单类库中最重要的内容. Symfony2 的Form组件是一个独立的类库,你可以在Symfony2项目之外使用它. 创建一个简单的表单: 假设你要创建一个应用程序的todo列表,需要显示一些任务.因为你的用户需要编辑和创建任务,所

  • 高性能PHP框架Symfony2经典入门教程

    Symfony2是一个基于PHP语言的Web开发框架,有着开发速度快.性能高等特点.本文以一个程序示例的实现过程详细叙述了Symfony2框架的配置与程序开发. 一.下载 首先是下载Symfony2,到 http://symfony.com/download或者本站下载http://www.jb51.net/codes/187833.html.本人以Ubuntu系统为例,采用.tgz的压缩包,解压源文件到/var/www目录中并执行: tar zxvf Symfony_Standard_Vend

  • Symfony查询方法实例小结

    本文实例讲述了Symfony查询方法.分享给大家供大家参考,具体如下: 1. createQuery的写法 $sql = 'SELECT COUNT(DISTINCT(g.goodsId)) FROM AppBundle:GoodsIndex g WHERE g.status = :status'; $params = array( 'status' => GoodsIndex::STATUS_NORMAL, ); if (!empty($keywords)) { $params['keywor

  • Symfony2安装的方法(2种方法)

    本文详细讲述了Symfony2安装的方法.分享给大家供大家参考,具体如下: 一.Composer安装Symfony Composer安装Symfony方法在前面的文章<Symfony之十分钟入门>有介绍 二.Symfony Installer安装Symfony 这里来着重介绍第二种Symfony Installer安装Symfony的方法 1.安装Symfony Installer 控制台执行命令 [Sun@localhost html]$ curl -LsS http://symfony.c

  • Symfony2联合查询实现方法

    本文实例讲述了Symfony2联合查询实现方法.分享给大家供大家参考,具体如下: 1.yml文件 Acme\MspadminBundle\Entity\MspArticle: type: entity table: msp_article manyToOne: Channel: targetEntity: MspChannel inversedBy: Articles joinColumn: name: channel_id referencedColumnName: channel_id Us

  • Symfony学习十分钟入门经典教程

    Symfony是一个强大的基于PHP的Web开发框架,在这里我们用十分钟的时间来做一个简单的增删改查的程序, 任何不熟悉Symfony的人都可以通过这个教程完成自己的第一个Symfony程序. 如果需要这个样例程序的全部源代码,可以访问 这里 ,或者通过下面的方式获取源代码: $git clone https://github.com/saharabear/symfony-sample.git 项目初始化 首先,需要你在自己的电脑中安装PHP环境并安装git.这方面的内容属于基础内容,网络上有大

  • Symfony数据校验方法实例分析

    本文实例讲述了Symfony数据校验方法.分享给大家供大家参考.具体分析如下: 校验在web应用程序中是一个常见的任务.数据输入到表单需要被校验.数据在被写入数据库之前或者传入一个webservice时也需要被校验. Symfony2 配备了一个Validator 组件,它让校验工作变得简单易懂.该组件是基于JSR303 Bean校验规范.一个Java规范用在PHP中. 基本验证 理解校验的最好方法是看它的表现.首先,假设你已经创建了一个用于你应用程序某个地方的PHP对象. 复制代码 代码如下:

  • Symfony2使用Doctrine进行数据库查询方法实例总结

    本文实例讲述了Symfony2使用Doctrine进行数据库查询方法.分享给大家供大家参考,具体如下: 预定义文中用到的变量: $em = $this->getDoctrine()->getEntityManager(); $repository = $em->getRepository('AcmeStoreBundle:Product') 1.基本方法 $repository->find($id); $repository->findAll(); $repository-&

  • Symfony2学习笔记之模板用法详解

    本文实例讲述了Symfony2学习笔记之模板用法.分享给大家供大家参考,具体如下: 我们知道,controller负责处理每一个进入Symfony2应用程序的请求.实际上,controller把大部分的繁重工作都委托给了其它地方,以使代码能够被测试和重用.当一个controller需要生成HTML,CSS或者其他内容时,它把这些工作给了一个模板化引擎. 模板: 一个模板仅仅是一个文本文件,它能生成任意的文本格式(HTML,XML,CSV,LaTex...).最著名的模板类型就是PHP模板了,可以

  • Symfony2创建页面实例详解

    本文实例讲述了Symfony2创建页面的方法.分享给大家供大家参考,具体如下: 在Symfony2中创建页面只需要两步: 1.创建路由:路由定义你页面的URI(如/about)并指定要执行的控制器(PHP函数).当传入的请求URL匹配该路由时,Symfony2将执行指定的控制器: 2.创建控制器:控制器是一个PHP函数,它接受传入的请求并将其转换成Symfony2的Response对象. 我们喜欢这样简单的实现,因为它符合Web的工作方式.每一个Web交互都是由HTTP请求开始,应用程序的任务就

  • Symfony页面的基本创建实例详解

    本文实例分析了Symfony页面的基本创建方法.分享给大家供大家参考.具体如下: 这里我们将会学习如何创建一个模块,这是组织页面的结构化元素.同时我们也会学习如何创创建一个分为一个动作也一个模板的页面,之所以分为动作与模板,是因为MVC模式.链接与表彰是基本的页面交互,我们将会学习如何在模板中插入这些元素并且在动作中进行处理. 创建一个模块框架 Symfony将页面组织为模块.在创建一个页面之前,我们需要创建一个模块,并且初始化为一个Symfony可以识别的文件结构的空壳. Symfony命令行

  • Symfony2实现从数据库获取数据的方法小结

    本文实例讲述了Symfony2实现从数据库获取数据的方法.分享给大家供大家参考,具体如下: 假设有一张表:test, 字段:name,color; 有2条记录: Tom blue Lily red 示例1: $conn = $this->getDoctrine()->getConnection(); $data = $conn->fetchcolumn("SELECT name, color FROM test"); echo '<pre>'; print

随机推荐