Zend framework2 zend framework 2/3集合集自定义属性

Zend framework2 zend framework 2/3集合集自定义属性,zend-framework2,customization,zend-framework3,zend-form-fieldset,zend-form-collection,Zend Framework2,Customization,Zend Framework3,Zend Form Fieldset,Zend Form Collection,从这些问题来判断: 我想没有一个好方法来定制集合的元素。 例如,拥有以下集合: //path: MyModule\Form\MyFieldset public function __construct($name = null) { parent::__construct('myFieldset'); $this->add([ 'name'=>'test', 'type' => Element\Collection::c

从这些问题来判断:

我想没有一个好方法来定制集合的元素。

例如,拥有以下集合:

//path: MyModule\Form\MyFieldset

public function __construct($name = null) {

    parent::__construct('myFieldset');

    $this->add([
        'name'=>'test',
        'type' => Element\Collection::class,
        'options' => [
            'label' => 'MyCollection',
            'count' => 6,
            'should_create_template' => true,
            'target_element' => new Element\Text()
        ],
    ]);
}
然后执行一些操作,以便为每个文本元素和/或自动编号的标签定义(此处,在当前类中)自定义属性,然后输出(只需调用zend helper FormCollection,而无需任何自定义视图帮助器):


文本元素n°1
文本元素编号2
[...]
我错了吗?


(我问这个问题是因为我找到了一个很好的解决方案,也许发布它会有所帮助)

target\u元素必须引用一个字段集。这可以是集合所在表单中的新实例,也可以是类名

例如:

$fieldset = new Fieldset();
$fieldset->add([
    'name' => 'some_field_name',
    'type' => 'text',
]);
$this->add([
    'name'=>'test',
    'type' => Element\Collection::class,
    'options' => [
        'label' => 'MyCollection',
        'count' => 6,
        'should_create_template' => true,
        'target_element' => $fieldset
    ],
]);
namespace Module\Form;
use Zend\Form\Fieldset;

class MyFieldset extends Fieldset {
    public static $instance_count = 1;

    public function __construct() {
        parent::__construct();

        $this->add([
            'name' => 'question',
            'type' => 'text',
            'attributes' => [
                'alt' => 'input' . MyFieldset::$instance_count,
            ],
            'options' => [
                'label' => 'Text element No ' . MyFieldset::$instance_count,
            ],
        ]);
        MyFieldset::$instance_count++;
    }
}

在为每个输入定制标签方面,我还没有找到一种方法

不太确定集合的内部工作方式,但我怀疑它会根据需要创建尽可能多的
target\u元素的新实例。就向标签(或任意属性)添加数字而言,您可以使用以
1
开头的静态属性创建一个fieldset类,将其添加到标签并增加其值

例如:

$fieldset = new Fieldset();
$fieldset->add([
    'name' => 'some_field_name',
    'type' => 'text',
]);
$this->add([
    'name'=>'test',
    'type' => Element\Collection::class,
    'options' => [
        'label' => 'MyCollection',
        'count' => 6,
        'should_create_template' => true,
        'target_element' => $fieldset
    ],
]);
namespace Module\Form;
use Zend\Form\Fieldset;

class MyFieldset extends Fieldset {
    public static $instance_count = 1;

    public function __construct() {
        parent::__construct();

        $this->add([
            'name' => 'question',
            'type' => 'text',
            'attributes' => [
                'alt' => 'input' . MyFieldset::$instance_count,
            ],
            'options' => [
                'label' => 'Text element No ' . MyFieldset::$instance_count,
            ],
        ]);
        MyFieldset::$instance_count++;
    }
}

我找到的解决方案与Richard Parnaby King提供的解决方案有一些共同之处:

目标元素必须引用字段集

但是,与设置克隆计数器相反,它扩展了
Zend\Form\Fieldset方法prepareElement

基本应用:

namespace Module\Form;

use Zend\Form\Fieldset;
use Zend\Form\FormInterface; //needed in order to call prepareElement method

class MyFieldset extends Fieldset {


    public function __construct($name = null) {
        parent::__construct($name);

        $this->add([
            'name' => 'question',
            'type' => 'text',
            'attributes' => [
                'alt' => 'input',
            ],
            'options' => [
                'label' => 'Text',
            ],
        ]);

    }//construct

    public function prepareElement(FormInterface $form){

        parent::prepareElement($form);

        $name = $this->getName(); 
        //Do stuff related to this fieldset

        foreach ($this->iterator as $elementOrFieldset) {

           $elementName=$elementOrFieldset->getName()
           //Do stuff related to this fieldset children
        }

    }//prepareElement

}
特点:

  • 自动编号标签和/或属性
  • 允许有条件地分配属性/标签
  • 允许不同元素之间的交互(例如:将元素A id作为目标传递给元素B)
  • 使用模板


  • 由于这个解决方案可以通过多种方式开发,我准备了一个完整的演示,可以运行和探索

    注意:此演示不是最佳的实现,而是一组示例,可以得出一个结果。:-)

    本示例旨在使用前缀“Bob”在默认模块“Application”下运行,以避免与其他文件发生冲突(我想象有人可能已经有一个名为TestController的文件,但我猜没有人有一个名为BobController的文件)

    然后,如果您完全按照下面的步骤操作,您应该能够毫无问题地运行和探索演示

    prepareElement
    方法在
    BobFieldset
    类中的实现可能看起来很庞大,但这只是注释、空格和示例的问题。根据您的需要,它可能非常小

    第1步:

    namespace Module\Form;
    
    use Zend\Form\Fieldset;
    use Zend\Form\FormInterface; //needed in order to call prepareElement method
    
    class MyFieldset extends Fieldset {
    
    
        public function __construct($name = null) {
            parent::__construct($name);
    
            $this->add([
                'name' => 'question',
                'type' => 'text',
                'attributes' => [
                    'alt' => 'input',
                ],
                'options' => [
                    'label' => 'Text',
                ],
            ]);
    
        }//construct
    
        public function prepareElement(FormInterface $form){
    
            parent::prepareElement($form);
    
            $name = $this->getName(); 
            //Do stuff related to this fieldset
    
            foreach ($this->iterator as $elementOrFieldset) {
    
               $elementName=$elementOrFieldset->getName()
               //Do stuff related to this fieldset children
            }
    
        }//prepareElement
    
    }
    
    编辑文件:Application\config\module.config.php

    //add bob route to router
    
    'router' => [
            'routes' => [
    
                'bob' => [
                    'type' => Literal::class,
                    'options' => [
                        'route'    => '/bob',
                        'defaults' => [
                            'controller' => Controller\BobController::class,
                            'action'     => 'index',
                        ],
                    ],
                ],
    
                [...]
    
    //add BobController
    
     'controllers' => [
            'factories' => [
                [...]
                Controller\BobController::class => InvokableFactory::class,
            ],
        ],
    
    第二步:

    namespace Module\Form;
    
    use Zend\Form\Fieldset;
    use Zend\Form\FormInterface; //needed in order to call prepareElement method
    
    class MyFieldset extends Fieldset {
    
    
        public function __construct($name = null) {
            parent::__construct($name);
    
            $this->add([
                'name' => 'question',
                'type' => 'text',
                'attributes' => [
                    'alt' => 'input',
                ],
                'options' => [
                    'label' => 'Text',
                ],
            ]);
    
        }//construct
    
        public function prepareElement(FormInterface $form){
    
            parent::prepareElement($form);
    
            $name = $this->getName(); 
            //Do stuff related to this fieldset
    
            foreach ($this->iterator as $elementOrFieldset) {
    
               $elementName=$elementOrFieldset->getName()
               //Do stuff related to this fieldset children
            }
    
        }//prepareElement
    
    }
    
    创建文件:Application\src\Controller\BobController.php

    <?php
    
    namespace Application\Controller;
    
    use Zend\Mvc\Controller\AbstractActionController;
    use Zend\View\Model\ViewModel;
    
    use Application\Form\BobForm;
    
    class BobController extends AbstractActionController
    {
        public function __construct(){}
    
        public function indexAction()
        {
    
         $form = new BobForm('album');
    
         $request = $this->getRequest();
    
    
         if( $request->isPost()){
    
           $form->setInputFilter($form->getInputFilter());
           $form->setData($request->getPost());
    
            if (! $form->isValid()) {
                return ['form' => $form];
            }
         }
         return ['form' => $form];
        }
    }
    

    我刚刚意识到还有另一个更好、更灵活的解决方案:扩展collection元素(为什么我以前没有尝试过呢?)

    这种方法的主要优点是不需要拆分元素的名称:“克隆号”(
    [0]、[1]、…
    )可以直接访问

    功能:

    namespace Module\Form;
    
    use Zend\Form\Fieldset;
    use Zend\Form\FormInterface; //needed in order to call prepareElement method
    
    class MyFieldset extends Fieldset {
    
    
        public function __construct($name = null) {
            parent::__construct($name);
    
            $this->add([
                'name' => 'question',
                'type' => 'text',
                'attributes' => [
                    'alt' => 'input',
                ],
                'options' => [
                    'label' => 'Text',
                ],
            ]);
    
        }//construct
    
        public function prepareElement(FormInterface $form){
    
            parent::prepareElement($form);
    
            $name = $this->getName(); 
            //Do stuff related to this fieldset
    
            foreach ($this->iterator as $elementOrFieldset) {
    
               $elementName=$elementOrFieldset->getName()
               //Do stuff related to this fieldset children
            }
    
        }//prepareElement
    
    }
    
  • 自动编号标签和/或属性
  • 允许有条件地分配属性/标签
  • 允许不同元素之间的交互(有限,请参阅下面的问题)
  • 使用模板(使用占位符->),无需检查索引是否为数字(这是我的另一个解决方案的问题)
  • 目标元素可以是一个简单的元素,无需实现
    Zend/Form/Fieldset
  • 问题:

    namespace Module\Form;
    
    use Zend\Form\Fieldset;
    use Zend\Form\FormInterface; //needed in order to call prepareElement method
    
    class MyFieldset extends Fieldset {
    
    
        public function __construct($name = null) {
            parent::__construct($name);
    
            $this->add([
                'name' => 'question',
                'type' => 'text',
                'attributes' => [
                    'alt' => 'input',
                ],
                'options' => [
                    'label' => 'Text',
                ],
            ]);
    
        }//construct
    
        public function prepareElement(FormInterface $form){
    
            parent::prepareElement($form);
    
            $name = $this->getName(); 
            //Do stuff related to this fieldset
    
            foreach ($this->iterator as $elementOrFieldset) {
    
               $elementName=$elementOrFieldset->getName()
               //Do stuff related to this fieldset children
            }
    
        }//prepareElement
    
    }
    
  • 设置ID可能有问题,因为(2)

  • 从扩展脚本无法访问最终元素的名称(例如:
    fieldset[subfieldset][0][elementName]
    ),因为稍后将按层次结构构建它


  • 工作原理:

    namespace Module\Form;
    
    use Zend\Form\Fieldset;
    use Zend\Form\FormInterface; //needed in order to call prepareElement method
    
    class MyFieldset extends Fieldset {
    
    
        public function __construct($name = null) {
            parent::__construct($name);
    
            $this->add([
                'name' => 'question',
                'type' => 'text',
                'attributes' => [
                    'alt' => 'input',
                ],
                'options' => [
                    'label' => 'Text',
                ],
            ]);
    
        }//construct
    
        public function prepareElement(FormInterface $form){
    
            parent::prepareElement($form);
    
            $name = $this->getName(); 
            //Do stuff related to this fieldset
    
            foreach ($this->iterator as $elementOrFieldset) {
    
               $elementName=$elementOrFieldset->getName()
               //Do stuff related to this fieldset children
            }
    
        }//prepareElement
    
    }
    
    1。扩展系列

    //file: Application\src\Form\Element\ExtendedCollection.php
    <?php
    
    namespace Application\Form\Element;
    
    use Zend\Form\Element\Collection;
    
    class ExtendedCollection extends Collection
    {
        protected $autonumbering_callback = false;
        protected $autonumbering_callback_options = [];  
    
        public function setOptions($options)
        {
            parent::setOptions($options);
    
            if (isset($options['autonumbering_callback'])) {
                $this->autonumbering_callback=(isset($options['autonumbering_callback'][0])) ? $options['autonumbering_callback'][0] : $options['autonumbering_callback'];
                $this->autonumbering_callback_options=(isset($options['autonumbering_callback'][1])) ? $options['autonumbering_callback'][1] : [];
            }
    
            return $this;
        }
    
        protected function addNewTargetElementInstance($key)
        {
    
            //Original instructions
            $this->shouldCreateChildrenOnPrepareElement = false;
    
            $elementOrFieldset = $this->createNewTargetElementInstance();
            $elementOrFieldset->setName($key);
    
            $this->add($elementOrFieldset);
    
            if (! $this->allowAdd && $this->count() > $this->count) {
                throw new Exception\DomainException(sprintf(
                    'There are more elements than specified in the collection (%s). Either set the allow_add option ' .
                    'to true, or re-submit the form.',
                    get_class($this)
                ));
            }
    
            //Callback
            if ($this->autonumbering_callback && method_exists(...$this->autonumbering_callback) && is_callable($this->autonumbering_callback)){
              call_user_func_array($this->autonumbering_callback,[$elementOrFieldset,$key,$this->autonumbering_callback_options]);
            }
    
            return $elementOrFieldset;
        }
    
    }
    
    //文件:Application\src\Form\Element\ExtendedCollection.php
    我已经提交了另一个(我相信更好的)解决方案。请检查一下,给我你的意见。
    
    //file: Application\src\Form\BobForm.php
    <?php
    
    namespace Application\Form;
    
    use Zend\Form\Form;
    use Zend\Form\Fieldset; //needed for myCallback3
    use Application\Form\Element\ExtendedCollection;
    
    class BobForm extends Form
    {
       private $inputFilter;
        public function __construct($name = null)
        {
            parent::__construct($name);
    
    
            $this->add([
                'name' => 'answer',
                'type' => ExtendedCollection::class,
                'options' => [
                    'count' =>3,
                    'should_create_template' => true,
                    'target_element' => new \Application\Form\BobFieldset2 ,
                    'autonumbering_callback'=>[
                                               [$this,'myCallback'],
                                               ['attributes'=>['title','data-something'],'whateverYouWant'=>'something',]
                                              ],
                    ],
            ]);
        }
    
        public function myCallback($elementOrFieldset, $key, $params){
    
          foreach($params['attributes'] as $attr){
            $autoNumAttr=str_replace('__num__',($key),$elementOrFieldset->getAttribute($attr));
            $elementOrFieldset->setAttribute($attr,$autoNumAttr);
          }//foreach
    
          $label = str_replace('__num__',($key+1),$elementOrFieldset->getLabel());
          $elementOrFieldset->setLabel($label);
        }
    
        public function myCallback2($elementOrFieldset, $key, $params){
    
          $char='a';
          foreach(range(1,$key) as $i) {
            if($key>0){$char++;}
          }
          $elementOrFieldset->setLabel('Answer '.$char);
        }
    
        public function myCallback3($elementOrFieldset, $key, $params, $isChild=null){
    
          if(!$isChild){$elementOrFieldset->setLabel('Answer '.($key+1));}
          else{$elementOrFieldset->setLabel($key);}
    
          //don't forget: use Zend\Form\Fieldset;
          if($elementOrFieldset instanceof Fieldset && !$isChild){
            $char='a';
            foreach($elementOrFieldset as $item){
              $this->myCallback3($item,($key+1 .$char++.') '),null,1);
            }
          }
        }
    }