如何创建ZF2表单输入/元素(How to create form inputs/elements

2019-07-31 11:23发布

编辑:现在我的主要问题已经成为“我如何以某种方式清洁主义实体管理器到我的形式,元素和输入类的手中的ServiceManager? 阅读上看到完整的文章。

我要去尝试,并通过实例让这里如此忍受我。 让我知道我要去哪里错了/右或在那里我可以提高

我试图创建一个登记表。 我可以用ZfcUser模块,但我想这样做我自己。 我使用ZF2与Doctrine2以及因此导致我从该模块远了一点。

我的策略是这样,

  1. 创建一个名为登记表窗体类

  2. 对于每个元素,其中每个元素将具有输入规范创建单独的“元素”的类

  3. 由于每个元件是从表单单独I类可以单独单元测试每一个。

一切似乎都很好,直到我想一个验证添加到会检查用户名不是就是使用尚未我的用户名元素。

下面是代码迄今

namepsace My\Form;

use Zend\Form\Form,
    Zend\Form\Element,
    Zend\InputFilter\Input,
    Zend\InputFilter\InputFilter,

/**
 * Class name : Registration
 */
class Registration
    extends Form
{

    const USERNAME     = 'username';
    const EMAIL        = 'email';
    const PASSWORD     = 'password';
    const PASS_CONFIRM = 'passwordConfirm';
    const GENDER       = 'gender';
    const CAPTCHA      = 'captcha';
    const CSRF         = 'csrf';
    const SUBMIT       = 'submit';

    private $captcha = 'dumb';

    public function prepareForm()
    {
        $this->setName( 'registration' );

        $this->setAttributes( array(
            'method' => 'post'
        ) );

        $this->add( array(
            'name'       => self::USERNAME,
            'type'       => '\My\Form\Element\UsernameElement',
            'attributes' => array(
                'label'     => 'Username',
                'autofocus' => 'autofocus'
            )
            )
        );

        $this->add( array(
            'name'       => self::SUBMIT,
            'type'       => '\Zend\Form\Element\Submit',
            'attributes' => array(
                'value' => 'Submit'
            )
        ) );

    }

}

我删除了很多,我认为是没有必要的。 这里是低于我的用户名元素。

namespace My\Form\Registration;

use My\Validator\UsernameNotInUse;
use Zend\Form\Element\Text,
    Zend\InputFilter\InputProviderInterface,
    Zend\Validator\StringLength,
    Zend\Validator\NotEmpty,
    Zend\I18n\Validator\Alnum;

/**
 *
 */
class UsernameElement
    extends Text
    implements InputProviderInterface
{

    private $minLength = 3;
    private $maxLength = 128;

    public function getInputSpecification()
    {
        return array(
            'name'     => $this->getName(),
            'required' => true,
            'filters'  => array(
                array( 'name'       => 'StringTrim' )
            ),
            'validators' =>
            array(
                new NotEmpty(
                    array( 'mesages' =>
                        array(
                            NotEmpty::IS_EMPTY => 'The username you provided is blank.'
                        )
                    )
                ),
                new AlNum( array(
                    'messages' => array( Alnum::STRING_EMPTY => 'The username can only contain letters and numbers.' )
                    )
                ),
                new StringLength(
                    array(
                        'min'      => $this->getMinLength(),
                        'max'      => $this->getMaxLength(),
                        'messages' =>
                        array(
                            StringLength::TOO_LONG  => 'The username is too long. It cannot be longer than ' . $this->getMaxLength() . ' characters.',
                            StringLength::TOO_SHORT => 'The username is too short. It cannot be shorter than ' . $this->getMinLength() . ' characters.',
                            StringLength::INVALID   => 'The username is not valid.. It has to be between ' . $this->getMinLength() . ' and ' . $this->getMaxLength() . ' characters long.',
                        )
                    )
                ),
                array(
                    'name'    => '\My\Validator\UsernameNotInUse',
                    'options' => array(
                        'messages' => array(
                            UsernameNotInUse::ERROR_USERNAME_IN_USE => 'The usarname %value% is already being used by another user.'
                        )
                    )
                )
            )
        );
    }    
}

现在,这里是我的验证

namespace My\Validator;

use My\Entity\Helper\User as UserHelper,
    My\EntityRepository\User as UserRepository;
use Zend\Validator\AbstractValidator,
    Zend\ServiceManager\ServiceManagerAwareInterface,
    Zend\ServiceManager\ServiceLocatorAwareInterface,
    Zend\ServiceManager\ServiceManager;

/**
 *
 */
class UsernameNotInUse
    extends AbstractValidator
    implements ServiceManagerAwareInterface
{

    const ERROR_USERNAME_IN_USE = 'usernameUsed';

    private $serviceManager;

    /**
     *
     * @var UserHelper
     */
    private $userHelper;
    protected $messageTemplates = array(
        UsernameNotInUse::ERROR_USERNAME_IN_USE => 'The username you specified is being used already.'
    );

    public function isValid( $value )
    {
        $inUse = $this->getUserHelper()->isUsernameInUse( $value );
        if( $inUse )
        {
            $this->error( UsernameNotInUse::ERROR_USERNAME_IN_USE, $value );
        }

        return !$inUse;
    }

    public function setUserHelper( UserHelper $mapper )
    {
        $this->userHelper = $mapper;
        return $this;
    }

    /**
     * @return My\EntityRepository\User
     */
    public function getUserHelper()
    {
        if( $this->userHelper == null )
        {
            $this->setUserHelper( $this->getServiceManager()->get( 'doctrine.entitymanager.orm_default' )->getObjectRepository( 'My\Entity\User') );
        }
        return $this->userHelper;
    }

    public function setServiceManager( ServiceManager $serviceManager )
    {
        echo get_class( $serviceManager );
        echo var_dump( $serviceManager );
        $this->serviceManager = $serviceManager;
        return $this;
    }

    /**
     *
     * @return ServiceManager
     */
    public function getServiceManager( )
    {
        return $this->serviceManager;
    }

}

为什么这似乎是个好主意吗?

  1. 这似乎是一个很好的可测性/再利用的抉择,因为我可以单独重新使用的元素在我的应用程序如果需要的话。

  2. 我能单元测试由每个元件产生的每个输入,以确保它正确地接受/拒绝输入。

这是我的元件的单元测试的示例

public function testFactoryCreation()
{
    $fac = new Factory();

    $element = $fac->createElement( array(
        'type' => '\My\Form\Registration\UsernameElement'
        ) );
    /* @var $element \My\Form\Registration\UsernameElement  */

    $this->assertInstanceOf( '\My\Form\Registration\UsernameElement',
                             $element );

    $input      = $fac->getInputFilterFactory()->createInput( $element->getInputSpecification() );
    $validators = $input->getValidatorChain()->getValidators();
    /* @var $validators \Zend\Validator\ValidatorChain */

    $expectedValidators = array(
        'Zend\Validator\StringLength',
        'Zend\Validator\NotEmpty',
        'Zend\I18n\Validator\Alnum',
        'My\Validator\UsernameNotInUse'
    );

    foreach( $validators as $validator )
    {
        $actualClass = get_class( $validator['instance'] );
        $this->assertContains( $actualClass, $expectedValidators );

        switch( $actualClass )
        {
            case 'My\Validator\UsernameNotInUse':
                $helper = $validator['instance']->getUserHelper();
                //HAVING A PROBLEM HERE
                $this->assertNotNull( $helper );
                break;

            default:

                break;
        }
    }

}

我遇到的问题是,验证无法正常获取UserHelper,这是真正从学说UserRepository。 出现这种情况的原因是因为验证只获得访问ValidatorPluginManager作为的ServiceManager,而不是访问应用广泛的ServiceManager。

我得到这个错误的验证部分,但如果我调用相同的get方法在一般服务经理这没有问题的作品。

1) Test\My\Form\Registration\UsernameElementTest::testFactoryCreation
Zend\ServiceManager\Exception\ServiceNotFoundException: Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for doctrine.entitymanager.orm_default

在验证程序的var_dump($的ServiceManager)显示我是类ValidatorPluginManager的。

我试图把一个厂,像这样的service_manager进入

'service_manager' => array(
                'factories' => array(
                    'My\Validator\UsernameNotInUse' => function( $sm )
                    {
                        $validator = new \My\Validator\UsernameNotInUse();
                        $em        = $serviceManager->get( 'doctrine.entitymanager.orm_default' );
                        /* @var $em \Doctrine\ORM\EntityManager */
                        $validator->setUserHelper( $em->getRepository( '\My\Entity\User' ) );

                        return $validator;
                    }
                )

但没有工作,因为它没有咨询应用级服务经理。

因此,总体而言,这里是我的问题:

  1. 是分离的形式和元素一个很好的的策略呢? 我应该继续下去这种方式? 什么是替代品? (我打破的东西了可测试性的缘故),我要与所有的输入组合原本只测试形式本身,而是它似乎像我会尝试做太多。

  2. 如何解决我上面的问题?

  3. 我是否应该使用Zend的表格/元/输入部分在我没有看到一些其他的方式?

Answer 1:

这是我的验证,使用一个静态方法来注入的EntityManager与任何doctine实体工作。

<?php

namespace Base\Validator;

use Traversable;
use Zend\Stdlib\ArrayUtils;
use Zend\Validator\AbstractValidator;
use Doctrine\ORM\EntityManager;

class EntityUnique extends AbstractValidator
{
    const EXISTS = 'exists';

    protected $messageTemplates = array(
        self::EXISTS => "A %entity% record already exists with %attribute% %value%",
    );

    protected $messageVariables = array(
        'entity' => '_entity',
        'attribute' => '_attribute',
    );


    protected $_entity;
    protected $_attribute;
    protected $_exclude;

    protected static $_entityManager;

    public static function setEntityManager(EntityManager $em) {

        self::$_entityManager = $em;
    }

    public function getEntityManager() {

        if (!self::$_entityManager) {

            throw new \Exception('No entitymanager present');
        }

        return self::$_entityManager;
    }

    public function __construct($options = null)
    {
        if ($options instanceof Traversable) {
            $options = ArrayUtils::iteratorToArray($token);
        }

        if (is_array($options)) {

            if (array_key_exists('entity', $options)) {

                $this->_entity = $options['entity'];
            }

            if (array_key_exists('attribute', $options)) {

                $this->_attribute = $options['attribute'];
            }

            if (array_key_exists('exclude', $options)) {

                if (!is_array($options['exclude']) ||
                    !array_key_exists('attribute', $options['exclude']) ||
                    !array_key_exists('value', $options['exclude'])) {

                    throw new \Exception('exclude option must contain attribute and value keys');
                }

                $this->_exclude = $options['exclude'];
            }
        }

        parent::__construct(is_array($options) ? $options : null);
    }

    public function isValid($value, $context = null)
    {
        $this->setValue($value);

        $queryBuilder = $this->getEntityManager()
            ->createQueryBuilder()
            ->from($this->_entity, 'e')
            ->select('COUNT(e)')
            ->where('e.'. $this->_attribute . ' = :value')
            ->setParameter('value', $this->getValue());

        if ($this->_exclude) {

            $queryBuilder = $queryBuilder->andWhere('e.'. $this->_exclude['attribute'] . ' != :exclude')
                ->setParameter('exclude', $this->_exclude['value']);
        }

        $query = $queryBuilder->getQuery();        
        if ((integer)$query->getSingleScalarResult() !== 0) {

            $this->error(self::EXISTS);
            return false;
        }

        return true;
    }
}

即。 我使用它对于也测试和精细的工作theese表单元素:

<?php

namespace User\Form\Element;

use Zend\Form\Element\Text;
use Zend\InputFilter\InputProviderInterface;

class Username extends Text implements InputProviderInterface
{
    public function __construct() {

        parent::__construct('username');
        $this->setLabel('Benutzername');
        $this->setAttribute('id', 'username');
    }

    public function getInputSpecification() {

        return array(
            'name' => $this->getName(),
            'required' => true,
            'filters'  => array(
                array(
                    'name' => 'StringTrim'
                ),
            ),
            'validators' => array(
                array(
                    'name' => 'NotEmpty',
                    'break_chain_on_failure' => true,
                    'options' => array(
                        'messages' => array(
                            'isEmpty' => 'Bitte geben Sie einen Benutzernamen ein.',
                        ),
                    ),
                ),
            ),
        );
    }
}

当创建一个新用户

<?php

namespace User\Form\Element;

use Zend\InputFilter\InputProviderInterface;
use User\Form\Element\Username;

class CreateUsername extends Username implements InputProviderInterface
{
    public function getInputSpecification() {

        $spec = parent::getInputSpecification();
        $spec['validators'][] = array(
            'name' => 'Base\Validator\EntityUnique',
            'options' => array(
                'message' => 'Der name %value% ist bereits vergeben.',
                'entity' => 'User\Entity\User',
                'attribute' => 'username',  
            ),    
        );

        return $spec;
    }
}

editin现有用户时

<?php

namespace User\Form\Element;

use Zend\InputFilter\InputProviderInterface;
use User\Form\Element\Username;

class EditUsername extends Username implements InputProviderInterface
{
    protected $_userId;

    public function __construct($userId) {

        parent::__construct();
        $this->_userId = (integer)$userId;
    }

    public function getInputSpecification() {

        $spec = parent::getInputSpecification();
        $spec['validators'][] = array(
            'name' => 'Base\Validator\EntityUnique',
            'options' => array(
                'message' => 'Der name %value% ist bereits vergeben.',
                'entity' => 'User\Entity\User',
                'attribute' => 'username',
                'exclude' => array(
                    'attribute' => 'id',
                    'value' => $this->_userId,  
                ),
            ),    
        );

        return $spec;
    }
}


文章来源: How to create form inputs/elements in ZF2