代码之家  ›  专栏  ›  技术社区  ›  Ben Hogan

ZF3-如何填写字段集集合索引中的Select元素

  •  0
  • Ben Hogan  · 技术社区  · 7 年前

    以下场景:
    我想通过Zend Framework 3表单编辑由3个字段(id、用户名和域)组成的表“accounts”。字段“domain”可以从一组域名中选择(这里是一个静态数组,用于简化操作)

    我有一个带有getters和setters的简单实体 AccountModel。php ':

    namespace Project\Model;
    
    class AccountModel {
    
        private $id;
    
        private $userName;
    
        private $domain;
    
        public function getUserName(){
            return $this->userName;
        }
        public function setUserName(string $userName){
            $this->userName = $userName;
            return $this;
        }
        public function getId() {
            return $this->id;
        }
        public function setId(int $id) {
            $this->id = $id;
            return $this;
        }
        public function getDomain() {
            return $this->domain;
        }
        public function setDomain($domain) {
            $this->domain = $domain;
            return $this;
        }
    }
    

    以及相应的字段集' AccountFieldset。php ':

    namespace Project\Form;
    
    use Zend\Form\Fieldset;
    use Zend\InputFilter\InputFilterProviderInterface;
    use Zend\Form\Element\Text;
    use Zend\Form\Element\Select;
    use Project\Model\AccountModel;
    use Zend\Hydrator\ClassMethods;
    
    
    class AccountFieldset extends Fieldset implements 
        InputFilterProviderInterface {
        const NAME_ID = 'id';
        const NAME_USERNAME = 'username';
        const NAME_DOMAIN = 'domain';
    
        private $domainsOptionValues = [ ];
    
        public function __construct(array $domains, $name = null, $options = null) {
            parent::__construct ( isset ( $name ) ? $name : 'account-fieldset', $options );
    
            $this->setHydrator ( new ClassMethods ( false ) )->setObject ( new AccountModel () );
            $this->domainsOptionValues = $domains;
        }
        public function init() {
            $this->add ( [ 
                'name' => self::NAME_ID,
                'type' => Text::class,
                'options' => [ 
                        'label' => 'Id' 
                ],
                'attributes' => [ 
                        'required' => 'required',
                        'readonly' => true 
                ] 
            ] );
            $this->add ( [ 
                    'name' => self::NAME_USERNAME,
                    'type' => Text::class,
                    'options' => [ 
                            'label' => 'Username' 
                    ],
                    'attributes' => [ 
                            'required' => 'required' 
                    ] 
            ] );
            $this->add ( [ 
                    'name' => self::NAME_DOMAIN,
                    'type' => Select::class,
                    'options' => [ 
                            'label' => 'Domains',
                            'value_options' => $this->domainsOptionValues 
                    ],
                    'attributes' => [ 
                            'required' => 'required' 
                    ] 
            ] );
        }
    
        public function getInputFilterSpecification() {
            return [ 
                    /**  some InputFilterSpecifications **/
            ];
        }
    }
    

    此字段集将用于表示表' AccountTableFieldset。php ':

    namespace Project\Form;
    
    use Project\Model\AccountsTableModel;
    use Zend\Form\Fieldset;
    use Zend\Hydrator\ClassMethods;
    use Zend\InputFilter\InputFilterProviderInterface;
    
    class AccountsTableFieldset extends Fieldset implements InputFilterProviderInterface {
    
        const NAME_ACCOUNTS = 'accounts';
    
        public function __construct($name = null, $options = []) {
            $name = isset ( $name ) ? $name : 'accounts-table';
            parent::__construct ( $name, $options );
    
            $this->setHydrator ( new ClassMethods ( false ) )->setObject ( new AccountsTableModel () );
    
            $this->add ( [ 
                    'type' => 'Zend\Form\Element\Collection',
                    'name' => 'accounts',
                    'options' => [ 
                            'label' => 'Accounts',
                            'count' => 1,   
                            'should_create_template' => false,
                            'allow_add' => false,
                            'target_element' => [ 
                                    'type' => AccountFieldset::class,
                            ] 
                    ] 
            ] );
        }
    
        public function getInputFilterSpecification() {
            return [ 
                    /** some InputFilterSpecification **/
            ];
        }
    }
    

    没有什么特别的' AccountsTableModel。php ':

    namespace Project\Model;
    
    class AccountsTableModel {
    
        private $accounts = [];
    
        /**
         * @return AccountModel[] 
         */
        public function getAccounts() : array {
            return $this->accounts;
        }
        public function setAccounts(array $accounts) {
            $this->accounts = $accounts;
            return $this;
        }
    }
    

    我的表格是这样的 AccountForm。php ':

    namespace Project\Form;
    
    use Zend\Form\Form;
    use Zend\Form\Element\Submit;
    
    class AccountsForm extends Form {
    
        const NAME_TABLE = 'accounts-table';
        const NAME_SUBMIT= 'accounts-save';
    
        public function init(){
    
            $this->setName('accounts-form');
    
            $this->add([
                'name' => self::NAME_TABLE,
                'type' => AccountsTableFieldset::class,
                'attributes' => [
                        'class' => 'accounts',
                        'id' => 'accounts-table',
                ],
            ]);                
    
            $this->add([
                        'name' => self::NAME_SUBMIT,
                        'type' => Submit::class,
                        'attributes' => [
                                'class' => 'btn btn-success',
                                'value' => 'Save accounts',
                                'id' => 'accounts-save',
                        ],
            ]);
        }   
    }
    

    此表单通过' AccountsFormFactory。php ':

    namespace Project\Factory;
    
    use Interop\Container\ContainerInterface;
    use Project\Form\AccountsForm;
    use Zend\ServiceManager\Factory\FactoryInterface;
    
    class AccountsFormFactory implements FactoryInterface{
    
        public function __invoke(ContainerInterface $container){
            $accountsForm = $container->get('FormElementManager')->get(AccountsForm::class);
            $accountsForm->init();
    
            return $accountsForm;
        }
    }
    

    为了填充AccountFieldset中的Select元素,我创建了“**AccountFieldsetFactory”。php**':

    namespace Project\Factory;
    
    use Interop\Container\ContainerInterface;
    use Project\Form\AccountFieldset;
    
    class AccountFieldsetFactory {
        public function __invoke(ContainerInterface $container){
            $domains = [
                '1' => 'example1.com',
                '2' => 'example2.com',
                '3' => 'example3.com',
            ];
            $accountsFieldset = new AccountFieldset($domains);
            $accountsFieldset->init();
    
            // die(__FILE__ . ' #'. __LINE__);
            return $accountsFieldset;
        }
    }
    

    注意,我让它死了。但遗憾的是,由于ElementFactory直接调用AccountFieldset,所以这条线从未到达。此时,我得到一个错误:

    Uncaught TypeError: Argument 1 passed to Project\\Form\\AccountFieldset::__construct() must be of the type array, string given, called in /var/www/html/zf3.local/vendor/zendframework/zend-form/src/ElementFactory.php on line 70
    

    为什么调用ElementFactory而不是我的AccountFieldsetFactory?我将“form\u元素”的“工厂”配置如下: ConfigProvider。php ':

    namespace MailManager;
    
    use Zend\Db\Adapter;
    use Project\Factory\FormElementManagerDelegatorFactory;
    
    class ConfigProvider {
        public function __invoke(){
    
            return [
                    'dependencies'  => $this->getDependencies(),
                    'routes'        => $this->getRoutes(),
                    'templates'     => $this->getTemplates(),
                    'form_elements' => [
                         'factories' => [
                            Form\AccountFieldset::class => Factory\AccountFieldsetFactory::class,
                         ]
                ]
            ];
        }
    
        public function getDependencies(){
            return [
                    'factories' => [
                            Action\AccountsAction::class => Factory\AccountsActionFactory::class,
    
                            Repository\AccountsRepositoryInterface::class => Factory\AccountsRepositoryFactory::class,
    
                            Storage\AccountsStorageInterface::class => Factory\AccountsStorageFactory::class,
    
                            Form\AccountsForm::class => Factory\AccountsFormFactory::class,
                            Form\NewAccountForm::class => Factory\NewAccountFormFactory::class,
    
                            Adapter\AdapterInterface::class => Adapter\AdapterServiceFactory::class,
                    ],
                    'delegators' => [
                            'FormElementManager' => [
                                    FormElementManagerDelegatorFactory::class,
                            ],
                    ],
            ];
        }
    
        public function getRoutes(){
            return [
                    /** some routes **/
            ];
        }
    
        public function getTemplates(){
            return [
                    'paths' => [
                        /** some paths **/
                    ],
            ];
        }
    }
    

    感谢中的建议 Configure FormElementManager #387 FormElementManagerDelegatorFactory工作正常,AccountFieldsetFactory显示在FormElementManager的factory部分。但遗憾的是(如前所述),AccountFieldsetFactory从未被调用。我错过了什么?

    1 回复  |  直到 7 年前
        1
  •  0
  •   Ben Hogan    7 年前

    这个问题是由一个简单的疏忽引起的:AccountsTableFieldset直接在构造函数中添加accountfieldset的集合。这应该由init()方法完成。所以改变我的' AccountTableFieldset。php ':

    namespace Project\Form;
    
    use Project\Model\AccountsTableModel;
    use Zend\Form\Fieldset;
    use Zend\Hydrator\ClassMethods;
    use Zend\InputFilter\InputFilterProviderInterface;
    
    class AccountsTableFieldset extends Fieldset implements InputFilterProviderInterface {
    
        const NAME_ACCOUNTS = 'accounts';
    
        public function __construct($name = null, $options = []) {
            $name = isset ( $name ) ? $name : 'accounts-table';
            parent::__construct ( $name, $options );
    
            $this->setHydrator ( new ClassMethods ( false ) )->setObject ( new AccountsTableModel () );
        }
    
        public function init() {
            $this->add ( [ 
                    'type' => 'Zend\Form\Element\Collection',
                    'name' => 'accounts',
                    'options' => [ 
                            'label' => 'Accounts',
                            'count' => 1,   
                            'should_create_template' => false,
                            'allow_add' => false,
                            'target_element' => [ 
                                    'type' => AccountFieldset::class,
                            ] 
                    ] 
            ] );
        }
    
        public function getInputFilterSpecification() {
            return [ 
                    /** some InputFilterSpecification **/
            ];
        }
    }