I have a simple Symfony2 application with two entities: Municipality and Poi. There is a relation "One-To-Many" between Municipality and Pois (i.e: zero or more pois placed in one municipality), so the Entity files are like this:
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;
}
}
At this point, I want to manage the One-To-Many relation between the municipality and their pois from the Municipality add/edit form in Sonata Admin.
I have followed the instructions explained in http://sonata-project.org/bundles/doctrine-orm-admin/master/doc/reference/form_field_definition.html#advanced-usage-one-to-many , so the MunicipalityAdmin class file is:
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'
))
;
}
}
The form I am trying to get is an add/edit form where I can define de name of the municipality and add/remove associated pois from the Poi entity, but what I actually get is a form where I can define the name of the municipality, and manage the Poi entity in a kind of sub-form.
This screenshoot describe the result --> http://i.imgur.com/nM1ywwh.png
I mean, in this way, I can add new Pois and relations with any municipality (i.e: Los Angeles), but what I'm trying to get is a list of al pois that are related to this municipality and the posibility to:
- Add new relations (i.e: associate an orphan Poi like Statue of Liberty to this municipality (New York)).
- Remove existing relations (i.e: remove an incorrect relation like "Walt Disney Concert Hall" in New York)
I've seen the way to manage this in the reverse way, selecting the Municipality related to each Poi in the Poi add/edit form (Many-to-one), but I wonder if is there any way to manage this relations in the other entity.
Can I do this in SonataAdmin? Any clue?
UPDATE: Solved the UI and adding pois, but not deleting
I've got the way to show widget that I am looking for, by definining the form in this way:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name')
->add('pois', null, array(), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position'
))
;
}
What I did is to change 'sonata_type_collection' for null value. Now I get a form like this screenshot -->
... that as just the behavior I want.
Initially, the additions in this widget (i.e: add new pois to a municipality), didn't persist. Thanks to the comment of Radomir Wojtera, I realized that there was methods not implemented in my Municipality Entity class (setPois, addPoi and removePoi).
I added this methods ...
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);
}
... and now I'm able to grab new Pois for a Municipality, but when I remove some Pois, they did't disassociate. so I can add relations, but not remove.
Finally I can resolve the second issue (removing Poi from a Municipality didn't work) by setting orphanRemoval=true in Entity configuration
The first issue is solved as commented in the question update.