symfony11:表单

写模板的时候,开发者大多的时间致力于表单,尽管如此,表单通常难以设计。因为要关心很多的比如:默认值、格式、验证、重新获取以及表单获取等等,一些开发者使者去略过一些重要的处理细节。Symfony可以自动化许多以上需要并能加速表单开发:

l         表单助手提供了在模板中快速写输入表单的方法,特别是对于复杂元素:时间、下拉列别、富文本框等

l         当表单是是编辑对象的属性时,模板可以使用对象表单助手加速。

l         YAML验证文件可以简化表单验证与重新获取

验证器对验证输入的代码打包, symfony 对最常用的验证器进行绑定,并且很容易的加入自定义验证器。

表单助手

模板中,表单元素的HTML标签经常混合PHP代码。Symfony中的表单助手能帮着简化这种任务。

主表单标签

前面已经说过,必须使用form_tag()助手创建表单,因为他转换用户URL到路由RUL

<?php echo form_tag('test/save') ?>

 => <form method="post" action="/path/to/save">

 

<?php echo form_tag('test/save', 'method=get multipart=true class=simpleForm') ?>

 => <form method="get" enctype="multipart/form-data" class="simpleForm"action="/path/to/save">

因为form_tag没有</form>标签,你最好在HTML代码中加上,虽然这看上去很不好。

标准表单元素

// 文本框 (input)

<?php echo input_tag('name', 'default value') ?>

 => <input type="text" name="name" id="name" value="default value" />

 

// 所有的表单助手接受额外的参数

// 允许你加入自定义属性

<?php echo input_tag('name', 'default value', 'maxlength=20') ?>

 => <input type="text" name="name" id="name" value="default value"maxlength="20" />

 

// (text area)

<?php echo textarea_tag('name', 'default content', 'size=10x20') ?>

 => <textarea name="name" id="name" cols="10" rows="20">

      default content

    </textarea>

 

// Check box

<?php echo checkbox_tag('single', 1, true) ?>

<?php echo checkbox_tag('driverslicense', 'B', false) ?>

 => <input type="checkbox" name="single" id="single" value="1"checked="checked" />

    <input type="checkbox" name="driverslicense" id="driverslicense"value="B" />

 

// Radio button

<?php echo radiobutton_tag('status[]', 'value1', true) ?>

<?php echo radiobutton_tag('status[]', 'value2', false) ?>

 => <input type="radio" name="status[]" id="status_value1" value="value1"checked="checked" />

    <input type="radio" name="status[]" id="status_value2" value="value2" />

 

// Dropdown list (select)

<?php echo select_tag('payment',

  '<option selected="selected">Visa</option>

   <option>Eurocard</option>

   <option>Mastercard</option>')

?>

 => <select name="payment" id="payment">

      <option selected="selected">Visa</option>

      <option>Eurocard</option>

      <option>Mastercard</option>

    </select>

 

// List of options for a select tag

<?php echo options_for_select(array('Visa', 'Eurocard', 'Mastercard'), 0) ?>

 => <option value="0" selected="selected">Visa</option>

    <option value="1">Eurocard</option>

    <option value="2">Mastercard</option>

 

// Dropdown helper combined with a list of options

<?php echo select_tag('payment', options_for_select(array(

  'Visa',

  'Eurocard',

  'Mastercard'

), 0)) ?>

 => <select name="payment" id="payment">

      <option value="0" selected="selected">Visa</option>

      <option value="1">Eurocard</option>

      <option value="2">Mastercard</option>

    </select>

 

// To specify option names, use an associative array

<?php echo select_tag('name', options_for_select(array(

  'Steve'  => 'Steve',

  'Bob'    => 'Bob',

  'Albert' => 'Albert',

  'Ian'    => 'Ian',

  'Buck'   => 'Buck'

), 'Ian')) ?>

 => <select name="name" id="name">

      <option value="Steve">Steve</option>

      <option value="Bob">Bob</option>

      <option value="Albert">Albert</option>

      <option value="Ian" selected="selected">Ian</option>

      <option value="Buck">Buck</option>

    </select>

 

// Dropdown list with multiple selection (selected values can be an array)

<?php echo select_tag('payment', options_for_select(

  array('Visa' => 'Visa', 'Eurocard' => 'Eurocard', 'Mastercard' => 'Mastercard'),

  array('Visa', 'Mastecard'),

), array('multiple' => true))) ?>

 

 => <select name="payment[]" id="payment" multiple="multiple">

      <option value="Visa" selected="selected">Visa</option>

      <option value="Eurocard">Eurocard</option>

      <option value="Mastercard">Mastercard</option>

    </select>

// Drop-down list with multiple selection (selected values can be an array)

<?php echo select_tag('payment', options_for_select(

  array('Visa' => 'Visa', 'Eurocard' => 'Eurocard', 'Mastercard' => 'Mastercard'),

  array('Visa', 'Mastecard')

), 'multiple=multiple') ?>

 => <select name="payment" id="payment" multiple="multiple">

      <option value="Visa" selected="selected">

      <option value="Eurocard">Eurocard</option>

      <option value="Mastercard" selected="selected">Mastercard</option>

    </select>

 

// Upload file field

<?php echo input_file_tag('name') ?>

 => <input type="file" name="name" id="name" value="" />

 

// Password field

<?php echo input_password_tag('name', 'value') ?>

 => <input type="password" name="name" id="name" value="value" />

 

// Hidden field

<?php echo input_hidden_tag('name', 'value') ?>

 => <input type="hidden" name="name" id="name" value="value" />

 

// Submit button (as text)

<?php echo submit_tag('Save') ?>

 => <input type="submit" name="submit" value="Save" />

 

// Submit button (as image)

<?php echo submit_image_tag('submit_img') ?>

 => <input type="image" name="submit" src="/images/submit_img.png" />

 

处理表单提交

动作需要使用$this->getRequestParameter($elem)获取提交表单的值。
        
        
一个好的方式是使用同一个动作来显示与处理表单,通过请求方法,或者表单被调用或者被处理并且请求被转向其他动作。
        
        
// mymodule/actions/actions.class.php
             
             
public function executeEditAuthor()
             
             
{
             
             
  if ($this->getRequest()->getMethod() != sfRequest::POST)
             
             
  {
             
             
    // Display the form
             
             
    return sfView::SUCCESS;
             
             
  }
             
             
  else
             
             
  {
             
             
    // Handle the form submission
             
             
    $name = $this->getRequestParameter('name');
             
             
    ...
             
             
    $this->redirect('mymodule/anotheraction');
             
             
  }
             
             
}
             
             
要能工作,表单目的地必须与显示的一致
        
        
//mymodule/templates/editAuthorSuccess.php
             
             
<?php echo form_tag('mymodule/editAuthor') ?>
             
             

        
        
时间输入
表单经常被用来获取时间,时间格式错误是表单提交错误中主要的部分。 Input_date_tag 助手能够提供一个交互的 JS 日历,如果设置 rich 属性为 true 则显示如下图:

如果没有设置rich属性或者设置为false,则显示三个<select>标签分别选择年、月和日。也可以使用select_day_tag(), select_month_tag(), and select_year_tag()来实现。时间输入的默认值是当前时间,可以以第二个参数传递过去:
  
  
<?php echo input_date_tag('dateofbirth', '
        
        
         
         2005-05-03
        
        ', 'rich=true') ?>
        
        
 =>上图格式,只是默认时间不是
        
        

        
        
         
          
        
        
// 下面的助手需要DateForm助手组
        
        
<?php use_helper('DateForm') ?>
        
        
 
        
        
<?php echo select_day_tag('day', 1, 'include_custom=') ?>
        
        
=> <select name="day" id="day">
        
        
      <option value=""></option>
        
        
      <option value="1" selected="selected">01</option>
        
        
      <option value="2">02</option>
        
        
      ...
        
        
      <option value="31">31</option>
        
        
    </select>
        
        
 
        
        
<?php echo select_month_tag('month', 1, 'include_custom= use_short_month=true') ?>
        
        
=> <select name="month" id="month">
        
        
      <option value=""></option>
        
        
      <option value="1" selected="selected">Jan</option>
        
        
      <option value="2">Feb</option>
        
        
      ...
        
        
      <option value="12">Dec</option>
        
        
    </select>
        
        
 
        
        
<?php echo select_year_tag('year', 2007, 'include_custom= year_end=2010') ?>
        
        
 => <select name="year" id="year">
        
        
      <option value=""></option>
        
        
      <option value="2006">2006</option>
        
        
      <option value="2007" selected="selected">2007</option>
        
        
      ...
        
        
    </select>
        
        

Input_date_tag()助手可接受的时间值有PHPstrtotime()方法决定:

// 合法格式
        
        
<?php echo input_date_tag('test', '
        
        
         
         2006-04-01
        
        ', 'rich=true') ?>
        
        
<?php echo input_date_tag('test', 1143884373, 'rich=true') ?>
        
        
<?php echo input_date_tag('test', 'now', 'rich=true') ?>
        
        
<?php echo input_date_tag('test', '23 October 2005', 'rich=true') ?>
        
        
<?php echo input_date_tag('test', 'next tuesday', 'rich=true') ?>
        
        
<?php echo input_date_tag('test', '1 week 2 days 4 hours 2 seconds', 'rich=true') ?>
        
        
// 返回空
        
        
<?php echo input_date_tag('test', null, 'rich=true') ?>
        
        
<?php echo input_date_tag('test', '', 'rich=true') ?> 
        
        

        
        
         
          
        
        
//  = 
        
        
         
         01/01/1970
        
        
        
        
<?php echo input_date_tag('test', 0, 'rich=true') ?>
        
        
 
        
        
// 非英语格式不能工作
        
        
<?php echo input_date_tag('test', '
        
        
         
         01/04/2006
        
        ', 'rich=true') ?>
        
        
文本编辑器

需要手动安装,具体参阅http://tinymce.moxiecode.com。下载文件而后加压缩至web/js目录,然后修改settings.yml文件以激活编辑器

all:
        
        
  .settings:
        
        
    rich_text_js_dir:  js/tiny_mce
        
        
然后在模板中的文本区表单中加入rich=true即可。
  
  
国家与语言选择器
国家标签助手
        
        

        
        
         
          
        
        
<?php echo select_country_tag('country', '
        
        
         
         
          
          AL
         
         
        
        ') ?>
        
        
 => <select name="country" id="country">
        
        
      <option value="AF">
        
        
         
         
          
          Afghanistan
         
         
        
        </option>
        
        
      <option value="
        
        
         
         AL
        
        " selected="selected">
        
        
         
         
          
          Albania
         
         
        
        </option>
        
        
      <option value="DZ">
        
        
         
         
          
          Algeria
         
         
        
        </option>
        
        
      <option value="AS">
        
        
         
         
          
          American Samoa
         
         
        
        </option>
        
        
语言标签助手
        
        

        
        
         
          
        
        
<?php echo select_language_tag('language', 'en') ?>
        
        
 => <select name="language" id="language">
        
        
      ...
        
        
      <option value="elx">Elamite</option>
        
        
      <option value="en" selected="selected">English</option>
        
        
      <option value="enm">English, Middle (1100-1500)</option>
        
        
      <option value="ang">English, Old (ca.450-1100)</option>
        
        
      <option value="myv">Erzya</option>
        
        
      <option value="eo">Esperanto</option>
        
        
……
        
        

对象表单助手

当表单编辑对象属性是,标准助手将变得很麻烦,例如,编译客户对象的电话属性:

<?php echo input_tag('telephone', $customer->getTelephone()) ?>
        
        
=> <input type="text" name="telephone" id="telephone" value="0123456789" />
        
        

Symfony提供了对象表单助手来简化此类操作:

<?php echo object_input_tag($customer, 'getTelephone') ?>
        
        
=> <input type="text" name="telephone" id="telephone" value="0123456789" />
        
        

对象表单助手在使用前必须应用user_helper(“Object”),供提供了下列方法:

<?php echo object_input_tag($object, $method, $options) ?>
        
        
<?php echo object_input_date_tag($object, $method, $options) ?>
        
        
<?php echo object_input_hidden_tag($object, $method, $options) ?>
        
        
<?php echo object_textarea_tag($object, $method, $options) ?>
        
        
<?php echo object_checkbox_tag($object, $method, $options) ?>
        
        
<?php echo object_select_tag($object, $method, $options) ?>
        
        
<?php echo object_select_country_tag($object, $method, $options) ?>
        
        
<?php echo object_select_language_tag($object, $method, $options) ?>
        
        

注意,没有object_password_tag()助手,因为他不需要获取对象的属性值并显示出来。

使用对象生成下拉列表
//使用options_for_select()创建选项列表
        
        
<?php echo options_for_select(array(
        
        
  '1' => 'Steve',
        
        
  '2' => 'Bob',
        
        
  '3' => 'Albert',
        
        
  '4' => 'Ian',
        
        
  '5' => 'Buck'
        
        
), 4) ?>
        
        
 => <option value="1">Steve</option>
        
        
    <option value="2">Bob</option>
        
        
    <option value="3">Albert</option>
        
        
    <option value="4" selected="selected">Ian</option>
        
        
    <option value="5">Buck</option>
        
        

假定你已经有一个从Propel查询集得到一个Author类对象的数组,要建议基于这个数组的选项列表,你需要循环获取每个对象的id和名称

动作中
        
        
$options = array();
        
        
foreach ($authors as $author)
        
        
{
        
        
  $options[$author->getId()] = $author->getName();
        
        
}
        
        
$this->options = $options;
        
        
模板中
        
        
<?php echo options_for_select($options, 4) ?>
        
        

这种情况下,symfony提供了一个助手objects_for_select()来自动完成,objects_for_select()助手直接使用对象数组,他需要2个额外的参数:方法名称和<option>标签的名字:

直接在模板中获取
        
        
<?php echo objects_for_select($authors, 'getId', 'getName', 4) ?>
        
        

这很智能且快速,但symfony还能做的更好,特别是有外键关联的列上。

基于外键列创建下拉列表

外键值是外键表的主键值,例如,文章表的author_idauthor表的外键,这个列可能的值都在author表的id中出现,使用下面的代码创建选项列表:

<?php echo select_tag('author_id', objects_for_select(AuthorPeer::doSelect(new Criteria()),' getId', '__toString', $article->getAuthorId())) ?>
        
        

        
        
         
          
        
        
=> <select name="author_id" id="author_id">
        
        
      <option value="1">Steve</option>
        
        
      <option value="2">Bob</option>
        
        
      <option value="3">Albert</option>
        
        
      <option value="4" selected="selected">Ian</option>
        
        
      <option value="5">Buck</option>
        
        
    </select>
        
        

Object_select_tag助手可以自动完成上面的部分,他会从schema中获取部分信息。

<?php echo object_select_tag($article, 'getAuthorId') ?>
        
        

Object_select_tag助手根据传递过来的方法名猜测相关的peer类名(根据getAuthorID方法得到AuthorPeer类),你也可以通过第三个参数制定你自己的类作为关联类。<option>的内容文本是对象类的__toString()方法的结果(如果$author->__toString方法不存在则使用主键替代)。另外可以通过Criteria来限定获取的条数。最后可以加入空选项或自定义选项显示在下拉列表的顶部,看下面的例子:

// 基本语法
        
        
<?php echo object_select_tag($article, 'getAuthorId') ?>
        
        
// 通过方法AuthorPeer::doSelect(new Criteria())
        
        
 
        
        
// 修改peer
        
        
<?php echo object_select_tag($article, 'getAuthorId', 'related_class=Foobar') ?>
        
        
// 通过方法FoobarPeer::doSelect(new Criteria())
        
        
 
        
        
// 修改Peer方法
        
        
<?php echo object_select_tag($article, 'getAuthorId','peer_method=getMostFamousAuthors') ?>
        
        
// 通过方法AuthorPeer::getMostFamousAuthors(new Criteria())
        
        
 
        
        
// 在列表顶部添加 <option value="">&nbsp;</option> 
        
        
<?php echo object_select_tag($article, 'getAuthorId', 'include_blank=true') ?>
        
        
 
        
        
//在列表顶部添加<option value="">Choose an author</option> 
        
        
<?php echo object_select_tag($article, 'getAuthorId', 'include_custom=Choose an author') ?>
        
        
更新对象

在动作中,很容易地去处理使用对象助手编辑对象属性的表单提交,比如,有一个包含nameageaddress属性的Author类对象,表单显示代码可能为:

<?php echo form_tag('author/update') ?>
        
        
  <?php echo object_input_hidden_tag($author, 'getId') ?>
        
        
  Name: <?php echo object_input_tag($author, 'getName') ?><br />
        
        
  Age:  <?php echo object_input_tag($author, 'getAge') ?><br />
        
        
  Address: <br />
        
        
         <?php echo object_textarea_tag($author, 'getAddress') ?>
        
        
</form>
        
        

表单提交时会调用update动作,调用ProelfromArray()方法可以很容易的更新对象。

public function executeUpdate ()
        
        
{
        
        
  $author = AuthorPeer::retrieveByPk($this->getRequestParameter('id'));
        
        
  $this->forward404Unless($author);
        
        
 
        
        
  $author->fromArray($this->getRequest()->getParameterHolder()->getAll(),AuthorPeer::TYPE_FIELDNAME);
        
        
  $author->save();
        
        
 
        
        
  return $this->redirect('/author/show?id='.$author->getId());
        
        

表单验证

前面提到在动作类中如何使用validateXXX()方法验证请求参数。但是,如果你使用这种方式来验证表单提交,你不得不重复地重写(再三地写)同样的部分。Symfony提供了表单验证技术,只用YAML文件而不是动作类中的PHP代码。

要演示表单验证特性,让我们考虑下面的表单(经典联系人表单,有nameemailagemessage字段)例子:modules/contact/templates/indexSuccess.php

<?php echo form_tag('contact/send') ?>
        
        
  Name:    <?php echo input_tag('name') ?><br />
        
        
  Email:   <?php echo input_tag('email') ?><br />
        
        
  Age:     <?php echo input_tag('age') ?><br />
        
        
  Message: <?php echo textarea_tag('message') ?><br />
        
        
  <?php echo submit_tag() ?>
        
        
</form>           //这是一种好习惯,虽然看起来不好
        
        

规则是:如果用户输入了非法数据并提交了表单,下一页会显示错误信息,让我们定义验证的具体内容:

l         Name必须,长度2100

l         Email必须,长度2100,必须是合法邮件地址

l         Age必须,大小在1120

l         Message必须

表单验证可以在服务端 / 客户端,服务端验证是必须的,可以防止数据库错误数据的写入,客户端是可选的,但却可以大大提升用户体验,客户端验证使用JS

验证器

有些验证规则被频繁的使用,symfony就将他们打包做成验证器。验证器是一个提供execute()方法的简单类,方法需要一个字段值作为参数,并返回真或假。

现在让我们来看看sfStringValidator,他验证输入是否是字符串,并且长度是否在两个数字之间,这就是必须的验证器:modules/contact/action/actions.class.php

public function validateSend()
        
        
{
        
        
  $name = $this->getRequestParameter('name');
        
        
 
        
        
  // name必须
        
        
  if (!$name)
        
        
  {
        
        
    $this->getRequest()->setError('name', 'name不能为空');
        
        
 
        
        
    return false;
        
        
  }
        
        
 
        
        
  // name必须且长度在2100之间
        
        
  $myValidator = new sfStringValidator();
        
        
  $myValidator->initialize($this->getContext(), array(
        
        
    'min'       => 2,
        
        
    'min_error' => 'name太短,至少2个字符',
        
        
    'max'       => 100,
        
        
    'max_error' => ' name太长,最多100个字符',
        
        
  ));
        
        
  if (!$myValidator->execute($name))
        
        
  {
        
        
    return false;
        
        
  }
        
        
  return true;
        
        
}
        
        

如果用户发送表单是name不符合规定,则sfStringValidator返回falsevalidateSend()方法将失败,handleErrorSend()方法将替代executeSend()方法被调用

sfRequestsetError()方法传递信息给模板以显示错误信息。验证器在内部设置错误信息,可以根据不同的错误设置不同的信息。

将描述转换为代码:

l         name: sfStringValidator (min=2, max=100)

l        email: sfStringValidator (min=2, max=100) sfEmailValidator

l         age: sfNumberValidator (min=0, max=120)

事实上,验证器不能处理表单非空验证。
   
   

验证文件

你可以简单的使用validateSend()方法来实施表单的验证,但这很不好,每个验证都要重复许多的代码。Symfony提供了两外的方式来定义表单验证规则,包含在YAML中。

例如:modules/contact/validate/send.yml

fields:
        
        
  name:
        
        
    required:              //必须
        
        
      msg:       name不能为空
        
        
    sfStringValidator:
        
        
      min:       2        //最小长度
        
        
      min_error: name太短,至少2个字符
        
        
      max:       100
        
        
      max_error: name太长,最多100个字符
        
        

验证器在某项验证失败后不会停止,他会测试所有的验证,即使验证失败了,symfony也仍然去查找validateXXX()方法并执行,所以两个验证是相互补充的。这样做的好处是,一个表单提交可能包含多个错误,可以把所有的错误信息全部显示出来。

验证文件放在模块的validate目录下,并且以要验证的动作命名。

重新显示表单

默认情况,symfony在验证失败后查找handleErrorSend()方法,方法不存在则显示sendError.php模板。但一般是验证失败后重新显示表单并列出错误信息,要这么做,你需要覆盖handleErrorSend()方法并且以转向表单显示结束:modules/contact/actions/actions.class.php

class ContactActions extends sfActions
        
        
{
        
        
  public function executeIndex()
        
        
  {
        
        
    // 显示表单
        
        
  }
        
        
 
        
        
  public function handleErrorSend()
        
        
  {
        
        
    $this->forward('contact', 'index');
        
        
  }
        
        
 
        
        
  public function executeSend()
        
        
  {
        
        
    // 验证表单提交
        
        
  }
        
        
}
        
        

如果选择使用同样的动作显示表单并处理表单提交,可以使handleErrorSend()方法简单的返回sfView::SUCCESS去通过sendSuccess.php显示表单:modules/contact/actions/actions.class.php

class ContactActions extends sfActions
        
        
{
        
        
  public function executeSend()
        
        
  {
        
        
    if ($this->getRequest()->getMethod() != sfRequest::POST)
        
        
    {
        
        
      // 为模板准备数据
        
        
 
        
        
      // 显示表单
        
        
      return sfView::SUCCESS;
        
        
    }
        
        
    else
        
        
    {
        
        
      // 处理表单提交
        
        
      ...
        
        
      $this->redirect('mymodule/anotheraction');
        
        
    }
        
        
  }
        
        
  public function handleErrorSend()
        
        
  {
        
        
    // 为模板准备数据
        
        
 
        
        
    // 显示表单
        
        
    return sfView::SUCCESS;
        
        
  }
        
        
}
        
        

这时,当用户输入了非法的name,表单将再次显示,但输入的数据没了,错误信息也不显示,你需要去修改显示表单的模板,在出错的地方插入错误信息。

在表单中显示错误信息

错误信息在表单验证失败后被作为参数加入到了请求中(你也可以手动方式加入错误信息,通过setError()方法)。sfRequest对象提供了连个游泳的方法来获取错误信息:hasErrorgetError,每个方法都要求将field名字作为参数传送。另外,还要在表单的上部显示一个警告来显示所有错误的集合:templates/indexSuccess.php

<?php if ($sf_request->hasErrors()): ?>
        
        
  <p>The data you entered seems to be incorrect.
        
        
  Please correct the following errors and resubmit:</p>
        
        
  <ul>
        
        
  <?php foreach($sf_request->getErrors() as $name => $error): ?>
        
        
    <li><?php echo $name ?>: <?php echo $error ?></li>
        
        
  <?php endforeach; ?>
        
        
  </ul>
        
        
<?php endif; ?>
        
        

在每个有错误的表单附近显示单独的错误:templates/indexSuccess.php

<?php echo form_tag('contact/send') ?>
        
        
  <?php if ($sf_request->hasError('name')): ?>
        
        
    <?php echo $sf_request->getError('name') ?> <br />
        
        
  <?php endif; ?>
        
        
  Name:    <?php echo input_tag('name') ?><br />
        
        
  ...
        
        
  <?php echo submit_tag() ?>
        
        
</form>
        
        

使用getError()方法看起来有些长,symfony提供了一个form_error()助手来代替:

<?php use_helper('Validation') ?>
        
        
<?php echo form_tag('contact/send') ?>
        
        
            <?php echo form_error('name') ?><br />
        
        
  Name:    <?php echo input_tag('name') ?><br />
        
        
  ...
        
        
  <?php echo submit_tag() ?>
        
        
</form>
        
        

Form_error()助手在错误信息的前后添加了特殊的字符来更容易的查看错误,默认字符使用下箭头($darr;),可以在settings.yml文件中修改:

all:
        
        
  .settings:
        
        
    validation_error_prefix:    ' &darr;&nbsp;'
        
        
    validation_error_suffix:    ' &nbsp;&darr;'
        
        

上面解决了错误信息显示的问题,但用户输入的内容没了,下一步来解决。

重新注入表单

当错误被捕获,原始请求信息仍然可以访问,用户输入的信息还在请求参数中。因此可以通过为每个field加入默认值的方式重新将数据注入表单:templates/indexSuccess.php

<?php use_helper('Validation') ?>
        
        
<?php echo form_tag('contact/send') ?>
        
        
           <?php echo form_error('name') ?><br />
        
        
  Name:    <?php echo input_tag('name', $sf_params->get('name')) ?><br />
        
        
           <?php echo form_error('email') ?><br />
        
        
  Email:   <?php echo input_tag('email', $sf_params->get('email')) ?><br />
        
        
           <?php echo form_error('age') ?><br />
        
        
  Age:     <?php echo input_tag('age', $sf_params->get('age')) ?><br />
        
        
           <?php echo form_error('message') ?><br />
        
        
  Message: <?php echo textarea_tag('message', $sf_params->get('message')) ?><br />
        
        
  <?php echo submit_tag() ?>
        
        
</form>
        
        

同样,这种方式相当乏味,symfony提供了另一种方式来解决问题,也是直接在YAML验证文件,只要激活表单的fillin:特性:validate/send.yml

fillin:
        
        
  enabled: true  # 激活表单重注入特性
        
        
  param:
        
        
    name: test  # 表单名,如果页面中只有一个表单则不是必须的
        
        
    skip_fields:   [email]  # 不注入的field
        
        
    exclude_types: [hidden, password] # 不注入的field类型
        
        
check_types:   [text, checkbox, radio, password, hidden] # 检查类型
        
        

        
        
         
          
        
        
//转换内容
        
        
    converters:         # 实施转换
        
        
      htmlentities:     [first_name, comments]
        
        
      htmlspecialchars: [comments]
        
        
重注入默认对text inputscheck boxexradio buttonstext areas以及select 组件 (simple and multiple)起作用,而对passwordhidden标签不起作用。
   
   
标准symfony验证器
Symfony中包含的标准验证器有:
    
    

l         sfStringValidator

l         sfNumberValidator

l         sfEmailValidator

l         sfUrlValidator

l         sfRegexValidator

l         sfCompareValidator

l         sfPropelUniqueValidator

l         sfFileValidator

l         sfCall

l         backValidator

每个验证器都有默认的一组参数和错误信息,你可以通过覆盖initialize()来修改或者在YAML文件中。
    
    
String Validator
sfStringValidator对字符串相关约束进行验证:
    
    
sfStringValidator:
        
        
  values:       [foo, bar]
        
        
  values_error: The only accepted values are foo and bar
        
        
  insensitive:  false  # 如果设置为真则比较的时候区分大小写
        
        
  min:          2
        
        
  min_error:    至少2个字符
        
        
  max:          100
        
        
  max_error:    最多100个字符
        
        
Number Validator
sfNumberValidator检验数字约束
    
    
sfNumberValidator:
        
        
  nan_error:    请输入数字
        
        
  min:          0
        
        
  min_error:    不得小于0
        
        
  max:          100
        
        
  max_error:    不得大于100
        
        
E-Mail Validator
sfEmailValidator 检验是否有内容且符合格式合法
    
    
sfEmailValidator:
        
        
  strict:       true
        
        
  email_error:  邮件格式不合法
        
        
合法的邮件格式是由RFC822定义的,然而,他却比验证器规定的宽松的多,比如:me@localhostRFC822中是合法的邮件格式,但却不能被验证器验证通过,把strict设置为false则使用RFC822的标准。
    
    
URL Validator

sfUrlValidator检验URL约束

sfUrlValidator:
        
        
  url_error:    URL地址错误
        
        
Regular Expression Validator
sfRegexValidator使用perl兼容的正则表达式来验证约束
    
    
sfRegexValidator:
        
        
  match:        No
        
        
  match_error:  提交中包含多个URL时被认为是垃圾地址
        
        
  pattern:      /http.*http/si
        
        
Compare Validator
sfCompareValidator检测两个请求参数是否相等,对于密码检测很有用
    
    
fields:
        
        
  password1:
        
        
    required:
        
        
      msg:      请输入密码
        
        
  password2:
        
        
    required:
        
        
      msg:      请再次输入密码
        
        
    sfCompareValidator:
        
        
      check:    password1
        
        
      compare_error: 两次输入的密码不一致
        
        
Propel Unique Validator
sfPropelUniqueValidator验证数据在数据库是否已经存在,对于唯一字段非常好用。
    
    
fields:
        
        
  nickname:
        
        
    sfPropelUniqueValidator:
        
        
      class:        User
        
        
      column:       login
        
        
      unique_error: 此用户已经存在,请换一个
        
        
File Validator
sfFileValidator验证文件上传的信息
    
    
fields:
        
        
  image:
        
        
    required:
        
        
      msg:      请选择上传文件
        
        
      file:     True
        
        
    sfFileValidator:
        
        
      mime_types:
        
        
        - 'image/jpeg'
        
        
        - 'image/png'
        
        
        - 'image/x-png'
        
        
        - 'image/pjpeg'
        
        
      mime_types_error: 只能上传PNGJPEG文件
        
        
      max_size:         512000
        
        
      max_size_error:   最大上传512K
        
        
Callback Validator
sfCallbackValidator代理第三方验证,第三方必须返回真或假。
    
    
fields:
        
        
  account_number:
        
        
    sfCallbackValidator:
        
        
      callback:      is_integer
        
        
      invalid_error: 请输入一个数字
        
        
  credit_card_number:
        
        
    sfCallbackValidator:
        
        
      callback:      [myTools, validateCreditCard]
        
        
      invalid_error: 请输入合法的信用卡号
        
        
Named Validators

不多说了,看代码吧:validate/send.yml

validators:
        
        
  myStringValidator:
        
        
    class: sfStringValidator
        
        
    param:
        
        
      min:       2
        
        
      min_error: This field is too short (2 characters minimum)
        
        
      max:       100
        
        
      max_error: This field is too long (100 characters maximum)
        
        

        
        
         
          
        
        
fields:
        
        
  name:
        
        
    required:
        
        
      msg:       The name field cannot be left blank
        
        
    myStringValidator:
        
        
  email:
        
        
    required:
        
        
      msg:       The email field cannot be left blank
        
        
    myStringValidator:
        
        
    sfEmailValidator:
        
        
      email_error:  This email address is invalid
        
        
限制验证的方法(POST GET

默认情况下,动作被POST方法调用的时候验证器运行,你可以修改默认的设置:

methods:         [post]     # 默认设置
        
        

        
        
         
          
        
        
fields:
        
        
  name:
        
        
    required:
        
        
      msg:       The name field cannot be left blank
        
        
    myStringValidator:
        
        
  email:
        
        
    methods:     [post, get] # 覆盖全局的设置
        
        
    required:
        
        
      msg:       The email field cannot be left blank
        
        
    myStringValidator:
        
        
    sfEmailValidator:
        
        
      email_error:  This email address is invalid
        
        
完整验证文件示例
fillin:
        
        
  enabled:      true
        
        

        
        
         
          
        
        
validators:
        
        
  myStringValidator:
        
        
    class: sfStringValidator
        
        
    param:
        
        
      min:       2
        
        
      min_error: 最少2个字符
        
        
      max:       100
        
        
      max_error: 最多两个字符
        
        

        
        
         
          
        
        
fields:
        
        
  name:
        
        
    required:
        
        
      msg:       name不能为空
        
        
    myStringValidator:
        
        
  email:
        
        
    required:
        
        
      msg:       email必须填写
        
        
    myStringValidator:
        
        
    sfEmailValidator:
        
        
      email_error:  email不合法
        
        
  age:
        
        
    sfNumberValidator
        
        
      nan_error:    Please enter an integer
        
        
      min:          0
        
        
      min_error:    "还没出生呢!!怎么发送消息?"
        
        
      max:          120
        
        
      max_error:    "嘿,老爷爷,您还能上网呢??"
        
        
  message:
        
        
    required:
        
        
      msg:          message字段不能为空
        
        

复杂验证

创建自定义验证器

每个验证器都是继承自sfValidator的类,如果symfony提供的验证器忙组不了你的需求,你可以在任何lib目录下自创一个。语法很简单:在initialize()方法中定义默认设置,在execute()方法中写验证信息。

Execute()方法获取第一个参数作为验证对象,第二个参数作为错误信息,所有传递都是引用传递,所以可以在方法内部修改错误信息。
  
  
Initialize()方法接受单例模式的上下文和YAML文件中的参数数组,它必须先调用父类sfValidatorinitialize()方法,然后设定默认值。
  
  
每个验证器都有一个参数仓库:$this->getParameterHolder()
  
  
举例:建立一个sfSpamValidator来验证一个字符串不是spam(散发者,比如在URL中包含另一个URL,在用户不知不觉中引入到别的地方),他检测$value是否包含比max_url次多的“http”字符串。在任意的lib目录下建立sfSpamValidator.class.php文件:
  
  
class sfSpamValidator extends sfValidator
        
        
{
        
        
  public function execute (&$value, &$error)
        
        
  {
        
        
    // 如果max_url=2, 正则表达式为 /http.*http/is
        
        
    $re = '/'.implode('.*', array_fill(0, $this->getParameter('max_url') + 1, 'http')).'/is';
        
        
 
        
        
    if (preg_match($re, $value))
        
        
    {
        
        
      $error = $this->getParameter('spam_error');
        
        
 
        
        
      return false;
        
        
    }
        
        
 
        
        
    return true;
        
        
  }
        
        
 
        
        
  public function initialize ($context, $parameters = null)
        
        
  {
        
        
    // 调用父类initialize()
        
        
    parent::initialize($context);
        
        
 
        
        
    // 设置默认参数值
        
        
    $this->setParameter('max_url', 2);
        
        
    $this->setParameter('spam_error', 'This is spam');
        
        
 
        
        
    // 设置参数
        
        
    $this->getParameterHolder()->add($parameters);
        
        
 
        
        
    return true;
        
        
  }
        
        
}
        
        
使用方式如下:validate/send.yml
  
  
fields:
        
        
  message:
        
        
    required:
        
        
      msg:          message不能为空
        
        
    sfSpamValidator:
        
        
      max_url:      3
        
        
      spam_error:   快离开这个站点,他是spammer
        
        
在表单中使用数组
PHP允许在表单中使用数组语法,示例代码:
  
  
<label for="story[title]">Title:</label>
        
        
<input type="text" name="story[title]" id="story[title]" value="default value" size="45" />
        
        

这时,若直接把带有[]的字段放入验证文件将会出现解析错误,解决方式是使用{}代替[]

fields:
        
        
  story{title}:
        
        
    required:     Yes
        
        
在空字段上执行验证

对于非必填字段也可能需要验证,例如用户修改密码:

fields:
        
        
  password1:
        
        
  password2:
        
        
    sfCompareValidator:
        
        
      check:         password1
        
        
      compare_error:  两次输入的密码不符
        
        
验证过程如下:
   
   
 
 l        
 
 如果password1 == null 并且 password2 == null:
   
   
 
 n    
 
 必需验证通过(不是必需字段)
   
   
 
 n    
 
 验证器不运行
   
   
 
 n    
 
 提交表单有效
   
   
 
 l        
 
 如果password2 == null password1 不是null
   
   
 
 n    
 
 必需验证通过(不是必需字段)
   
   
 
 n    
 
 验证器不运行
   
   
 
 n    
 
 提交表单有效
   
   
但希望在password1非空的时候验证password2,幸运的是,symfony验证器能处理这种情况——使用分组参数。分组中的字段在非空或者一个非空的时候会被验证。所以修改验证代码如下:
   
   
fields:
        
        
  password1:
        
        
    group:           password_group
        
        
  password2:
        
        
    group:           password_group
        
        
    sfCompareValidator:
        
        
      check:         password1
        
        
      compare_error:  两次输入的密码不符
        
        
验证过程如下:
   
   
 
 l        
 
 如果password1 == null 并且 password2 == null:
    
    
 
 n    
 
 必需验证通过
    
    
 
 n    
 
 验证器不运行
    
    
 
 n    
 
 提交表单有效
    
    
 
 l        
 
 Password1 == null 并且 password2 == “foo”:
    
    
 
 n    
 
 必须验证通过
    
    
 
 n    
 
 Password2非空,验证器执行并验证失败
    
    
 
 n    
 
 错误信息抛给password2
    
    
 
 l        
 
 Password1 == foo” 并且 password2 == null:
    
    
 
 n    
 
 必须验证通过
    
    
 
 n    
 
 Password1非空,password2的验证器被执行且验证失败
    
    
 
 n    
 
 错误信息抛给password2
    
    
 
 l        
 
 Password1 == foo” 并且 password2 == foo:
    
    
 
 n    
 
 必须验证通过
    
    
 
 n    
 
 Password2非空,验证器执行并验证通过
    
    
 
 n    
 
 提交表单有效
    
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值