2012-09-09 21 views
11

EDIT: Câu hỏi chính của tôi giờ đây đã trở thành 'Làm cách nào để có được ServiceManager với trình quản lý thực thể học thuyết vào bàn tay của biểu mẫu, phần tử và lớp đầu vào của tôi một cách rõ ràng?' Đọc để xem toàn bộ bài đăng.Cách tạo các đầu vào/phần tử biểu mẫu trong ZF2

Tôi sẽ cố gắng và yêu cầu bằng ví dụ ở đây để chịu đựng với tôi. Hãy cho tôi biết nơi tôi đang đi sai/phải hoặc nơi tôi có thể cải thiện

Tôi đang cố gắng tạo biểu mẫu đăng ký. Tôi có thể sử dụng mô-đun ZfcUser nhưng tôi muốn làm điều này một mình. Tôi đang sử dụng ZF2 với Doctrine2 cũng để dẫn tôi đi từ mô-đun đó một chút.

Chiến lược của tôi là điều này,

  1. Tạo một lớp mẫu gọi là hình thức đăng ký

  2. Tạo 'tố' các lớp riêng biệt cho mỗi phần tử trong đó mỗi phần tử sẽ có một đặc điểm kỹ thuật đầu vào

  3. Vì mỗi phần tử là một lớp riêng biệt từ biểu mẫu, tôi có thể kiểm tra từng phần tử một cách riêng biệt.

Tất cả có vẻ tốt cho đến khi tôi muốn thêm trình xác thực vào phần tử tên người dùng sẽ kiểm tra xem tên người dùng đó chưa được sử dụng hay chưa.

Đây là mã vậy, đến nay

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' 
      ) 
     )); 

    } 

} 

tôi loại bỏ rất nhiều điều mà tôi nghĩ là không cần thiết. Đây là phần tử tên người dùng của tôi bên dưới.

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.' 
         ) 
        ) 
       ) 
      ) 
     ); 
    }  
} 

Bây giờ đây là validator của tôi

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

} 

Tại sao điều này có vẻ như là một ý tưởng tốt với tôi?

  1. Nó có vẻ như một lựa chọn tốt/tái sử dụng để làm vì tôi có thể tái sử dụng các yếu tố riêng biệt trên ứng dụng của tôi nếu cần thiết.

  2. Tôi có thể kiểm tra từng đầu vào được tạo bởi từng yếu tố để đảm bảo đầu vào chấp nhận/từ chối đầu vào chính xác.

Đây là ví dụ về kiểm tra đơn vị của tôi cho các phần tử

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

} 

Vấn đề tôi đang gặp là validator không thể tìm nạp UserHelper đúng cách, mà thực sự là một UserRepository từ học thuyết . Lý do điều này xảy ra là do các trình xác nhận hợp lệ chỉ truy cập vào ValidatorPluginManager như một ServiceManager thay vì có quyền truy cập vào ServiceManager ứng dụng rộng.

Tôi gặp lỗi này cho phần Trình xác thực, mặc dù nếu tôi gọi cùng một phương thức nhận trên trình quản lý dịch vụ chung, nó hoạt động mà không có vấn đề gì.

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) trong trình xác nhận cho tôi biết đây là lớp ValidatorPluginManager.

Tôi đã thử đặt một nhà máy ở mục service_manager như vậy

'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; 
        } 
       ) 

nhưng điều đó không làm việc vì nó không có ý kiến ​​của quản lý dịch vụ mức ứng dụng.

Vì vậy, tổng thể, đây là những câu hỏi của tôi:

  1. là chiến lược này tách các hình thức và các yếu tố một tốt nhất? Tôi có nên tiếp tục theo cách này không? Lựa chọn thay thế là gì? (Tôi cho phá vỡ công cụ lên vì lợi ích của testability) Tôi sẽ thử nghiệm ONLY hình thức bản thân ban đầu với một sự kết hợp của tất cả các yếu tố đầu vào nhưng nó có vẻ như tôi muốn được cố gắng để làm quá nhiều.

  2. Làm cách nào để giải quyết vấn đề tôi có ở trên?

  3. Tôi có nên sử dụng các phần Form/Element/Input của Zend theo một cách khác mà tôi không thấy không?

+0

Bạn đã đã kiểm tra [docs] (http://zf2.readthedocs.org/en/latest/modules/zend.form.quick-start.html) về biểu mẫu? Tôi đặc biệt khuyên bạn nên sử dụng Chú thích vì đây là cách nhanh nhất để tạo biểu mẫu. –

+0

@DanielM Tôi đã xem các tài liệu bạn đã đề cập nhưng câu hỏi 'làm cách nào để tôi kiểm tra từng đầu vào một cách riêng biệt?' vẫn còn. Với phương pháp chú thích tôi sẽ nhận được một hình thức hoàn toàn nhận ra đúng không? Vì vậy, tôi sẽ không bị buộc phải kiểm tra bằng cách sử dụng kết hợp đầu vào EVERY? –

+0

Vâng, tôi không kiểm tra chúng. Tôi không nghĩ rằng thử nghiệm được yêu cầu ở đây bởi vì các lớp phần tử chính nó được kiểm tra, các trình duyệt tính hợp lệ được kiểm tra và các bộ lọc đầu vào cũng vậy. Tất nhiên, nếu bạn tạo một lớp Element mới cho một mục đích đặc biệt không phù hợp với các phần tử hiện có, bạn nên tạo các trường hợp thử nghiệm mới. Nếu không, tiền phạt của nó. Và có, các biểu mẫu được tạo bằng cách sử dụng chú thích có thể được hiển thị hoàn toàn mà không cần phải làm gì ngoại trừ 'echo $ this-> formCollection ($ myForm);' bên trong khung nhìn nếu bạn không cần bất kỳ hiển thị tùy chỉnh nào. –

Trả lời

8

đây là trình xác thực của tôi, sử dụng phương pháp tĩnh để tiêm thực thểManager và làm việc với bất kỳ thực thể doctine nào.

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

ie. Tôi đang sử dụng nó cho theese yếu tố hình thức mà còn được kiểm tra và làm việc tốt:

<?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.', 
         ), 
        ), 
       ), 
      ), 
     ); 
    } 
} 

Khi tạo một người dùng mới

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

khi editin một người dùng hiện

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

Vì vậy, khi nào bạn đặt thực thể tĩnhManager?Tôi nghĩ rằng đây có thể là cách để đi trừ khi tôi có kế hoạch thay đổi ZendF2. –

+0

Tôi sẽ đợi để xem có ai đăng bất kỳ nỗ lực nào khác không nhưng tôi nghĩ rằng đây là một lựa chọn tốt. Cám ơn về công việc của bạn. –

+0

trong ứng dụng Tôi đang thiết lập nó trong module.php của mô-đun có chứa trình xác nhận hợp lệ và trong các thử nghiệm tôi làm điều đó trong phương thức thiết lập của lớp kiểm tra. –

Các vấn đề liên quan