EDIT: теперь мой главный вопрос: "Как я могу получить ServiceManager с менеджером сущностей доктрины в руки моего класса, элемента и классов ввода каким-то чистым способом?" Читайте дальше, чтобы увидеть полную запись.
Я собираюсь попробовать и попросить пример здесь, так что медведь со мной. Сообщите мне, где я ошибаюсь/прав, или где я мог бы улучшить
Я пытаюсь создать регистрационную форму. Я мог бы использовать модуль ZfcUser, но я хочу сделать это самостоятельно. Я также использую ZF2 с Doctrine2, чтобы немного отвести меня от этого модуля.
Моя стратегия была такой,
-
Создайте класс формы, называемый регистрационной формой
-
Создайте отдельные классы элементов для каждого элемента, где каждый элемент будет иметь спецификацию ввода
-
Поскольку каждый элемент является отдельным классом из формы, я могу unit test каждый отдельно.
Все казалось прекрасным, пока я не захотел добавить валидатор к элементу имени пользователя, который будет проверять, что имя пользователя НЕ используется.
Вот код до сих пор
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;
}
}
Почему это показалось мне хорошей идеей?
-
Это казалось хорошим выбором для проверки/повторного использования, поскольку я мог бы повторно использовать элементы отдельно в моем приложении, если это необходимо.
-
Я мог unit test каждый ввод, сгенерированный каждым элементом, чтобы убедиться, что он правильно принимает/отклоняет ввод.
Это пример моего unit test для элемента
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.
Я попытался помещать factory в запись 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;
}
)
но это не сработало, потому что он не консультировался с менеджером сервисов уровня приложения.
Итак, в целом, вот мои вопросы:
-
Является ли эта стратегия отличным от формы и элементов? Должен ли я продолжать идти этим путем? Какие альтернативы? (Я за то, чтобы ломать вещи ради проверки). Я собирался проверить ТОЛЬКО сама форма изначально с комбинацией ВСЕХ входов, но казалось, что я попытаюсь сделать слишком много.
-
Как решить проблему, которая у меня выше?
-
Должен ли я использовать части формы/элемента/ввода Zend каким-то другим способом, который я не вижу?