Intereting Posts
Maxmind geolocation apis: Apache vs PHP Как получить долготу широты нескольких адресов геолокации Как увеличить счетчик в строке замены при использовании preg_replace? Laravel, sync () – как синхронизировать массив, а также передавать дополнительные поля поворота? как динамически проверять количество аргументов функции в php почему Пытается получить свойство ошибки, отличной от объекта Полностью удалить phpMyAdmin Получить доменное имя (не субдомен) в php Лучший способ создания абсолютного пути в PHP? (См. 3 метода, перечисленных внутри) Перезагрузка jqGrid не работает Что более эффективно, строковые функции PHP или регулярное выражение в PHP? mysql_fetch_assoc (): предоставленный аргумент не является допустимым ресурсом результата MySQL в php Автосохранение ввода формы с использованием прототипа и PHP Как я могу безопасно обнаруживать SSL в CakePHP за обратным прокси-сервером nginx? Как преобразовать .wav в .mp3 файл в php

Как создать входы / элементы формы в ZF2

EDIT: Мой главный вопрос теперь стал «Как я могу получить ServiceManager с менеджером сущностей доктрины в руки моей формы, элемента и классов ввода каким-то чистым способом?» Читайте дальше, чтобы увидеть полный пост.

Я собираюсь попробовать и попросить пример здесь, так что медведь со мной. Сообщите мне, где я ошибаюсь / прав, или где я мог бы улучшить

Я пытаюсь создать регистрационную форму. Я мог бы использовать модуль ZfcUser, но я хочу сделать это самостоятельно. Я также использую ZF2 с Doctrine2, чтобы немного отвести меня от этого модуля.

Моя стратегия заключалась в том,

  1. Создать класс формы, называемый регистрационной формой

  2. Создайте отдельные классы «element» для каждого элемента, где каждый элемент будет иметь спецификацию ввода

  3. Поскольку каждый элемент представляет собой отдельный класс из формы, я могу отдельно тестировать каждый.

Все выглядело прекрасно, пока я не захотел добавить валидатор к элементу имени пользователя, который будет проверять, что имя пользователя НЕ используется.

Вот код до сих пор

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\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; } } } , 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 приложения.

Я получаю эту ошибку для части Validator, хотя, если я вызываю тот же метод 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 каким-то другим способом, который я не вижу?

это мой валидатор, используя статический метод для внедрения 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; } } 

то есть. я использую его для этих элементов формы, которые также проверены и работают нормально:

 <?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\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; } } 

при редактировании существующего пользователя

 <?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; } }