Formulation of the problem
There is a set of some objects, for example, incoming one-to-one communications (for the purposes of this article, the type of communication does not matter) with objects from another set, for example, responses to letters. SonataAdminBundle is used to manage entities (i.e., an Admin class is defined for each entity). It is necessary to create new answers directly from the List View of the letters.
Entity entities, respectively, letters and responses can look like this:
The essence of the incoming letternamespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; class Incoming { protected $id; protected $incomingTitle; protected $response;
The essence of the response to the incoming letter namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; class Response { protected $id; protected $responseTitle; protected $incoming; protected $text; public function setResponse( \AppBundle\Entity\Incoming $incoming) { $this->incoming = $incoming; return $this; }
Solution options
At first glance, solving the problem is similar to creating a Custom Admin Action in SonataAdminBundle, the process of which is described in
[1] . Following this guide, we could implement an action that creates and saves the response object, attaches it to the current letter object and redirects the user to the edit form of the saved answer to enter its title and text.
In this case, we would need:
')
- Create an action to create a response (for example, createResponseAction) in a CRUD controller, inheriting from Sonata \ AdminBundle \ Controller \ CRUDController
- Copy the contents of createAction from Sonata \ AdminBundle \ Controller \ CRUDController into our action, making changes to it that performs the functions described above.
Such an approach is attractive because for its implementation it is sufficient to strictly follow the guidelines for creating the Custom Admin Action, but it leads to duplication of code and is fraught with unforeseen errors when finalizing createAction before createResponseAction. You can avoid the drawbacks of the approach by directly using the existing and also redefining the Sonata \ AdminBundle \ Controller \ CRUDController intended for this action.
To do this, we will solve the problem in stages:
- provide a transition to the form of creating a new response by pressing the control (for example, a button) in the List View line of letters;
- we realize the automatic connection between the created response and the letter, in the line of which the control element was pressed, before the creation form is displayed.
Go to the form to create a new response
The process of creating a control in the ListView row is described in detail in
[1] . Let us dwell on the features relating to the solution of our problem, namely, generating the url to switch to the form of creating a new object. Offered in
[1] option
{# src/AppBundle/Resources/views/CRUD/list__action_create_other_admin.html.twig #} <a class="btn btn-sm" href="{{ admin.generateObjectUrl('create', object) }}"> </a>
not suitable, since the admin.generateObjectUrl function generates a url to create an object of the current Admin class; in our case, this is a letter (Incoming), but it is necessary that there be an answer (Response). Therefore, we use the following option, decorating the button with the icon:
{# 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>
The key point here is the use of the admin.getRouteGenerator.generateUrl function, which takes as its argument the Admin service, to create an object of which you need to generate a url. Now the task is to transfer the desired Admin service to the template. This can be done by accessing the Symfony2 container directly from list__action_create_other_admin.html.twig, which will deprive the approach of universality, so we used the variable template_variables.otherAdmin, which is passed to the template as described below.
The templates corresponding to the _actions buttons in the ListView row are displayed using the SonataAdminBundle CRUD \ list__action.html.twig include template twig function, namely:
{% include actions.template %}
where actions.template is a variable that is defined in the Admin class in the configureListFields section.
protected function configureListFields(ListMapper $listMapper) { $listMapper
Thus, we need to add the with keyword to the CRUD \ list__action.html.twig to ensure that the variable is passed to the child templates. Since not all of them will use this variable, you should check for its presence:
{% include actions.template with {template_variables : (actions.template_variables is defined ? actions.template_variables : null)} %}
Now you can define the template_variables.otherAdmin variable in the admin class, assigning the required Admin service (in our case, sonata.admin.response) to it and it will be available in the list__action_create_other_admin.html.twig template.
protected function configureListFields(ListMapper $listMapper) { $listMapper
Now, when you click on the button in the List View line of letters, a form opens to create a reply to the letter.
In the
second part of the article, the implementation of automatic linking between the letter and the answer to it, as well as questions about the button display depending on whether the user has the rights to create the answer, is considered.
Links to used resources
- CREATING A CUSTOM ADMIN ACTION