将验证绑定到表单
当表单首次呈现时,将一个表单类(如前面示例中描述的Application\Form\Factory)与一个可以执行过滤或验证的类(如前面配方中描述的Application\Filter\*)绑定是没有什么价值的。然而,一旦表单数据被提交,兴趣就会增长。如果表单数据未能通过验证,则可以对值进行过滤,然后重新显示。验证错误信息可以与表单元素绑定,并呈现在表单字段旁边。
如何做…
1.首先,一定要实现在 《实现表单工厂》、《链式$_POST过滤器》和 《链式$_POST验证器》示例中定义的类。
- 现在我们将注意力转向
Application/Form/Factory类,并添加属性和设置器,允许我们附加Application/Filter/Filter和Application/Filter/Validator的实例。我们还需要定义$data,它将用于保留过滤和验证的数据。
const DATA_NOT_FOUND = 'Data not found. Run setData()';const FILTER_NOT_FOUND = 'Filter not found. Run setFilter()';const VALIDATOR_NOT_FOUND = 'Validator not found. Run setValidator()';protected $filter;protected $validator;protected $data;public function setFilter(Filter $filter){$this->filter = $filter;}public function setValidator(Validator $validator){$this->validator = $validator;}public function setData($data){$this->data = $data;}
- 接下来,我们定义了一个
validate()方法,该方法调用嵌入式Application/Filter/Validator实例的process()方法。我们检查$data和$validator是否存在。如果不存在,就会抛出相应的异常,并指示需要先运行哪个方法。
public function validate(){if (!$this->data)throw new RuntimeException(self::DATA_NOT_FOUND);if (!$this->validator)throw new RuntimeException(self::VALIDATOR_NOT_FOUND);
- 调用
process()方法后,我们将验证结果消息与表单元素消息关联起来。需要注意的是,process()方法会返回一个布尔值,代表数据集的整体验证状态。当表单在验证失败后重新显示时,错误信息将出现在每个元素旁边。
$valid = $this->validator->process($this->data);foreach ($this->elements as $element) {if (isset($this->validator->getResults()[$element->getName()])) {$element->setErrors($this->validator->getResults()[$element->getName()]->messages);}}return $valid;}
- 以类似的方式,我们定义了一个
filter()方法,该方法调用嵌入式Application/Filter/Filter实例的process()方法。与步骤3中描述的validate()方法一样,我们需要检查$data和$filter是否存在。如果缺少任何一个,我们将抛出一个带有适当消息的RuntimeException。
public function filter(){if (!$this->data)throw new RuntimeException(self::DATA_NOT_FOUND);if (!$this->filter)throw new RuntimeException(self::FILTER_NOT_FOUND);
- 然后我们运行
process()方法,产生一个Result对象数组,其中$item属性代表过滤链的最终结果。然后我们对结果进行循环,如果对应的$element键匹配,则将值属性设置为过滤后的值。我们还添加过滤过程中产生的任何消息。当表单重新显示时,所有的值属性都会显示过滤后的结果。
$this->filter->process($this->data);foreach ($this->filter->getResults() as $key => $result) {if (isset($this->elements[$key])) {$this->elements[$key]->setSingleAttribute('value', $result->item);if (isset($result->messages)&& count($result->messages)) {foreach ($result->messages as $message) {$this->elements[$key]->addSingleError($message);}}}}}
如何运行…
你可以像上面描述的那样,先对Application/Form/Factory进行修改。对于测试目标,您可以使用链式$_POST过滤器示例中的如何做部分中所示的professor数据库表。各种列的设置应该能让您了解要定义哪些表单元素、过滤器和验证器。
作为一个例子,你可以定义一个chap_06_tying_filters_to_form_definitions.php文件,它将包含表单包装器、元素和过滤器分配的定义。下面是一些例子:
<?phpuse Application\Form\Generic;define('VALIDATE_SUCCESS', 'SUCCESS: form submitted ok!');define('VALIDATE_FAILURE', 'ERROR: validation errors detected');$wrappers = [Generic::INPUT => ['type' => 'td', 'class' => 'content'],Generic::LABEL => ['type' => 'th', 'class' => 'label'],Generic::ERRORS => ['type' => 'td', 'class' => 'error']];$elements = ['first_name' => ['class' => 'Application\Form\Generic','type' => Generic::TYPE_TEXT,'label' => 'First Name','wrappers' => $wrappers,'attributes'=> ['maxLength'=>128,'required'=>'']],'last_name' => ['class' => 'Application\Form\Generic','type' => Generic::TYPE_TEXT,'label' => 'Last Name','wrappers' => $wrappers,'attributes'=> ['maxLength'=>128,'required'=>'']],// etc.];// overall form config$formConfig = ['name' => 'prospectsForm','attributes' => ['method'=>'post','action'=>'chap_06_tying_filters_to_form.php'],'row_wrapper' => ['type' => 'tr', 'class' => 'row'],'form_wrapper' => ['type'=>'table','class'=>'table','id'=>'prospectsTable','class'=>'display','cellspacing'=>'0'],'form_tag_inside_wrapper' => FALSE,];$assignments = ['first_name' => [ ['key' => 'length','params' => ['min' => 1, 'max' => 128]],['key' => 'alnum','params' => ['allowWhiteSpace' => TRUE]],['key' => 'required','params' => []] ],'last_name' => [ ['key' => 'length','params' => ['min' => 1, 'max' => 128]],['key' => 'alnum','params' => ['allowWhiteSpace' => TRUE]],['key' => 'required','params' => []] ],'address' => [ ['key' => 'length','params' => ['max' => 256]] ],'city' => [ ['key' => 'length','params' => ['min' => 1, 'max' => 64]] ],'state_province'=> [ ['key' => 'length','params' => ['min' => 1, 'max' => 32]] ],'postal_code' => [ ['key' => 'length','params' => ['min' => 1, 'max' => 16] ],['key' => 'alnum','params' => ['allowWhiteSpace' => TRUE]],['key' => 'required','params' => []] ],'phone' => [ ['key' => 'phone', 'params' => []] ],'country' => [ ['key' => 'in_array','params' => $countries ],['key' => 'required','params' => []] ],'email' => [ ['key' => 'email', 'params' => [] ],['key' => 'length','params' => ['max' => 250] ],['key' => 'required','params' => [] ] ],'budget' => [ ['key' => 'float', 'params' => []] ]];
你可以使用前面示例中介绍的已经存在的chap_06_post_data_config_callbacks.php和chap_06_post_data_config_messages.php文件。最后,定义一个chap_06_tying_filters_to_form.php文件,设置自动加载并包含这三个配置文件。
<?phprequire __DIR__ . '/../Application/Autoload/Loader.php';Application\Autoload\Loader::init(__DIR__ . '/..');include __DIR__ . '/chap_06_post_data_config_messages.php';include __DIR__ . '/chap_06_post_data_config_callbacks.php';include __DIR__ . '/chap_06_tying_filters_to_form_definitions.php';
接下来,你可以创建表单工厂、过滤器和验证器类的实例。
use Application\Form\Factory;use Application\Filter\ { Validator, Filter };$form = Factory::generate($elements);$form->setFilter(new Filter($callbacks['filters'], $assignments['filters']));$form->setValidator(new Validator($callbacks['validators'], $assignments['validators']));
然后你可以检查是否有任何$_POST数据。如果有,则进行验证和过滤。
$message = '';if (isset($_POST['submit'])) {$form->setData($_POST);if ($form->validate()) {$message = VALIDATE_SUCCESS;} else {$message = VALIDATE_FAILURE;}$form->filter();}?>
视图逻辑非常简单:只需渲染表单。任何验证信息和各种元素的值将作为验证和过滤的一部分被分配。
<?= $form->render($form, $formConfig); ?>
下面是一个使用不良表格数据的例子。

请注意过滤和验证信息。也请注意不良标签。

