Symfony form doesn't persist object with ManyToMany relationship

I have a problem persisting an item form using a ManyToMany relationship.

I cannot save fields that are on the "mappedBy" side of the relationship.

The code below doesn't save anything to the database and doesn't throw any errors:

// Entity/Pet
/**
 * @var \Doctrine\Common\Collections\Collection
 *
 * @ORM\ManyToMany(targetEntity="AppBundle\Entity\Customer", mappedBy="pet", cascade={"persist"})
 */
private $customer;

/**
 * Set customer
 *
 * @param \AppBundle\Entity\Customer $customer
 * @return Pet
 */
public function setCustomer($customer)
{
    $this->customer = $customer;

    return $this;
}

// Entity/Customer
/**
 * @var Pet
 *
 * @ORM\ManyToMany(targetEntity="AppBundle\Entity\Pet", inversedBy="customer", cascade={"persist"})
 * @ORM\JoinTable(name="customer_pet",
 *   joinColumns={
 *     @ORM\JoinColumn(name="customer_id", referencedColumnName="id")
 *   },
 *   inverseJoinColumns={
 *     @ORM\JoinColumn(name="pet_id", referencedColumnName="id")
 *   }
 * )
 */
private $pet;

// PetType.php
$builder->add('customer', 'entity', 
          array(
            'class' => 'AppBundle:Customer',
            'property' => 'firstname',
            'empty_value' => 'Choose owner',
            'multiple' => true
          ));

      

It works the other way around. So if I save something from the CustomerType everything works.

EDIT:

The solution below worked for me, but after a couple of days I found a problem with this solution. If the form is submitted with a value that has already been stored in the database, then Symfony will throw an error. To prevent that I had to check if a given client was already assigned to a pet.

The validation of the currently assigned clients should have been done at the beginning of the function and not after the form was submitted, because for some reason after submitting the Pet () object, the presented values ​​are presented not only those present in the db.

So in the beginning I put all the already assigned clients in an array

  $em = $this->getDoctrine()->getManager();
  $pet = $em->getRepository('AppBundle:Pet')->find($id);
  $petOriginalOwners = array();
  foreach ($pet->getCustomer() as $petCustomer) 
  {
      $petOriginalOwners[] = $petCustomer->getId();
  } 

      

And after submitting the form, I checked if the presented IDs are present in the array

if ($form->isValid()) 
{
  foreach ($form['customer']->getData()->getValues() as $v) 
  {
    $customer = $em->getRepository('AppBundle:Customer')->find($v->getId());
    if ($customer && !in_array($v->getId(), $petOriginalOwners) )      
    {
      $customer->addPet($pet);
    }
  }
  $em->persist($pet);
  $em->flush();
  return $this->redirect($this->generateUrl('path'));
} 

      

+3


source to share


1 answer


In Symfony2, an object with a doctrine commented inverseedBy property is the one that should CHANGE THE ADDITIONAL TABLES CREATED BY THE MANYTOMANY RELATIONSHIP. This is why, when creating a client, it inserts the corresponding rows into this additional table, keeping the corresponding pets.

If you want the same behavior to happen the other way around, I recommend:

//PetController.php
public function createAction(Request $request) {
    $entity = new Pet();
    $form = $this->createCreateForm($entity);
    $form->submit($request);



    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        foreach ($form['customer']->getData()->getValues() as $v) {
            $customer = $em->getRepository('AppBundle:Customer')->find($v->getId());
            if ($customer) {
                $customer->addPet($entity);
            }
        }
        $em->persist($entity);
        $em->flush();

        return $this->redirect($this->generateUrl('pet_show', array('id' => $entity->getId())));
    }

    return $this->render('AppBundle:pet:new.html.twig', array(
                'entity' => $entity,
                'form' => $form->createView(),
    ));
}

private function createCreateForm(Pet $entity) {
        $form = $this->createForm(new PetType(), $entity, array(
            'action' => $this->generateUrl('pet_create'),
            'method' => 'POST',
        ));

        return $form;
    }

      



These two are the standard Symfony2 CRUD actions in the controller, corresponding to the subject Pet.

The only setting is the foreach structure inserted in the first activity, this way you force the same pet to be added to every client that you select on the form, thereby getting the behavior you want.

Look, this is very likely. This is not the RIGHT WAY, or the RIGHT WAY, but the WAY, and it works. Hope it helps.

+1


source







All Articles