4

What is the most effective way to use DoctrineModule\Validator\NoObjectExists validator in a Zend Form that is used for editing also? Because when I use the same Form for saving edited values, this validates the Object existence and flags form is invalid.

Wilt
  • 41,477
  • 12
  • 152
  • 203
  • see my answer here http://stackoverflow.com/questions/20680700/zf2-inputfilter-doctrine-noobjectexists-editing-object-does-not-validate#answer-29318872 – blackbishop Mar 28 '15 at 16:05

3 Answers3

6

Few weeks ago I resolved the same problem using a helper method in my custom filter. I am not sure if this is the correct approach but it works.

  1. Write a custom inputfilter extending Zend\InputFilter\InputFilter.
  2. Add your generic filters and validators at init() method of filter.
  3. Write a helper method in the input filter which appends doctrine's existence validator to current validator chain like below.
  4. Add existence validator to your filter instance when creating new object, use generic filter instance when editing.

So,

<?php
/**
 * Baz filter
 */
class BazFilter extends Zend\InputFilter\InputFilter
{

/**
 * This method will be triggered automatically when you retrive baz filter via inputfiltermanager.
 */
public function init() 
{
    // Define your input names, types, validators and filters as arrays
    $this->add(array(
        'name' => 'code',
        'required' => true,
        'validators' => array(),
        'filters' => array()
    ));

    $this->add( array(...) );
    $this->add( array(...) );
    // ...
}

/**
 * Appends doctrine's noobjectexists validator to validator chain only when required.
 * 
 * @access public
 * @param \Doctrine\ORM\EntityRepository $repository
 * @return \Zend\InputFilter\InputFilter
 */
public function appendExistenceValidator(\Doctrine\ORM\EntityRepository $repository)
{
     $validatorSignature = array(
        'name' => 'code',
        'validators' => array(
            array(
                'name' => 'DoctrineModule\Validator\NoObjectExists',
                'options' => array(
                    'object_repository' => $repository,
                    'fields' => 'code',
                    'messages' => array( NoObjectExists::ERROR_OBJECT_FOUND => "This object with code already exists in database." )
                )
            )
        )
    );
    $validator = $this->getFactory()->createInput( $validatorSignature );
    $this->add($validator);
    return $this;
    }
}

Finally, append this inputfilter to your form when editing:

// $form = your form instance
// $filter = Bazfilter instance
$form->setData($postData)->setInputFilter( $filter );
if( $form->isValid() === false ) {
    // ...
}

When creating:

// $filter = bazfilter instance
$repository = $entityManager->getRepository('Your\Entity\Name');
$filter->appendExistenceValidator( $repository ); // <-- Notice this line

$form->setData($postData)->setInputFilter( $filter );
if( $form->isValid() === false ) {
    // ...
}
Wilt
  • 41,477
  • 12
  • 152
  • 203
edigu
  • 9,878
  • 5
  • 57
  • 80
1

You can't use NoObjectExists in an easy way, you should use UniqueObject instead:

https://github.com/doctrine/DoctrineModule/blob/ea9e6902fd24906250d01d31561454451a788f86/src/DoctrineModule/Validator/UniqueObject.php

Tadej
  • 379
  • 4
  • 12
0

I tried using DoctrineModule\Validator\NoObjectExists in form and it prevents updates which keep the unique field(s) unchanged. As @Tadej mentioned - use UniqueObject. Here is an example form:

class ExampleForm extends Form implements InputFilterProviderInterface
{
    /**
    * @var EntityManager
    */
    private $entityManager;

    /**
    * @var Repository
    */
    private $repository;

    /**
    * ExampleForm constructor.
    *
    * @param EntityManager $entityManager
    * @param Repository $repository
    */
    public function __construct(EntityManager $entityManager, Repository $repository)
    {
        $this->entityManager = $entityManager;
        $this->repository    = $repository;
        $this->add(
            [
                'type'       => Text::class,
                'name'       => 'name',
                'options'    => [
                    'label' => _('Name *')
                ],
                'attributes' => [
                    'class' => 'form-control',
                ],
            ]
        );
    }

    /**
    * @return array
    */
    public function getInputFilterSpecification()
    {
        return [
            'name'     => [
                'required'   => true,
                'filters'    => [
                ],
                'validators' => [
                    [
                        'name'    => UniqueObject::class,
                        'options' => [
                            'object_manager'    => $this->entityManager,
                            'object_repository' => $this->repository,
                            'fields'            => ['name'],
                            'use_context'       => true,
                            'messages'          => [
                                UniqueObject::ERROR_OBJECT_NOT_UNIQUE => "Name '%value%' is already in use",
                            ]
                        ]
                    ]
                ]
            ],
        ];
    }
}

Note the use of 'use_context' option - it must be used if field is not primary key. 'messages' is optional.

Andon Totev
  • 204
  • 2
  • 3