YRM
YRM

Reputation: 301

form select parent hydration

I have a zf2 application that works with doctrine. I have the following entity:

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

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

    /**
     * @var ArrayCollection
     * @ORM\OneToMany(targetEntity="YrmUser\Entity\Role", mappedBy="parent")
     */
    protected $children;

    /**
     * @var Role
     * @ORM\ManyToOne(targetEntity="YrmUser\Entity\Role", inversedBy="children", cascade={"persist"})
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
     */
    protected $parent;
}

for this entity i have a form:

class RoleForm extends Form
{

    /**
     * [init description]
     * 
     * @return void
     */
    public function init()
    {

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

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


        $this->add(
            array(
                'name' => 'name',
                'attributes' => array(
                    'type'  => 'text',
                    'placeholder' =>'Name',
                ),
                'options' => array(
                    'label' => 'Name',
                ),
            )
        );
        $this->add(
            array(
                'type' => 'DoctrineModule\Form\Element\ObjectSelect',
                'name' => 'parent',
                'attributes' => array(
                    'id' => 'parent_id',
                ),
                'options' => array(
                    'label' => 'Parent',
                    'object_manager' => $this->objectManager,
                    'property'       => 'name',
                    'is_method'      => true,
                    'empty_option' => '-- none --',
                    'target_class'   => 'YrmUser\Entity\Role',
                    'is_method'      => true,
                    'find_method'    => array(
                        'name'   => 'findBy',
                        'params' => array(
                            'criteria' => array('parent' => null),
                        ),
                    ),
                ),
            )
        );
    }
}

The hydration for the select in the form works as it only shows other roles that don't have a parent. But when editing a existing entity it shows itself in the select so i can select itself as its parent. I figured if i would have the id of current entity inside the form i can create a custom repo with a method that retrieves all roles without a parent and does not have the current entity id. But i cant figure out how to get the id of the currently edited entity from inside the form.

Any help is appreciated.

Cheers,

Yrm

Upvotes: 0

Views: 195

Answers (1)

AlexP
AlexP

Reputation: 9857

You can fetch the bound entity within the form using $this->getObject().

You have actually already set this with setObject(new Role());. Unfortunately this means that it was not loaded via Doctine and you will have the same issue, no $id to work with.

Therefore you will need to add the 'parent role' options (value_options) after you have bound the role loaded via doctrine.

From within the controller, I normally request the 'edit' form from a service class and pass in the entity instance or id that is being edited. Once set you can then modify existing form elements before passing it back to the controller.

// Controller
class RoleController
{
   public function editAction()
   {
     $id      = $this->params('id'); // assumed id passed as param
     $service = $this->getRoleService();
     $form    = $service->getRoleEditForm($id); // Pass the id into the getter 

     // rest of the controller...
   }  
}  

By passing in the $id when you fetch the form you can then, within a service, modify the form elements for that specific role.

class RoleService implements ObjectManagerAwareInterface, ServiceLocatorAwareInterface
{
  protected function loadParentRolesWithoutThisRole(Role $role);

  public function getRoleEditForm($id)
  {
    $form = $this->getServiceLocator()->get('Role\Form\RoleEditForm');

    if ($id) {
      $role = $this->getObjectManager()->find('Role', $id);
      $form->bind($role); // calls $form->setObject() internally

      // Now the correct entity is attached to the form
      // Load the roles excluding the current
      $roles = $this->loadParentRolesWithoutThisRole($role);

      // Find the parent select element and set the options
      $form->get('parent')->setValueOptions($roles); 
    }

    // Pass form back to the controller
    return $form;
  }
}

By loading the options after the form has initialized you do not need the current DoctrineModule\Form\Element\ObjectSelect. A normal Select element that has no default value_options defined should be fine.

Upvotes: 1

Related Questions