实现表单工厂

表单工厂的目的是从单个配置数组中生成一个可用的表单对象。表单对象应该能够检索它所包含的单个元素,以便生成输出。

如何做…

1.首先,让我们创建一个名为Application\Form\Factory的类来包含工厂代码。它将只有一个属性,$elements和一个getter

  1. namespace Application\Form;
  2. class Factory
  3. {
  4. protected $elements;
  5. public function getElements()
  6. {
  7. return $this->elements;
  8. }
  9. // 剩余代码
  10. }
  1. 在我们定义主要的表单生成方法之前,重要的是要考虑我们计划接收什么配置格式,以及表单生成到底会产生什么。在这个例子中,我们将假设生成将产生一个Factory实例,有一个$elements属性。这个属性将是一个Application\Form\GenericApplication\Form\Element类的数组。

  2. 我们现在准备好处理generate()方法。这将循环浏览配置数组,创建适当的Application\Form\GenericApplication\Form\Element\*对象,这些对象将被存储在$elements数组中。新方法将接受配置数组作为参数。将这个方法定义为静态的方法是很方便的,这样我们就可以根据需要使用不同的配置块来生成任意多的实例。

  3. 我们创建一个Application\Form\Factory的实例,然后我们开始在配置数组中循环。

  1. public static function generate(array $config)
  2. {
  3. $form = new self();
  4. foreach ($config as $key => $p) {
  1. 接下来,我们检查Application\Form\Generic类的构造函数中是否有可选的参数。
  1. $p['errors'] = $p['errors'] ?? array();
  2. $p['wrappers'] = $p['wrappers'] ?? array();
  3. $p['attributes'] = $p['attributes'] ?? array();
  1. 现在所有的构造函数参数都到位了,我们可以创建表单元素实例,然后将其存储在$elements中。
  1. $form->elements[$key] = new $p['class']
  2. (
  3. $key,
  4. $p['type'],
  5. $p['label'],
  6. $p['wrappers'],
  7. $p['attributes'],
  8. $p['errors']
  9. );
  1. 接下来,我们将注意力转移到选项上。如果设置了选项参数,我们使用list()将数组值提取到变量中。然后我们使用switch()测试元素类型,并使用适当数量的参数运行setOptions()
  1. if (isset($p['options'])) {
  2. list($a,$b,$c,$d) = $p['options'];
  3. switch ($p['type']) {
  4. case Generic::TYPE_RADIO :
  5. case Generic::TYPE_CHECKBOX :
  6. $form->elements[$key]->setOptions($a,$b,$c,$d);
  7. break;
  8. case Generic::TYPE_SELECT :
  9. $form->elements[$key]->setOptions($a,$b);
  10. break;
  11. default :
  12. $form->elements[$key]->setOptions($a,$b);
  13. break;
  14. }
  15. }
  16. }
  1. 最后,我们返回表单对象,并结束该方法。
  1. return $form;
  2. }
  1. 理论上,此时,我们可以通过简单地迭代元素数组并运行render()方法,轻松地在视图逻辑中渲染表单。视图逻辑可能是这样的。
  1. <form name="status" method="get">
  2. <table id="status" class="display" cellspacing="0" width="100%">
  3. <?php foreach ($form->getElements() as $element) : ?>
  4. <?php echo $element->render(); ?>
  5. <?php endforeach; ?>
  6. </table>
  7. </form>
  1. 最后,我们返回表单对象,并结束该方法。

  2. 接下来,我们需要在Application\Form\Element下定义一个离散的Form类。

  1. namespace Application\Form\Element;
  2. class Form extends Generic
  3. {
  4. public function getInputOnly()
  5. {
  6. $this->pattern = '<form name="%s" %s> ' . PHP_EOL;
  7. return sprintf($this->pattern, $this->name,
  8. $this->getAttribs());
  9. }
  10. public function closeTag()
  11. {
  12. return '</' . $this->type . '>';
  13. }
  14. }
  1. 回到Application\Form\Factory类,我们现在需要定义一个简单的方法来返回一个sprintf()包装器模式,作为输入的分装。举个例子,如果包装器是一个属性为class="test"div,我们将产生这样的模式。<div class="test">%s</div>。然后我们的内容将被sprintf()函数替换成%s
  1. protected function getWrapperPattern($wrapper)
  2. {
  3. $type = $wrapper['type'];
  4. unset($wrapper['type']);
  5. $pattern = '<' . $type;
  6. foreach ($wrapper as $key => $value) {
  7. $pattern .= ' ' . $key . '="' . $value . '"';
  8. }
  9. $pattern .= '>%s</' . $type . '>';
  10. return $pattern;
  11. }
  1. 最后,我们准备好定义一个方法来进行整体表单的渲染。我们为每一个表单行获取包装器sprintf()模式。然后我们在元素中循环,渲染每一个元素,并将输出包装在行模式中。接下来,我们生成一个Application\Form\Element\Form实例。然后,我们检索表单包装器sprintf()模式,并检查form_tag_inside_wrapper标志,它告诉我们是否需要将表单标签放在表单包装器内部或外部。
  1. public static function render($form, $formConfig)
  2. {
  3. $rowPattern = $form->getWrapperPattern(
  4. $formConfig['row_wrapper']);
  5. $contents = '';
  6. foreach ($form->getElements() as $element) {
  7. $contents .= sprintf($rowPattern, $element->render());
  8. }
  9. $formTag = new Form($formConfig['name'],
  10. Generic::TYPE_FORM,
  11. '',
  12. array(),
  13. $formConfig['attributes']);
  14. $formPattern = $form->getWrapperPattern(
  15. $formConfig['form_wrapper']);
  16. if (isset($formConfig['form_tag_inside_wrapper'])
  17. && !$formConfig['form_tag_inside_wrapper']) {
  18. $formPattern = '%s' . $formPattern . '%s';
  19. return sprintf($formPattern, $formTag->getInputOnly(),
  20. $contents, $formTag->closeTag());
  21. } else {
  22. return sprintf($formPattern, $formTag->getInputOnly()
  23. . $contents . $formTag->closeTag());
  24. }
  25. }

如何运行…

参考前面的代码,创建Application\Form\FactoryApplication\Form\Element\Form类。

接下来,你可以定义一个chap_06_form_factor.php调用脚本,设置自动加载和锚定新类。

  1. <?php
  2. require __DIR__ . '/../Application/Autoload/Loader.php';
  3. Application\Autoload\Loader::init(__DIR__ . '/..');
  4. use Application\Form\Generic;
  5. use Application\Form\Factory;

接下来,使用第一个示例中定义的$wrappers数组来定义包装器。您也可以使用第二个示例中定义的$statusList数组。

看看是否有来自$_POST的任何状态输入。任何输入都将成为选定的键。否则,选定的键是默认的。

  1. $email = $_POST['email'] ?? '';
  2. $checked0 = $_POST['status0'] ?? 'U';
  3. $checked1 = $_POST['status1'] ?? 'U';
  4. $checked2 = $_POST['status2'] ?? ['U'];
  5. $checked3 = $_POST['status3'] ?? ['U'];

现在您可以定义整体的表单配置。名称和属性参数用于配置表单标签本身。另外两个参数代表表单级和行级包装器。最后,我们提供了一个 form_tag_inside_wrapper 标志,用于指示表单标签不应该出现在包装器(即<table> )内部。如果包装器是<div>,我们会将这个标志设置为TRUE

  1. $formConfig = [
  2. 'name' => 'status_form',
  3. 'attributes' => ['id'=>'statusForm','method'=>'post', 'action'=>'chap_06_form_factory.php'],
  4. 'row_wrapper' => ['type' => 'tr', 'class' => 'row'],
  5. 'form_wrapper' => ['type'=>'table','class'=>'table', 'id'=>'statusTable',
  6. 'class'=>'display','cellspacing'=>'0'],
  7. 'form_tag_inside_wrapper' => FALSE,
  8. ];

接下来,定义一个数组,该数组为工厂要创建的每个表单元素保存参数。数组键是表单元素的名称,必须是唯一的。

  1. $config = [
  2. 'email' => [
  3. 'class' => 'Application\Form\Generic',
  4. 'type' => Generic::TYPE_EMAIL,
  5. 'label' => 'Email',
  6. 'wrappers' => $wrappers,
  7. 'attributes'=> ['id'=>'email','maxLength'=>128, 'title'=>'Enter address',
  8. 'required'=>'','value'=>strip_tags($email)]
  9. ],
  10. 'password' => [
  11. 'class' => 'Application\Form\Generic',
  12. 'type' => Generic::TYPE_PASSWORD,
  13. 'label' => 'Password',
  14. 'wrappers' => $wrappers,
  15. 'attributes' => ['id'=>'password',
  16. 'title' => 'Enter your password',
  17. 'required' => '']
  18. ],
  19. // etc.
  20. ];

最后,一定要生成表格。

  1. $form = Factory::generate($config);

实际的显示逻辑非常简单,因为我们只需调用表单级别的render()方法。

  1. <?= $form->render($form, $formConfig); ?>

下面是实际输出。

实现表单工厂 - 图1