Enlazar y guardar un formulario con un campo de colección usando una matriz / Doctrine2

Tengo dos entidades

Room.php

/**
 * @ORM\Table(name="room")
 * @ORM\Entity(repositoryClass="Ahsio\StackBundle\Repository\RoomRepository")
 */
class Room
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
     protected $id;

     // ...    

    /**
     * @ORM\OneToMany(targetEntity="Ahsio\StackBundle\Entity\Workstation", mappedBy="room", cascade={"persist", "remove"}, orphanRemoval=true)
     */
     protected $workstations;

y workstation.php

/**
 * @ORM\Table(name="workstation")
 * @ORM\Entity(repositoryClass="Ahsio\StackBundle\Repository\Workstation")
 */
 class Workstation
 {
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
     private $id;

    /**
     * @ORM\ManyToOne(targetEntity="Ahsio\StackBundle\Entity\Room")
     * @ORM\JoinColumn(name="room_id", referencedColumnName="id", nullable=false)
     */
     private $room;

     // ...

I added a Room Type with a collection field for adding/updating/removing workstations

RoomType.php

class RoomType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->add('id', 'hidden', array(
                'read_only' => true,
            ))
            // ....
            ->add('workstations', 'collection', array(
                 'type'         => new WorkstationType(),
                 'allow_add'    => true,
                 'allow_delete' => true,
                 'by_reference' => false,
            ))
        ;
     }
     // ...
}

The Workstation Type is a simple one with only two fields (id & number).

The problem is that when I'm trying to bind RoomType for an existing Room (which contains two workstations of id (1 and 2)) with The following array (Which contains only one of the two workstations):

array(4) {
  ["id"]=> string(1) "3"
  ["workstations"]=> array(1) {
    [0]=> array(2) {
      ["id"]=> int(1)
      ["number"]=> int(200)
     }
  }

The update is well done, The workstation for id=2 is removed. But when I'm trying to remove all workstations for a given Room using:

array(4) {
  ["id"]=> string(1) "3"
  ["workstations"]=> array(0) {
  }

The workstations are still there, The bind I did is well performed, here is what I got when I did a $form->getData() after binding my $form using the last given array:

object(Ahsio\StackBundle\Entity\Room)#229 (4) {
    ["id":protected]=> string(1) "3"
    ["name":protected]=> string(20) "first room (updated)"
    ["description":protected]=> string(39) "This first room is an old one (updated)"
    ["workstations":protected]=> array(0) {
    }
 }

So, no workstations for the updated Room. Can someone tell me why my workstations aren't deleted when I persist this last binded Room?

Here is a part of my Controller code which I'm using to test the update ...

        // The Room $id is given ... 
        $roomArray = array(
        'id'           => $id, 
        'name'         => 'first room (updated)',
        'description'  => 'This first room is an old one (updated)',
            'workstations' => array(),
        );
        $form     = $this->createForm(new RoomType(), $room);      

        $form->bind($roomArray);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getEntityManager();
            $em->persist($room);
            $em->flush();
        }

preguntado el 09 de marzo de 12 a las 16:03

Hello Ahmed, how are you? Could you set a breakpoint in src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php on lines 113 and 146 (if you're using the latest version of Symfony) and tell us what's happening? +1 for the interesting question -

Hello Gregoire! I'm fine, what about you? When I set a breakpoint in ResizeFormListener.php (Line 113) I get an empty array array(0) { } for updating using a room array without any workstation. Even when I set one or more workstations to this array, it appears & it works fine. The form->remove($name) is performed using the array index of each workstation. I don't think the problem is related to the bind. -

2 Respuestas

I think the setter that receives workstations array is never called because the array is empty.

Try to see if it is called by adding a trace.

Also, you should add the cascade="merge" option in your workstations mapping.

respondido 10 mar '12, 16:03

It's called and an empty array is passed to room's workstations which I think is the right behavior. - Ahmed Siouani

I accepted the answer as it helped me figure out how to solve this issue. I'll update your answer later. - Ahmed Siouani

Creo que el persist should not be called when trying to update and delete entities/related entities.

You should probably try calling:

$em->merge($room);

before binding with form data. That way you will restore data from database and orphanRemoval should do the rest.

Also, shouldn't you bind your form with Request and not with data object?

respondido 09 mar '12, 17:03

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.