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.