SonataAdminBundle: создание объекта из List View (часть 1)

от автора


Постановка задачи

Имеется набор некоторых объектов, например, входящих писем, связанных связью один-к-одному (для целей данной статьи вид связи значения не имеет) с объектами из другого набора, например, ответами на письма. Для управления сущностями используется SonataAdminBundle (т.е. для каждой сущности определен Admin-класс). Необходимо создавать новые ответы непосредственно из списка (List View) писем.

Сущности (entity) соответственно письма и ответа могут выглядеть следующим образом:

Сущность входящего письма

    namespace AppBundle\Entity;      use Doctrine\ORM\Mapping as ORM;      /**     * @ORM\Entity     * @ORM\Table(name="incoming", options={"comment":"Входящее письмо"})     */     class Incoming     {         /**         * @ORM\Column(type="integer")         * @ORM\Id         * @ORM\GeneratedValue(strategy="AUTO")         */         protected $id;          protected $incomingTitle;          /**         * @ORM\OneToOne(targetEntity="Response", mappedBy="incoming")         */         protected $response;          // другие поля          /**         * Добавление ответа на входящее письмо         *         * @param \AppBundle\Entity\Response $response         *         * @return \AppBundle\Entity\Incoming         */         public function setResponse( \AppBundle\Entity\Response $response)         {             $this->response = $response;              return $this;         }     }      

Сущность ответа на входящее письмо

    namespace AppBundle\Entity;      use Doctrine\ORM\Mapping as ORM;      /**     * @ORM\Entity     * @ORM\Table(name="response", options={"comment":"Ответ на входящее письмо"})     */     class Response     {         /**         * @ORM\Column(type="integer")         * @ORM\Id         * @ORM\GeneratedValue(strategy="AUTO")         */         protected $id;          /**         * @ORM\Column(type="text", options={"comment":"Заголовок ответа"})         */         protected $responseTitle;          /**         * @ORM\OneToOne(targetEntity="Incoming", inversedBy="response")         * @ORM\JoinColumn(name="incoming_id", referencedColumnName="id")         */         protected $incoming;          /**          * @ORM\Column(type="text", options={"comment":"Текст ответа"})          */         protected $text;          /**         * Добавление входящего письма         *         * @param \AppBundle\Entity\Incoming $incoming         *         * @return \AppBundle\Entity\Response         */         public function setResponse( \AppBundle\Entity\Incoming $incoming)         {             $this->incoming = $incoming;              return $this;         }          //Другие действия     } 

Варианты решения

Решение задачи на первый взгляд аналогично созданию Custom Admin Action в SonataAdminBundle, процесс которого описан в [1]. Следуя данному руководству, мы могли бы реализовать действие, которое создает и сохраняет объект ответа, прикрепляет его к текущему объекту письма и перенаправляет пользователя на форму редактирования сохраненного ответа для ввода его заголовка и текста.

При этом нам было бы необходимо:

  • Создать действие по созданию ответа (например, createResponseAction) в CRUD контроллере, унаследовавшись от Sonata\AdminBundle\Controller\CRUDController
  • Скопировать содержимое createAction из Sonata\AdminBundle\Controller\CRUDController в наше действие, внеся в него изменения, выполняющее функции, которые описаны выше

Такой подход привлекателен тем, что для его реализации достаточно строго следовать руководству по созданию Custom Admin Action, однако ведет к дублированию кода и чреват непредвиденными ошибками при доработке createAction до createResponseAction. Избежать недостатков подхода можно, напрямую используя существующие, а также переопределяя предназначенные для этого действия Sonata\AdminBundle\Controller\CRUDController.

Для этого будем решать задачу поэтапно:

  • обеспечим переход на форму создания нового ответа по нажатию элемента управления (например, кнопки) в строке List View писем;
  • реализуем автоматическую связь между создаваемым ответом и письмом, в строке которого был нажат элемент управления, до отображения формы создания.

Переход на форму создания нового ответа

Процесс создания элемента управления в строке ListView подробно описан в [1]. Остановимся на особенностях, касающихся решения нашей задачи, а именно — генерации url для перехода на форму создания нового объекта. Предлагаемый в [1] вариант

{# src/AppBundle/Resources/views/CRUD/list__action_create_other_admin.html.twig #} <a class="btn btn-sm" href="{{ admin.generateObjectUrl('create', object) }}">Создать ответ</a> 

не подходит, поскольку функция admin.generateObjectUrl генерирует url для создания объекта текущего Admin-класса; в нашем случае это письмо (Incoming), а нужно, чтобы был ответ (Response). Поэтому используем следующий вариант, украсив кнопку иконкой:

{# src/AppBundle/Resources/views/CRUD/list__action_create_other_admin.html.twig #} <a href="{{ admin.getRouteGenerator.generateUrl(template_variables.otherAdmin, 'create'}" class="btn btn-sm btn-default edit_link" title="Создать ответ">     <i class="fa fa-plus"></i>     Создать ответ </a> 

Ключевым моментом здесь является использование функции admin.getRouteGenerator.generateUrl, принимающей в качестве аргумента Admin-сервис, для создания объекта которого необходимо сгенерировать url. Теперь задача состоит в том, чтобы передать нужный Admin-сервис в шаблон. Это можно сделать, обратившись к контейнеру Symfony2 прямо из list__action_create_other_admin.html.twig, что лишит подход универсальности, поэтому мы использовали переменную template_variables.otherAdmin, которая передается в шаблон описанным ниже способом.

Шаблоны, соответствующие кнопкам _actions в строке ListView, отображаются посредством twig-функции include шаблона SonataAdminBundle CRUD\list__action.html.twig, а именно:

{% include actions.template %} 

где actions.template — переменная, которая определеяется в Admin-классе в секции configureListFields.

    protected function configureListFields(ListMapper $listMapper)     {         $listMapper          // other fields...              ->add('_action', 'actions', array(                 'actions' => array(                  // ...                  'createOtherAdmin' => array(                      // ВОТ ЭТА ПЕРЕМЕННАЯ                     'template' => 'AppBundle:CRUD:list__action_create_other_admin.html.twig'                 )             )         ))         ;     } 

Таким образом, нам нужно добавить в CRUD\list__action.html.twig ключевое слово with, чтобы обеспечить передачу переменной в дочерние шаблоны. Поскольку не все из них будут использовать данную переменную, следует сделать проверку на ее наличие:

    {% include actions.template with {template_variables : (actions.template_variables is defined ? actions.template_variables : null)} %} 

Теперь можно определить переменную template_variables.otherAdmin в Admin-классе, присвоив ей нужный Admin-сервис (в нашем случае это sonata.admin.response) и она станет доступна в шаблоне list__action_create_other_admin.html.twig.

    protected function configureListFields(ListMapper $listMapper)     {         $listMapper          // other fields...              ->add('_action', 'actions', array(                 'actions' => array(                  // ...                  'createOtherAdmin' => array(                      'template' => 'AppBundle:CRUD:list__action_create_other_admin.html.twig',                      // Передаем Admin-сервис в качестве аргумента в шаблон                     'template_variables' => array('otherAdmin'=> $this->getConfigurationPool()->getContainer()->get('sonata.admin.response');)                 )             )         ))         ;     } 

Теперь при нажатии на кнопку в строке List View писем открывается форма для создания ответа на письмо.

Во второй части статьи будет рассмотрена реализация установления автоматической связи между письмом и ответом на него, а также вопросы отображения кнопки в зависимости от наличия у пользователя прав на создание ответа.

Ссылки на используемые ресурсы

  1. CREATING A CUSTOM ADMIN ACTION

ссылка на оригинал статьи https://habrahabr.ru/post/278987/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *