Chapter 3: Working with Forms
咕~~(╯﹏╰)b,没想到还是有些人来看。
基本输入
这一章的内容比较简单,在ZF框架里,form标签和表单元素的标签都被封装成类,通过这些类可以简化输出构成表单的HTML内容,这些类具有一些方法用来设定表单或者输入控件的属性,我只举一个书中例子:
<?php
class Form_Example extends Zend_Form
{
public function init()
{
// initialize form
$this->setAction('/sandbox/example/form')
->setMethod('post');
// create text input for name
$name = new Zend_Form_Element_Text('name');
$name->setLabel('Name:')
->setOptions(array('id' => 'fname'));
// create radio buttons for type
$type = new Zend_Form_Element_Radio('type');
$type->setLabel('Membership type:')
->setMultiOptions(
array(
'silver' => 'Silver',
'gold' => 'Gold',
'platinum' => 'Platinum'
)
)
->setOptions(array('id' => 'mtype'));
// create checkbox for newsletter subscription
$subscribe = new Zend_Form_Element_Checkbox('subscribe');
$subscribe->setLabel('Subscribe to newsletter')
->setCheckedValue('yes')
->setUncheckedValue('no');
// attach elements to form
$this->addElement($name)
->addElement($type)
->addElement($subscribe);
}
}
这个示例中,先创建一个form元素,再创建三个输入控件,然后将三个控件加入form中。ZF封装的HTML表单元素有16个:
Element Class | Description |
Zend_Form_Element_Text | |
Zend_Form_Element_Hidden | |
Zend_Form_Element_Password | |
Zend_Form_Element_Radio | |
Zend_Form_Element_Checkbox | |
Zend_Form_Element_MultiCheckbox | |
Zend_Form_Element_Select | |
Zend_Form_Element_MultiSelect | |
Zend_Form_Element_Textarea | |
Zend_Form_Element_File | |
Zend_Form_Element_Image | |
Zend_Form_Element_Button | |
Zend_Form_Element_Hash | |
Zend_Form_Element_Captcha | |
Zend_Form_Element_Reset | |
Zend_Form_Element_Submit |
注意,除了通常在HTML4里会见到的表单控件外,ZF有两个特别的类型:Hash和Captcha。此处难免会有新的疑虑,即是HTML5里新加的元素将会怎样?
验证与过滤
除了上面提到的之外,ZF最重要的,是提供一个机制,可以方便地给这些表单控件接收到的用户数据增加验证和过滤。比如,要给一个文字输入增加一个过滤器,将其中的特殊符号进行HTML编码,你可以这样做:
// filter special characters
$name = new Zend_Form_Element_Text('name');
$name->setLabel('Username:')
->setOptions(array('size' => '16'))
->addFilter('HtmlEntities');
也有另外一个做法:
// filter special characters
$name = new Zend_Form_Element_Text('name');
$name->setLabel('Username:')
->setOptions(array('size' => '16'))
->addFilter(new Zend_Filter_HtmlEntities());
ZF提供的过滤器有:
验证器的做法与过滤器类似:
// should contain only integer values between 1 and 100
$age = new Zend_Form_Element_Text('age');
$age->setLabel('Age:')
->setOptions(array('size' => '4'))
->setRequired(true)
->addValidator('Int')
->addValidator('Between', false, array(1,100));
当然,这些代码按逻辑上说应该是放在接收数据提交的PHP代码块里,可是从书中的示例来看,它们的调用却是在生成form的时候,所以究竟ZF实现这些验证的具体做法是如何,现在还不清楚,一种可能是:它们动态生成Ajax代码来在客户端执行这些验证操作,就像.NET里那样,但是书中在抛砖引玉时拿出的传统代码却又不是Ajax代码,而是在接收表单数据的PHP代码中的验证处理。总之,需要进一步看下文才知道。
ZF框架提供的验证器有:
从79页的Ask Expert部分可以推敲,这些验证和过滤是在server端用PHP来完成的,即是说是在处理提交的表单数据的时候。
在ZF中处理提交的表单数据时,可以将验证器和过滤器链在一起使用,像刚刚上面那段示例代码中,将两个验证器都应用到age的数据上,这两个验证会自动被逐个逐个执行。当然从后面的代码示例来看,将验证器和过滤器同时混合起来链在一起也没问题。另外,验证器有一个使用上的特点,即可以指定是否在某个验证失败时就即刻停止处理,返回错误,这个是通过addValidator()的第二个参数来指定的。
处理提交的数据
其实这里主要是三个方法,不如用书中例子来解释:
<?php
class ExampleController extends Zend_Controller_Action
{
public function formAction()
{
$form = new Form_Example;
$this->view->form = $form;
// check the request
// run the validators
if ($this->getRequest()->isPost())
{
if ($form->isValid($this->getRequest()->getPost()))
{
// valid data: get the filtered and valid values
// do something, save to database or write to file
// display a success view
$values = $form->getValues();
$this->_redirect('/form/success');
}
else
{
// invalid data: get the error message array
// for manual processing (if needed)
// redisplay the form with errors
$this->view->messages = $form->getMessages();
}
}
}
}
?>
注意其中的三个form类的方法:isValid()、getValues()、getMessages()。
继续Square实例
下面到了实践的时候了,来回到贯穿书中的square项目。上次在给这个项目创建主布局时,在菜单里加了个contact条目,而它指向的内容一直是空白的,现在就来为这个项目创建一个联络表单。
为了完成这个实验,需要先找到书中使用的字体,用来在CAPTCHA输入中使用的。
先在/public/下面创建个fonts文件夹,把这个字体放进去。并且再创建一个captcha文件夹。然后创建这个文件:/library/Square/Form/Contact.php。其内容书中有。
现在提一些关于ZF框架的自动加载类的机制,在自定义的类的类名前加一个自定义的字符串,比如在这个示例中为“Square_”。然后在application.ini中注册这个字符串。ZF框架就会自动为你加载这个类的定义文件,简单说就是你不需要自己写include指令来加载你写的类定义PHP文件。而这个过程在ZF的术语中被称为“注册自定义命名空间”(register custom namespace),当然这个写在类名中并被注册的前缀就是namespace。
在刚刚创建的Contact.php中,我们自定义的类的名称为Square_Form_Contact,那么现在来注册这个命名空间:在application.ini中添加:
autoloaderNamespaces[] = "Square_"
还没完,还需要创建一个controller来使用这个form。这个过程包括创建controller文件和view文件并在ini文件中指定新的路径,和在master layout中修改contact的a标签等等。。。
另外,在我的case中,我需要修改CAPTCHA图片的路径为:
// create captcha
$captcha = new Zend_Form_Element_Captcha('captcha', array(
'captcha' => array(
'captcha' => 'Image',
'wordLen' => 6,
'timeout' => 300,
'width' => 300,
'height' => 100,
'imgUrl' => '/square/public/captcha', // in my case
'imgDir' => APPLICATION_PATH . '/../public/captcha',
'font' => APPLICATION_PATH . '/../public/fonts/LiberationSansRegular.ttf',
)
));
最后见到的页面为:
同时,我也需要修改表单的提交链接,来适应我自己的情况,在自定义Form的类定义里:
$this->setAction('square/public/contact/index')
在完成这个实验的过程中,我见到这本书的附带代码里,application.ini中用#来标示为注脚,可是在我所使用的ZF版本中,这个做法会弹出错误,生成这个做法已经被废弃:
<b>Fatal error</b>: Uncaught exception 'Zend_Config_Exception' with message 'Comments starting with '#' are deprecated in D:\idsweb\htdocs\square\application/configs/application.ini on line 16
如果想要成功提交这个表单,需要安装Apache的服务器本身是有一个有效的STMP服务器,或者能够提供邮件功能服务的机器,否则即使将代码中$mail->addTo('info@square.example.com');的邮箱地址换成你自己的有效邮箱也不行。
这个过程中Zend_Mail的send()方法抛出一个异常,而这个异常在ErrorController中被捕捉,于是它得到了控制,输出了上面的错误信息。
所以现在我们能够尝试的,只是在输入的数据不符合要求的情况下,ZF自带的验证器会产生怎样的效果:
注意:ZF的表单类的工作机制:如果验证失败的话,它会将用户填写的数据保留在表单里,同错误信息一同显示给用户。
这一章最后讨论的内容是,如何修改ZF框架输出的表单和表单元素的HTML内容,这个话题是针对网页设计者而言的,多数时候,比如错误信息,我们可能会有一个整体的设计,将表单提交后的提示信息统一显示在某个位置,并且按照指定的设计样式,所以用来包住提示信息的HTML标签以及它出现的位置是极为多变的,而为了提供这个能力,ZF框架有一套关于Decorators的类和与之相关的方法。这本书在这里给出的例子极为简单,只是将一个按钮包在一个p标签中:
<?php
class Form_Example extends Zend_Form
{
...
public $buttonDecorators = array(
array('ViewHelper'),
array('HtmlTag', array('tag' => 'p'))
);
public function init()
{
...
// create submit button
$submit = new Zend_Form_Element_Submit(
'submit',
array('class' => 'submit')
);
$submit->setLabel('Sign Up')
->setDecorators($this->buttonDecorators);
}
}
...
这个地方更深入的地方我也一知半解,作者也不打算继续探讨。总之,一般来说,都是需要自己扩展Zend_Form_Decorator_Abstract类。