现在。我们继续为表单添加一些验证机制。方法是编辑另一个组件装饰器类来表达一个“invalid”状态并扩展FormHandler类增加一个validate()方法以处理组件示例数组。如果组件非法(“invalid”),我们通过一个“invalid”类将它包装在<span>元素中。这里是一个证明这个目标的测试
class WidgetTestCase extends UnitTestCase { // ... function testInvalid() { $text =& new Invalid(new TextInput(‘email’)); $output = $text->paint(); $this->assertWantedPattern( ‘~^<span class=”invalid”><input[^>]+></span>$~i’, $output); } } |
这里是Invalid WidgetDecorator子类:
//代码Here’s the Invalid WidgetDecorator subclass:
class Invalid extends WidgetDecorator { function paint() { eturn ‘<span class=”invalid”>’.$this->widget->paint().’</span>’; } } |
装饰器的一个有点是你可以将他们串在一起(使用)。Invalid装饰器仅仅知道:它正在包装一个组件:它不必关心组件是否是一个TextInput, Select,或者是一个有标签的被装饰版本的组件 。
这导致了下一个合理的测试用例:
class WidgetTestCase extends UnitTestCase { // ... function testInvalidLabeled() { $text =& new Invalid( new Labeled( ‘Email’ ,new TextInput(‘email’))); $output = $text->paint(); $this->assertWantedPattern(‘~<b>Email:</b> <input~i’, $output); $this->assertWantedPattern( ‘~^<span class=”invalid”>.*</span>$~i’, $output); } } |
有了Invalid装饰器,我们来处理FormHandler::validate() 方法:
class FormHandlerTestCase extends UnitTestCase { // ... function testValidateMissingName() { $post =& new Post; $post->set(‘fname’, ‘Jason’); $post->set(‘email’, ‘jsweat_php@yahoo.com’); $form = FormHandler::build($post); $this->assertFalse(FormHandler::validate($form, $post)); $this->assertNoUnwantedPattern(‘/invalid/i’, $form[0]->paint()); $this->assertWantedPattern(‘/invalid/i’, $form[1]->paint()); $this->assertNoUnwantedPattern(‘/invalid/i’, $form[2]->paint()); } } |
这个测试捕获(包含)了所有的基本方面:建立一个Post实例的存根,使用它建立一个组件集合,然后将集合传送给validate方法。
class FormHandler { function validate(&$form, &$post) { // first name required if (!strlen($post->get(‘fname’))) { $form[0] =& new Invalid($form[0]);} // last name required if (!strlen($post->get(‘lname’))) { $form[1] =& new Invalid($form[1]); } } } |
不协调的代码
当我看这段代码时,我发现了两个不协调之处:通过数字索引访问表单元素,需要传递$_post数组。给validation方法。在以后的重构中,最好是创建一个组件集合用一个以表单元素名字索引的关联数组表示或者用一个Registry模式作为更合理的一步。你也可以给类Widget增加一个方法返回它的
当前数值,取消需要传递$_Post实例给Widget集合的构造函数。所有这些都超出了这个例子目的的范围。
为了验证目的,我们继续增加一个简单的 正则方法(regex)来验证email地址:
class FormHandlerTestCase extends UnitTestCase { // ... function testValidateBadEmail() { $post =& new Post; $post->set(‘fname’, ‘Jason’); $post->set(‘lname’, ‘Sweat’); $post->set(‘email’, ‘jsweat_php AT yahoo DOT com’); $form = FormHandler::build($post); $this->assertFalse(FormHandler::validate($form, $post)); $this->assertNoUnwantedPattern(‘/invalid/i’, $form[0]->paint()); $this->assertNoUnwantedPattern(‘/invalid/i’, $form[1]->paint()); $this->assertWantedPattern(‘/invalid/i’, $form[2]->paint()); } } |
实现这个简单的email验证的代码如下:
//代码
class FormHandler { function validate(&$form, &$post) { // first name required if (!strlen($post->get(‘fname’))) { $form[0] =& new Invalid($form[0]);} // last name required if (!strlen($post->get(‘lname’))) { $form[1] =& new Invalid($form[1]); } // email has to look real if (!preg_match(‘~\w+@(\w+\.)+\w+~’ ,$post->get(‘email’))) { $form[2] =& new Invalid($form[2]); } } } |
你也可以创建一个测试用例以验证form表单何时有效://代码
class FormHandlerTestCase extends UnitTestCase { // ... function testValidate() { $post =& new Post; $post->set(‘fname’, ‘Jason’); $post->set(‘lname’, ‘Sweat’); $post->set(‘email’, ‘jsweat_php@yahoo.com’); $form = FormHandler::build($post); $this->assertTrue(FormHandler::validate($form, $post)); $this->assertNoUnwantedPattern(‘/invalid/i’, $form[0]->paint()); $this->assertNoUnwantedPattern(‘/invalid/i’, $form[1]->paint()); $this->assertNoUnwantedPattern(‘/invalid/i’, $form[2]->paint()); } } |
这又提出了在本方法内追踪任何验证失败的需求,因此它可以返回true如果所有的都合格。
延伸阅读:
从魔兽看PHP设计模式
《PHP设计模式介绍》导言
PHP设计模式介绍 第一章 编程惯用法
PHP设计模式介绍 第二章 值对象模式
PHP设计模式介绍 第三章 工厂模式
PHP设计模式介绍 第四章 单件模式
PHP设计模式介绍 第五章 注册模式
PHP设计模式介绍 第六章 伪对象模式
PHP设计模式介绍 第七章 策略模式
PHP设计模式介绍 第八章 迭代器模式