Search code examples
ormdoctrine-ormzend-framework2zend-form

Saving a Doctrine 2 Entity that contains an ObjectSelect element using Zend Form


I was following along with the Doctrine Hydrator tutorial, but I am having issues saving when my fieldset contains an ObjectSelect. I'm using ORM mapping on my entities. Basically I have a Role entity with id and name. I also have a User entity with id, name and role (ManyToOne). I also have my getters and setters. My setRole() method passes the Role entity as a parameter.

/** @param Role $role */
public function setRole(\Application\Entity\Role $role) {
    $this->role = $role;
}

I setup a UserFieldset with a Doctrine Hydrator.

$this->setHydrator(new DoctrineHydrator($objectManager, 'Application\Entity\User'))
     ->setObject(new User());

The object select for the Role

$this->add(array(
    'type' => 'DoctrineModule\Form\Element\ObjectSelect',
    'name' => 'role',
    'options' => array(
        'label' => 'Role',
        'object_manager' => $objectManager,
        'target_class' => 'Application\Entity\Role',
        'property' => 'name'
    )
));

I then setup a UserForm that sets the DoctrineHydrator and adds the UserFieldset.

My controller action

public function addUserAction() {
    $objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
    $form = new UserForm($objectManager);
    $user = new User();
    $form->bind($user);
    if ($this->request->isPost()) {
        $form->setData($this->request->getPost());
        if ($form->isValid()) {
            $objectManager->persist($user);
            $objectManager->flush();
        }
    }
    return array('form' => $form);
}

What seems to be happening is that the ID of the role is passed to setRole rather than an object. As a workaround I've modified my action to:

if ($form->isValid()) {
    $objectManager->persist($user);
    $data = $this->request->getPost();
    $role = $objectManager->find('Application\Entity\Role', $data->user['role']);
    $user->setRole($role);
    $objectManager->flush();
}

It seems as if this additional step should not be required, but I am not sure if I need to modify my setRole or if I also need to bind a Role entity to the form. This is obviously a simplified example, but my actual forms have many associations that will be tedious to have to code in the controller like this.

UPDATE: Debug information about post and form.

var_dump($form->getData());
var_dump($this->request->getPost());

Output

object(Application\Entity\User)[395]
    protected 'id' => int 6
    protected 'name' => string 'Jane Doe' (length=8)
    protected 'role' => null

object(Zend\Stdlib\Parameters)[146]
    public 'user' => 
        array (size=3)
        'id' => string '' (length=0)
        'name' => string 'Jane Doe' (length=8)
        'role' => string '3' (length=1)
    public 'submit' => string 'Add User' (length=8)

Solution

  • At long last I got it working. The issue was that I needed to add the role to my input filter on the fieldset

    public function getInputFilterSpecification() {
        return array(
            'name' => array('required' => true),
            'role' => array('required' => true)
        )
    }
    

    ... and my validation group on my form.

    $this->setValidationGroup(array(
        'User' => array(
            'name',
            'role'
        )
    ));
    

    Now to save the user in my action it is simply

    if ($form->isValid()) {
        $objectManager->persist($user);
        $objectManager->flush();
    }