Search code examples
validationzend-framework2db2duplicateszend-form2

ZF2 + Duplicate Form Validation on composite key


I have a Form having primary key on two fields (gid, bid). I need to add validation to block duplicate entries into database.

I have checked with ZF2 Solution for this . http://framework.zend.com/manual/2.2/en/modules/zend.validator.db.html#excluding-records . While this approach of handling composite keys is not look the ideal way, But still I am trying it because it look like only buil-in way. Now it require me to provide second field's value (value option in exclude), which is again a problem. As I am trying it

$inputFilter->add(array(
     'name'     => 'gid',
     'required' => true,
     'validators' => array(
         array(
                'name' => 'NotEmpty',
                'options' => array(
                    'messages' => array(
                        'isEmpty' => 'required'
                    ),
                 ),
         ),
         array (
            'name' => 'Zend\Validator\Db\NoRecordExists',
            'options' => array (
                'table' => 'gtable',
                'field' => 'gid',
                'adapter' => $this->dbAdapter,
                'messages' => array(
                    \Zend\Validator\Db\NoRecordExists::ERROR_RECORD_FOUND => 'The specified key already exists in database' 
                ),
                'exclude' => array(
                    'field' => 'bid',
                    'value' => [?],
                ),
            )
        ),
     )
 ));

How do I get this value, As Form is absolute separate Class/File than controller where I have the submitted form values. Is some better architecture solution of this problem exists Or Some hack to pass submitted field value to Form Class is only solution ?

Note : I am not in favor of Build My Validation Plugin for this task as short time is constraint for functionality.


Solution

  • You can do all the job in your form. To achieve that, you could define your forms as factories in your module Module.php.

    Module.php

    use MyNamespace\MyForm;
    
    //NOTE THAT THE SERVICE MANAGER IS INJECTED. YOUR FORM COULD RECEIVE IT THROUGH THE CONSTRUCTOR
    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'my_form' => function( $sm ) {
                    $form = new MyForm( $sm );
    
                    return $form;
                },
            ),
        );
    }
    

    When you want to use the form is as easy as use this code in your controller:

    class MyController extends AbstractActionController
    {
        public function createAction() {
            $form = $this->getServiceLocator()->get( 'my_form' ) );
            (...)
        }
    }
    

    And your MyForm.php

    use Zend\Form\Form;
    
    class MyForm extends Form
    {
        public $serviceManager, $request, $postData;
    
        public function __construct( $serviceManager ) {
            parent::__construct( null );
    
            $this->serviceManager = $serviceManager;
            $this->request = $serviceManager->get( 'Application')->getMvcEvent()->getRequest();
            $this->postData = get_object_vars( $this->request->getPost() );
        }
    }
    

    This way you can get advantage of the Service Manager within your form. And the public postData, where you'll find the bid value you're looking for to build your NoRecordExists filter.