2

So i got a ZF2 application, got a Form and a InputFilter in the InputFilter i have:

$this->add(
        array(
            'name'       => 'email',
            'required'   => true,
            'validators' => array(
                array(
                    'name' => 'EmailAddress'
                ),
                array(
                    'name'      => 'DoctrineModule\Validator\NoObjectExists',
                    'options' => array(
                        'object_repository' => $sm->get('doctrine.entitymanager.orm_default')->getRepository('YrmUser\Entity\User'),
                        'fields'            => 'email'
                    ),
                ),
            ),
        )
    );

works great, however when i edit a existing object and save it the NoObjectExists validator says a matching object is found so it doesn't validate. Is there a solution to this problem? Or should i just remove the validator on the edit form and catch the exception when a duplicate is inserted?

UPDATE: How to use DoctrineModule\Validator\NoObjectExists in edit forms - Zend Framework 2 & Doctrine 2

is the same issue but the answer is to just remove the validator on editing, this off-course is not a solution. As you would still have to catch the exception thrown for when inserting a duplicate. I could do that no problem but what im asking for is a solution to make it work WITH NoObjectExists (otherwise whats the use of this validator if i have to catch the exception for duplicates anyway)

UPDATE, added other relevant code (my form and entity have more fields than this but i removed them to keep it readable on here)

FORM:

namespace YrmUser\Form;

use Zend\Form\Form;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use DoctrineORMModule\Stdlib\Hydrator\DoctrineEntity;

use YrmUser\Entity\User;



class UserForm extends Form
{
protected $objectManager;

/**
 * __construct description
 *
 * @param String $name form name
 * 
 * @return void
 */
public function __construct($name = null)
{
    parent::__construct('new-user');
}

public function init()
{
    $this->setHydrator(
        new DoctrineHydrator($this->objectManager, 'YrmUser\Entity\User')
    )->setObject(new User());

    $this->setAttribute('method', 'post');


    $this->add(
        array(
            'name' => 'email',
            'attributes' => array(
                'type'  => 'email',
                'placeholder' =>'Email',
            ),
            'options' => array(
                'label' => 'Email',
            ),
        )
    );

}
}

FILTER:

class UserFilter extends InputFilter
{
/**
 * [__construct description]
 * 
 * @param ServiceLocator $sm servicelocator
 */
public function __construct($sm)
{


    $this->add(
        array(
            'name'       => 'email',
            'required'   => true,
            'validators' => array(
                array(
                    'name' => 'EmailAddress'
                ),
                array(
                    'name'      => 'DoctrineModule\Validator\NoObjectExists',
                    'options' => array(
                        'object_repository' => $sm->get('doctrine.entitymanager.orm_default')->getRepository('YrmUser\Entity\User'),
                        'fields'            => 'email'
                    ),
                ),
            ),
        )
    );
}
}

CONTROLLER ACTION:

public function editAction() 
{
    $id = (int) $this->params('id', null);
    if (null === $id) {
        return $this->redirect()->toRoute('manage-users');
    }

    $em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');

    $formManager = $this->getServiceLocator()->get('FormElementManager');
    $form = $formManager->get('UserForm');

    $user = $em->find('YrmUser\Entity\User', $id);
    $form->setInputFilter(new UserFilter($this->getServiceLocator()));
    $form->bind($user);

    $request = $this->getRequest();
    if ($request->isPost()) {
        $form->setData($request->getPost());
        if ($form->isValid()) {
            $em->persist($user);
            $em->flush();
            return $this->redirect()->toRoute('manage-users');
        }
    }

    return array(
        'form' => $form,
        'id' => $id
    );
}

ENTITY:

class User
{
/**
 * @var int
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;



/**
 * @var string
 * @ORM\Column(type="string", unique=true,  length=255)
 */
protected $email;



/**
 * Get id.
 *
 * @return int
 */
public function getId()
{
    return $this->id;
}

/**
 * Set id.
 *
 * @param int $id user id
 *
 * @return void
 */
public function setId($id)
{
    $this->id = (int) $id;
}


/**
 * Get email.
 *
 * @return string
 */
public function getEmail()
{
    return $this->email;
}

/**
 * Set email.
 *
 * @param string $email user email adress
 *
 * @return void
 */
public function setEmail($email)
{
    $this->email = $email;
}
}

thanks in advance,

Yrm

Community
  • 1
  • 1
YRM
  • 301
  • 4
  • 13
  • Please post your code of the edit action of the controller and the entire form code too. Hard to tell what the issue is without more code as there are many reasons you could be receiving this error. – Purple Hexagon Dec 19 '13 at 14:10
  • Its obvious why i'm getting the "error" right? The form does not validate because the email already exists in the database. The thing is the existing email belongs to the row im trying to edit/update. – YRM Dec 20 '13 at 14:28
  • The issue is obvious what is causing it isn't. It could be an issue in the controller action or it could be the form hydrator to name a few issues. Just wanted to rule a few things out quick goodle search though and seems this question is a possible dupilcate of this issue http://stackoverflow.com/questions/16808574/how-to-use-doctrinemodule-validator-noobjectexists-in-edit-forms-zend-framewor – Purple Hexagon Dec 20 '13 at 14:45
  • I added the other relevant code, but i think whats causing the issue is obvious as well. The record i am editing has a email when i dont change the email in my form and try to save it the validator does a select(i guess) to see if that email exists. It does exist so the validator does not validate. What i would need would be a way to exclude the current row from being checked. I've seen the other question but just removing the validator is not a solution. You would then have to catch the exception for a duplicate entry but i'm asking if there is a way to make the NoObjectExists work. – YRM Dec 20 '13 at 16:08
  • I think the correct solution would be to use [validation groups](http://framework.zend.com/manual/2.0/en/modules/zend.form.collections.html#validation-groups-for-fieldsets-and-collection) – NDM Jun 13 '14 at 13:15
  • Thanks for your input! But by removing the `email` from the validation group you just remove the validation. Thats the same as in the "duplicate" answer but simply removing the validator is not a solution because now you have to catch the exception thrown if there is another record with the same email which is what i thought NoObjectExists was for. – YRM Jun 17 '14 at 10:34

1 Answers1

5

I lately had the same issue on my project, I spend lot of time searching for a solution and I've finally found this module LosBase.

It uses two customs validators which extend DoctrineModule\Validator\NoObjectExists : NoEntityExists for Add action and NoOtherEntityExists for Edit action.

So I used this appraoch to resolve my problem. This is the solution I've made so far :

NoOtherEntityExists validator :

use Zend\Validator\Exception\InvalidArgumentException;
use DoctrineModule\Validator\NoObjectExists;

class NoOtherEntityExists extends NoObjectExists
{
    private $id; //id of the entity to edit
    private $id_getter;  //getter of the id
    private $additionalFields = null; //other fields
    public function __construct(array $options)
    {
        parent::__construct($options);
         if (isset($options['additionalFields'])) {
          $this->additionalFields = $options['additionalFields'];
         }
        $this->id = $options['id'];
        $this->id_getter = $options['id_getter'];
    }
    public function isValid($value, $context = null)
    {
        if (null != $this->additionalFields && is_array($context)) {
            $value = (array) $value;
            foreach ($this->additionalFields as $field) {
                $value[] = $context[$field];
            }
        }
        $value = $this->cleanSearchValue($value);
        $match = $this->objectRepository->findOneBy($value);

        if (is_object($match) && $match->{$this->id_getter}() != $this->id) {
            if (is_array($value)) {
                $str = '';
                foreach ($value as $campo) {
                    if ($str != '') {
                        $str .= ', ';
                    }
                    $str .= $campo;
                }
                $value = $str;
            }
            $this->error(self::ERROR_OBJECT_FOUND, $value);
            return false;
        }
        return true;
    }
}

NoEntityExists validator :

use Zend\Validator\Exception\InvalidArgumentException;
use DoctrineModule\Validator\NoObjectExists;

class NoEntityExists extends NoObjectExists
{
    private $additionalFields = null;
    public function __construct(array $options)
    {
        parent::__construct($options);
        if (isset($options['additionalFields'])) {
            $this->additionalFields = $options['additionalFields'];
        }
    }
    public function isValid($value, $context = null)
    {
        if (null != $this->additionalFields && is_array($context)) {
            $value = (array) $value;
            foreach ($this->additionalFields as $field) {
                $value[] = $context[$field];
            }
        }
        $value = $this->cleanSearchValue($value);
        $match = $this->objectRepository->findOneBy($value);
        if (is_object($match)) {
            if (is_array($value)) {
                $str = '';
                foreach ($value as $campo) {
                    if ($str != '') {
                        $str .= ', ';
                    }
                    $str .= $campo;
                }
                $value = $str;
            }
            $this->error(self::ERROR_OBJECT_FOUND, $value);
            return false;
        }
        return true;
    }
}

Using this validators with inputFilter :

In my custom input filters, I added two methods : one to append the NoEntityExists validator, and the other to append the NoOtherEntityExists validator :

/**
 * Appends doctrine's NoObjectExists Validator for Add FORM .
 *
 * @param \Doctrine\ORM\EntityRepository $repository
 * @return \Zend\InputFilter\InputFilter
 */
public function appendAddValidator(EntityRepository $repository)
{
    $this->add($this->getFactory()->createInput( array(
            'name' => 'libellesite', //unique field name
            'validators' => array(
                    array(
                            'name' => 'Netman\Form\NoEntityExists',//use namespace
                            'options' => array(
                                    'object_repository' => $repository,
                                    'fields' => 'libellesite',
                                    'messages' => array(
                                    'objectFound' => 'custom message here'
                                    ),
                            ),
                    ),
            )
    )));
    return $this;
}

/**
 * Appends doctrine's NoObjectExists Validator for EDIT FORM.
 *
 * @param \Doctrine\ORM\EntityRepository $repository
 * @return \Zend\InputFilter\InputFilter
 */
public function appendEditValidator(EntityRepository $repository, $id)
{
    $this->add($this->getFactory()->createInput( array(
            'name' => 'libellesite',
            'validators' => array(
                    array(
                            'name' => 'Netman\Form\NoOtherEntityExists',
                            'options' => array(
                                    'object_repository' => $repository,
                                    'fields' => 'libellesite',
                                    'id'=>$id, //
                                    'id_getter'=>'getCodesite',//getter for ID
                                    'messages' => array(
                                    'objectFound' => 'custom message here'
                                    ),
                            ),
                    ),
            )
    )));
    return $this;
}

Controller :

In the addAction :

$repository = $em->getRepository('Entity\Name');  
$form->setInputFilter($filter->appendAddValidator($repository));

In the editAction :

$id = $this->params('id', null);
$repository = $em->getRepository('Entity\Name');  
$form->setInputFilter($filter->appendEditValidator($repository,$id));
blackbishop
  • 30,945
  • 11
  • 55
  • 76