Vamos a ver el modo de que las entidades relacionadas persistan, ManyToMany, desde los 2 sitios (owner y inversed)
Para persistir la entidad relacionada desde la entidad owner, no hay problema, todo funciona perfectamente, el problema viene cuando queremos persistir desde la entidad inversa (inversed).
Suponemos que tenemos 2 entidades, EntityOne.php (owner) y EntityTwo.php (inversed), y hay una relación ManyToMany entre ellas, y necesitamos que desde una Admin Class de SonataAdminBundle, podamos modificar la relación de los items de una y otra entidad.
Persistir ManyToMany
Lo primero es declarar las relaciones y las funciones propias de cada clase.
Entidad owner, EntityOne.php
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * EntityOne * * @ORM\Table() */ class EntityOne { //... /** * @ORM\ManyToMany(targetEntity="EntityTwo", inversedBy="entityOne") * @ORM\JoinTable(name="entityone_entitytwo") */ private $entityTwo; //... /** * Add entityTwo * * @param \AppBundle\Entity\EntityTwo $entityTwo * @return EntityOne */ public function addEntityTwo(\AppBundle\Entity\EntityTwo $entityTwo) { $this->entityTwo[] = $entityTwo; return $this; } /** * Remove entityTwo * * @param \AppBundle\Entity\EntityTwo $entityTwo */ public function removeEntityTwo(\AppBundle\Entity\EntityTwo $entityTwo) { $this->entityTwo->removeElement($entityTwo); } /** * Get entityTwo * * @return \Doctrine\Common\Collections\Collection */ public function getEntityTwo() { return $this->entityTwo; } /** * Constructor */ public function __construct() { $this->entityTwo= new \Doctrine\Common\Collections\ArrayCollection(); } }
Y ahora la entidad inversed EntityTwo.php:
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * EntityTwo * * @ORM\Table() */ class EntityTwo { //... /** * @ORM\ManyToMany(targetEntity="EntityOne", mappedBy="entityTwo") */ private $entityOne; //... /** * Add entityOne * * @param \AppBundle\Entity\EntityOne $entityOne * @return EntityTwo */ public function addEntityOne(\AppBundle\Entity\EntityOne $entityOne) { $this->entityOne[] = $entityOne; $entityOne->addEntityTwo($this); } /** * Remove entityOne * * @param \AppBundle\Entity\EntityOne $entityOne */ public function removeEntityOne(\AppBundle\Entity\EntityOne $entityOne) { $this->entityOne->removeElement($entityOne); $entityOne->removeEntityTwo($this); } /** * Get entityOne * * @return \Doctrine\Common\Collections\Collection */ public function getEntityOne() { return $this->entityOne; } /** * Constructor */ public function __construct() { $this->entityOne= new \Doctrine\Common\Collections\ArrayCollection(); } }
Importantes, son las líneas 33 y 44, se utilizan las clases de la entidad owner, para persistir estos datos.
la Admin Class del owner, nada que destacar, EntityOneAdmin.php
<?php namespace AppBundle\Admin; use Sonata\AdminBundle\Admin\Admin; use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Datagrid\DatagridMapper; use Sonata\AdminBundle\Form\FormMapper; use Sonata\AdminBundle\Show\ShowMapper; use Sonata\AdminBundle\Route\RouteCollection; /** * Description of EntityOneAdmin * * @author Juanjo García */ class EntityOneAdmin extends Admin { protected function configureRoutes(RouteCollection $collection) { //... } // Fields to be shown on create/edit forms protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('entityTwo') ; } // Fields to be shown on filter forms protected function configureDatagridFilters(DatagridMapper $datagridMapper) { //... } // Fields to be shown on lists protected function configureListFields(ListMapper $listMapper) { //... } /** * @param ShowMapper $showMapper */ protected function configureShowFields(ShowMapper $showMapper) { //... } }
También es muy importante la configuración, en la Admin Class, forzando a que ejecute las clases propias de la entidad inversed y no de la owner, EntityTwoAdmin.php
<?php namespace AppBundle\Admin; use Sonata\AdminBundle\Admin\Admin; use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Datagrid\DatagridMapper; use Sonata\AdminBundle\Form\FormMapper; use Sonata\AdminBundle\Show\ShowMapper; use Sonata\AdminBundle\Route\RouteCollection; /** * Description of EntityTwoAdmin * * @author Juanjo García */ class EntityTwoAdmin extends Admin { protected function configureRoutes(RouteCollection $collection) { //... } // Fields to be shown on create/edit forms protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('entityOne', 'sonata_type_model', array('by_reference' => false, 'multiple' => true)) ; } // Fields to be shown on filter forms protected function configureDatagridFilters(DatagridMapper $datagridMapper) { //... } // Fields to be shown on lists protected function configureListFields(ListMapper $listMapper) { //... } /** * @param ShowMapper $showMapper */ protected function configureShowFields(ShowMapper $showMapper) { //... } }
Esto es todo, ya podemos persistir las relaciones desde las dos Admin Class.