使用Zend Framework 2中的FormElement ViewHelper渲染自定义表单元素

时间:2012-09-11 10:45:43

标签: php zend-form zend-framework2 viewhelper

我为一个特殊的,复杂的目的创建了一个新的表单元素类(带有附加按钮的文本输入字段,用于打开“搜索向导”弹出窗口)。

为了正确渲染这个元素,我还创建了一个表单视图助手。到目前为止,一切正常并且很好。

但是,如果我尝试使用FormCollection视图助手渲染表单,则该元素将呈现为基本输入元素。这是因为FormCollection助手依赖的FormElement视图助手使用硬编码的if子句系列将元素的类型映射到特定的表单视图助手。它无法映射我的元素类,因此可以回退到FormInput

即。 (取自Zend / Form / View / Helper / FormElement.php,第41-49行):

    if ($element instanceof Element\Button) {
        $helper = $renderer->plugin('form_button');
        return $helper($element);
    }

    if ($element instanceof Element\Captcha) {
        $helper = $renderer->plugin('form_captcha');
        return $helper($element);
    }

    ...

    $helper = $renderer->plugin('form_input');
    return $helper($element);

等等。

我有点卡在这里,因为这种架构并没有真正提升可扩展性。

我想到的唯一解决方案(除了手工渲染表单)是扩展FormElement视图助手类,从而创建我自己的CustomFormElement视图助手。但是,由于其复杂性,我将自定义元素放入自己的模块中。所以我必须动态编写这个CustomFormElement助手来添加来自任何模块的自定义元素。我不认为这是推荐的程序。

是否有其他解决方案,或者甚至是我的完整方法未经推荐?提前谢谢!

5 个答案:

答案 0 :(得分:7)

我认为最简单的方法是扩展Zend\Form\View\Helper\FormElement,在render()方法中处理字段类型,并将FormElement注册为应用程序/模块的默认FormElement。假设您有自己想要呈现的自定义TestField:

namespace Application\Form\View\Helper; 

use \Zend\Form\ElementInterface;
use \Zend\Form\View\Helper\FormElement
use \Application\Form\Element\TestField;

class MyFormElement extends FormElement
{
    public function render(ElementInterface $element)
    {
        $renderer = $this->getView();
        if (!method_exists($renderer, 'plugin')) {
            // Bail early if renderer is not pluggable
            return '';
        }

        //your custom fields go here...
        if ($element instanceof TestField) {
            $helper = $renderer->plugin('\Application\Form\View\Helper\FormTestField');
            return $helper($element);
        }

        return parent::render($element);
    }
}

在Application / config / module.config.php中:

'view_helpers' => array(
    'invokables' => array(
         'form_element' => 'Application\Form\View\Helper\MyFormElement',
    )
)

答案 1 :(得分:3)

尽可能地使用FormElement视图帮助程序,addType覆盖使用的视图帮助程序。即在视图中,就在呈现表单之前:

<?php $this->plugin('FormElement')->addType('text', 'formcustom'); ?> 

这将使用您的视图助手按键名覆盖FormRowFormCollection助手中使用的视图助手:

在您的配置中

'view_helpers' => array(
    'invokables' => array(
        'formcustom' => 'Application\Form\View\Helper\FormCustom',
    )
),

当问到这个问题时,方法可能不存在。但现在是。

答案 2 :(得分:3)

以下是我所做的事情,感觉就像保持事物分离和整洁的正确程度一样。

假设:

  • 一个新元素:MyModule \ Form \ MyElement,它扩展了Zend \ Form \ Element
  • MyElement的新视图助手类:MyModule \ Form \ View \ Helper \ FormMyElement,它扩展了Zend \ Form \ View \ Helper \ AbstractHelper

以下是如何通过将以下内容添加到module.config.php来注册用于呈现元素的视图助手:

'view_helpers' => array(
    'invokables'=> array(
        'formMyElement' => 'MyModule\Form\View\Helper\FormMyElement',
    ),
    'factories' => array(
        'formElement' => function($sm) {
            $helper = new \Zend\Form\View\Helper\FormElement();
            $helper->addClass('MyModule\Form\MyElement', 'formMyElement');
            return $helper;
        }
    ),
),

关键是你为FormElement提供了一个新的工厂方法,它仍然创建相同的标准类(不需要覆盖它),但也调用addClass方法将自定义助手注册为自定义的正确帮助器元件。如果您不想为助手创建短名称,可以删除invokables部分并将FQCN放入addClass调用中,但我喜欢使用短名称。

这是我迄今为止发现的最佳方法。理想情况下,您不必接管FormElement的构造,只需修改传递给它的配置即可。这种方法的缺点是,如果你有多个定义自定义表单元素的模块,如果它们都试图重新定义FormElement工厂,它们将发生冲突。您不能以这种方式在多个模块中指定添加项。因此,如果有人发现可以设置的更好的配置,只是简单地传递给FormElement :: addClass()方法,请告诉我。

顺便说一句,我发现这个页面并没有解决方程式的助手方面,而是讨论了注册新的表单元素类以及如何覆盖内置类:http://framework.zend.com/manual/current/en/modules/zend.form.advanced-use-of-forms.html

答案 3 :(得分:2)

----自定义表单元素-----

namespace App\Form\View\Helper;

use Zend\Form\View\Helper\FormElement as ZendFormElement;

/**
 * Description of FormElement
 */
class FormElement
        extends ZendFormElement
{

    public function addTypes(array $types)
    {
        foreach ($types as $type => $plugin) {
            $this->addType($type, $plugin);
        }
    }

}

---- application module.config.php --------------

//..........
    'view_helpers' => array(
        'invokables' => array(
            'formRTE' => 'App\Form\View\Helper\FormRTE',
        ),
        'factories' => array(
            'formElement' => function($sm) {
                $helper = new App\Form\View\Helper\FormElement();
                $helper->addTypes(array(
                    'rte' => 'formRTE',
                    ));
                return $helper;
            }
        ),
    ),
//.........

答案 4 :(得分:0)

好像我们都遇到了Zend的Form问题。我认为它可以更好地与整个MVC结构集成。

我认为你的方法很合理。我可能想到的是以下

  1. 为您的元素提供一个名为helper的变量,就像在ZF1中一样。
  2. 创建自定义表单元素渲染器,该渲染器还将检查表单元素的渲染器属性,以决定如何渲染它。
  3. 您可以重复使用ViewHelperProviderInterface或创建自己的界面:

    class CustomElement implements ViewHelperProviderInterface
    {
         public function getViewHelperConfig()
         {
              return array('type' => '\My\View\Helper');
         }
    }
    

    class CustomElement implements FormHelperProviderInterface
    {
         public function getFormHelperConfig()
         {
              return '\My\View\Helper';
              // or
              return new My\View\Helper();
         }
    }
    

    然后在FormElement课程中,您可以执行以下操作:

        if ('week' == $type) {
            $helper = $renderer->plugin('form_week');
            return $helper($element);
        }
    
        if ($element instanceof THEINTERFACE) {
              return $renderer->plugin($element->getFormHelperConfig());
        }
    
        $helper = $renderer->plugin('form_input');
        return $helper($element);
    

    这可能是你想到的。

    你可能最好创建自己的界面,因为第一个界面已经有了某种意义,它可能会使某些人感到困惑。

    除此之外,每个模块只需要在模块配置中提供helper_map密钥,以便在使用MVC组件进行渲染时使用它的视图助手。