Version

Documentation » Admin

Sortable Sonata Type Model in Admin =================================== This is a full working example on how to implement a sortable feature in your Sonata admin form between two entities. Background ---------- The sortable function is already available inside Sonata for the ``ChoiceType``. But the ``ModelType`` (or sonata_type_model) extends from choice, so this function is already available in our form type. We only need some configuration to make it work. The goal here is to fully configure a working example to handle the following need : User got some expectations, but some are more relevant than the others. Pre-requisites -------------- Configuration ^^^^^^^^^^^^^ - you already have SonataAdmin and DoctrineORM up and running. - you already have a ``UserBundle``. - you already have ``User`` and ``Expectation`` Entities classes. - you already have an ``UserAdmin`` and ``ExpectationAdmin`` set up. The Recipe ---------- Part 1 : Update the data model configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The first thing to do is to update the Doctrine ORM configuration and create the join entity between ``User`` and ``Expectation``. We are going to call this join entity ``UserHasExpectations``. .. note:: We can't use a ``Many-To-Many`` relation here because the joined entity will required an extra field to handle ordering. So we start by updating ``UserBundle/Resources/config/doctrine/User.orm.xml`` and adding a ``One-To-Many`` relation. .. code-block:: xml Then update ``UserBundle/Resources/config/doctrine/Expectation.orm.xml`` and also adding a ``One-To-Many`` relation. .. code-block:: xml We now need to create the join entity configuration, create the following file in ``UserBundle/Resources/config/doctrine/UserHasExpectations.orm.xml``. .. code-block:: xml Part 2 : Update the data model entities ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Update the ``UserBundle\Entity\User.php`` entity with the following:: /** * @var UserHasExpectations[] */ protected $userHasExpectations; public function __construct() { $this->userHasExpectations = new ArrayCollection; } /** * @param ArrayCollection $userHasExpectations */ public function setUserHasExpectations(ArrayCollection $userHasExpectations) { $this->userHasExpectations = new ArrayCollection; foreach ($userHasExpectations as $one) { $this->addUserHasExpectations($one); } } /** * @return ArrayCollection */ public function getUserHasExpectations() { return $this->userHasExpectations; } /** * @param UserHasExpectations $userHasExpectations */ public function addUserHasExpectations(UserHasExpectations $userHasExpectations) { $userHasExpectations->setUser($this); $this->userHasExpectations[] = $userHasExpectations; } /** * @param UserHasExpectations $userHasExpectations * * @return $this */ public function removeUserHasExpectations(UserHasExpectations $userHasExpectations) { $this->userHasExpectations->removeElement($userHasExpectations); return $this; } Update the ``UserBundle\Entity\Expectation.php`` entity with the following:: /** * @var UserHasExpectations[] */ protected $userHasExpectations; /** * @param UserHasExpectations[] $userHasExpectations */ public function setUserHasExpectations($userHasExpectations) { $this->userHasExpectations = $userHasExpectations; } /** * @return UserHasExpectations[] */ public function getUserHasExpectations() { return $this->userHasExpectations; } /** * @return string */ public function __toString() { return $this->getLabel(); } Create the ``UserBundle\Entity\UserHasExpectations.php`` entity with the following:: namespace UserBundle\Entity; class UserHasExpectations { /** * @var integer $id */ protected $id; /** * @var User $user */ protected $user; /** * @var Expectation $expectation */ protected $expectation; /** * @var integer $position */ protected $position; /** * Get id * * @return integer $id */ public function getId() { return $this->id; } /** * @return User */ public function getUser() { return $this->user; } /** * @param User $user * * @return $this */ public function setUser(User $user) { $this->user = $user; return $this; } /** * @return Expectation */ public function getExpectation() { return $this->expectation; } /** * @param Expectation $expectation * * @return $this */ public function setExpectation(Expectation $expectation) { $this->expectation = $expectation; return $this; } /** * @return int */ public function getPosition() { return $this->position; } /** * @param int $position * * @return $this */ public function setPosition($position) { $this->position = $position; return $this; } /** * @return string */ public function __toString() { return (string) $this->getExpectation(); } } Part 3 : Update admin classes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is a very important part, the admin class **should** be created for the join entity. If you don't do that, the field will never display properly. So we are going to start by creating this ``UserBundle\Admin\UserHasExpectationsAdmin.php`` ... .. code-block:: php namespace UserBundle\Admin; use Sonata\AdminBundle\Admin\AbstractAdmin; use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Form\FormMapper; final class UserHasExpectationsAdmin extends AbstractAdmin { protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('expectation', 'sonata_type_model', ['required' => false]) ->add('position', 'hidden') ; } protected function configureListFields(ListMapper $listMapper) { $listMapper ->add('expectation') ->add('user') ->add('position') ; } } ... and define the service in ``UserBundle\Resources\config\admin.xml``. .. code-block:: xml UserBundle\Entity\UserHasExpectations Now update the ``UserBundle\Admin\UserAdmin.php`` by adding the ``sonata_type_model`` field:: protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('userHasExpectations', 'sonata_type_model', [ 'label' => 'User\'s expectations', 'query' => $this->modelManager->createQuery('UserBundle\Entity\Expectation'), 'required' => false, 'multiple' => true, 'by_reference' => false, 'sortable' => true, ]) ; $formMapper ->get('userHasExpectations') ->addModelTransformer(new ExpectationDataTransformer($this->getSubject(), $this->modelManager)); } There is two important things that we need to show here : * We use the field ``userHasExpectations`` of the user, but we need a list of ``Expectation`` to be displayed, that's explain the use of ``query``. * We want to persist ``UserHasExpectations``Entities, but we manage ``Expectation``, so we need to use a custom `ModelTransformer `_ to deal with it. Part 4 : Data Transformer ^^^^^^^^^^^^^^^^^^^^^^^^^ The last (but not least) step is create the ``UserBundle\Form\DataTransformer\ExpectationDataTransformer.php`` to handle the conversion of ``Expectation`` to ``UserHasExpectations``:: namespace UserBundle\Form\DataTransformer; class ExpectationDataTransformer implements Symfony\Component\Form\DataTransformerInterface { /** * @var User $user */ private $user; /** * @var ModelManager $modelManager */ private $modelManager; public function __construct(User $user, ModelManager $modelManager) { $this->user = $user; $this->modelManager = $modelManager; } public function transform($data) { if (!is_null($data)) { $results = []; /** @var UserHasExpectations $userHasExpectations */ foreach ($data as $userHasExpectations) { $results[] = $userHasExpectations->getExpectation(); } return $results; } return $data; } public function reverseTransform($expectations) { $results = new ArrayCollection(); $position = 0; /** @var Expectation $expectation */ foreach ($expectations as $expectation) { $userHasExpectations = $this->create(); $userHasExpectations->setExpectation($expectation); $userHasExpectations->setPosition($position++); $results->add($userHasExpectations); } // Remove Old values $qb = $this->modelManager->getEntityManager()->createQueryBuilder(); $expr = $this->modelManager->getEntityManager()->getExpressionBuilder(); $userHasExpectationsToRemove = $qb->select('entity') ->from($this->getClass(), 'entity') ->where($expr->eq('entity.user', $this->user->getId())) ->getQuery() ->getResult(); foreach ($userHasExpectationsToRemove as $userHasExpectations) { $this->modelManager->delete($userHasExpectations, false); } $this->modelManager->getEntityManager()->flush(); return $results; } }

Found a typo? Something is wrong in this documentation? Just fork and edit it!