Relações Um para Muitos e formulário de adição/edição no SonataAdmin e Symfony2
-
21-12-2019 - |
Pergunta
Eu tenho um aplicativo Symfony2 simples com duas entidades:Município e Poi.Existe uma relação "Um-para-muitos" entre Município e Pois (ou seja:zero ou mais pois colocados em um município), então os arquivos da Entidade ficam assim:
Poc\PocBundle\Entity\Municipality.php
<?php
// Poc\PocBundle\Entity\Municipality.php
namespace Poc\PocBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Municipality
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="Poc\PocBundle\Entity\MunicipalityRepository")
*/
class Municipality
{
/**
* @ORM\OneToMany(targetEntity="Poi", mappedBy="municipality")
*/
protected $pois;
public function __construct()
{
$this->pois = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return Municipality
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Add pois
*
* @param \Poc\PocBundle\Entity\Poi $pois
* @return Municipality
*/
public function addPois(\Poc\PocBundle\Entity\Poi $pois)
{
$this->pois[] = $pois;
return $this;
}
/**
* Remove pois
*
* @param \Poc\PocBundle\Entity\Poi $pois
*/
public function removePois(\Poc\PocBundle\Entity\Poi $pois)
{
$this->pois->removeElement($pois);
}
/**
* Get pois
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getPois()
{
return $this->pois;
}
public function __toString()
{
return $this->name;
}
}
Poc\PocBundle\Entity\Poi.php
<?php
// Poc\PocBundle\Entity\Poi.php
namespace Poc\PocBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Poi
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="Poc\PocBundle\Entity\PoiRepository")
*/
class Poi
{
/**
* @ORM\ManyToOne(targetEntity="Municipality", inversedBy="pois")
* @ORM\JoinColumn(name="municipality_id", referencedColumnName="id")
*/
protected $municipality;
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return Poi
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set municipality
*
* @param \Poc\PocBundle\Entity\Municipality $municipality
* @return Poi
*/
public function setMunicipality(\Poc\PocBundle\Entity\Municipality $municipality = null)
{
$this->municipality = $municipality;
return $this;
}
/**
* Get municipality
*
* @return \Poc\PocBundle\Entity\Municipality
*/
public function getMunicipality()
{
return $this->municipality;
}
public function __toString()
{
return $this->name;
}
}
Neste ponto, quero gerenciar a relação um-para-muitos entre o município e seus pontos de equilíbrio no formulário de adição/edição de município no Sonata Admin.
Eu segui as instruções explicadas em http://sonata-project.org/bundles/doctrine-orm-admin/master/doc/reference/form_field_definition.html#advanced-usage-one-to-many , então o arquivo da classe MunicipalityAdmin é:
Poc/PocBundle/Admin/MunicipalityAdmin.php
<?php
namespace Poc\PocBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;
class MunicipalityAdmin extends Admin
{
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('name')
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'edit' => array(),
)
))
;
}
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name')
->add('pois', 'sonata_type_collection', array(), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position'
))
;
}
}
O formulário que estou tentando obter é um formulário de adição/edição onde posso definir o nome do município e adicionar/remover pontos associados da entidade Poi, mas o que realmente consigo é um formulário onde posso definir o nome do município e gerencie a entidade Poi em uma espécie de subformulário.
Esta captura de tela descreve o resultado -> http://i.imgur.com/nM1ywwh.png
Quer dizer, desta forma posso acrescentar novos Pois e relações com qualquer município (ou seja:Los Angeles), mas o que estou tentando conseguir é uma lista de todos os pois que estão relacionados a este município e a possibilidade de:
- Adicione novas relações (ou seja:associar um Poi órfão como Estátua da Liberdade a este município (Nova York)).
- Remova as relações existentes (ou seja:remova uma relação incorreta como "Walt Disney Concert Hall" em Nova York)
Já vi a forma de gerenciar isso de forma inversa, selecionando o Município relacionado a cada Poi no formulário de adição/edição de Poi (Muitos para um), mas gostaria de saber se existe alguma forma de gerenciar essas relações no outra entidade.
Posso fazer isso no SonataAdmin?Qualquer pista?
ATUALIZAR:Resolveu a UI e adicionou pois, mas não excluiu
Eu consegui mostrar o widget que estou procurando, definindo o formulário desta forma:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name')
->add('pois', null, array(), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position'
))
;
}
O que fiz foi alterar 'sonata_type_collection' para valor nulo.Agora recebo um formulário como esta captura de tela ->
...isso é exatamente o comportamento que eu quero.
Inicialmente, as adições neste widget (ou seja:adicionar novos pois a um município), não persistiu.Graças ao comentário de Radomir Wojtera, percebi que havia métodos não implementados na minha classe Entidade Municipal (setPois, addPoi e removePoi).
Eu adicionei esses métodos ...
public function setPois($pois)
{
if (count($pois) > 0) {
foreach ($pois as $i) {
$this->addPoi($i);
}
}
return $this;
}
public function addPoi(\Poc\PocBundle\Entity\Poi $poi)
{
$poi->setMunicipality($this);
$this->pois->add($poi);
}
public function removePoi(\Poc\PocBundle\Entity\Poi $poi)
{
$this->pois->removeElement($poi);
}
...e agora consigo pegar novos Pois para um Município, mas quando removo alguns Pois, eles não se desassociam.então posso adicionar relações, mas não remover.
Solução
Finalmente posso resolver o segundo problema (remover Poi de um município não funcionou) definindo orphanRemoval=true na configuração da entidade
class Municipality
{
/**
* @ORM\OneToMany(targetEntity="Poi", mappedBy="municipality", cascade={"persist"}, orphanRemoval=true))
*/
protected $pois;
...
O primeiro problema é resolvido conforme comentado na atualização da pergunta.