編集:私の主な質問は、「教義エンティティマネージャーを備えたServiceManagerをフォーム、要素、および入力クラスにクリーンな方法で渡すにはどうすればよいですか?」になりました。投稿全体を見るには読んでください。
ここで例を挙げて質問しますので、ご容赦ください。どこが間違っている/正しいのか、どこを改善できるのか教えてください
登録フォームを作成しようとしています。ZfcUserモジュールを使用することもできますが、これを自分で実行したいと思います。Doctrine2でZF2も使用しているので、そのモジュールから少し離れています。
私の戦略はこれでした、
登録フォームと呼ばれるフォームクラスを作成します
要素ごとに個別の「要素」クラスを作成します。各要素には入力仕様があります。
各要素はフォームとは別のクラスであるため、それぞれを個別に単体テストできます。
ユーザー名がまだ使用されていないことを確認するバリデーターをユーザー名要素に追加するまでは、すべて問題ないように見えました。
これまでのコードは次のとおりです
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'
)
) );
}
}
必要ないと思うものをたくさん削除しました。これが私のusername要素です。
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;
}
}
なぜこれが私にとって良い考えのように思えたのですか?
必要に応じてアプリケーション全体で要素を個別に再利用できるため、テスト容易性/再利用の選択としては適切であるように思われました。
各要素によって生成された各入力を単体テストして、入力を正しく受け入れる/拒否することを確認できます。
これは、要素の単体テストの例です
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です。これが発生する理由は、バリデーターがアプリケーション全体のServiceManagerにアクセスするのではなく、ServiceManagerとしてValidatorPluginManagerにのみアクセスできるためです。
バリデーター部分でこのエラーが発生しますが、一般的なサービスマネージャーで同じ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;
}
)
しかし、アプリケーションレベルのサービスマネージャーに相談していないため、機能しませんでした。
だから、全体的に、ここに私の質問があります:
フォームと要素を分離するこの戦略は良いものですか?私はこのように続けるべきですか?代替手段は何ですか?(私はテスト容易性のためにものを分割するためのものです)私は元々すべての入力の組み合わせでフォーム自体だけをテストするつもりでしたが、私はやりすぎのようでした。
上記の問題を解決するにはどうすればよいですか?
Zendのフォーム/要素/入力部分を、表示されていない他の方法で使用する必要がありますか?