Symfony2 Sonata admin dynamically change input dat

2020-02-29 19:43发布

For one of my objects I need to create some dynamic form rendering... But I cant figure out how to do this in Sonata Admin. For example when I create an object I have a field type. In this field I select a type that my object is going to be. Now when I select the type I want to make a field appear, based on the type. For example, if I select type "Carousel" I want to show a field that is selecting all object form entity Gallery. If I select type "Product" I want to display field with all products to selectt from... How can I acheve that?

Right now I have this:

/**
 * @param FormMapper $formMapper
 */
protected function configureFormFields(FormMapper $formMapper)
{
    $formMapper
        ->with('Module', array(
            'class' => 'col-md-6'
        ))
            ->add('position')
            ->add('type', null, array(
                'attr' => array('class' => 'module_type')
            ))
            ->add('items', 'entity', array(
                'class' => 'ApplicationSonataMediaBundle:Gallery'
            ))
        ->end()
    ;
}

And I have overriden the edit template:

{% extends 'SonataAdminBundle:CRUD:edit.html.twig' %}

{% block javascripts %}
    {{ parent() }}
    <script type="text/javascript">
        $(document).ready(function () {
            $(".module_type").change(function() {

            });

        });
    </script>

{% endblock %}

As you can see the gallery is hardcoded now..

I cant figure out how to do this now... How to say that if the value selected is this, use that entity in field... The problem is that the way form is rendered in Sonata is very complicated... I dont understand it..

maybe I should use ajax? But again, when I send a value and get the response how to add a field without refresh?

Any help appreciated.

4条回答
欢心
2楼-- · 2020-02-29 20:04

Sonata provides you with the 'sonata_type_choice_field_mask' type which allows you to change the fields displayed on the form dynamically depending on the value of this 'sonata_type_choice_field_mask' input so you don't have to use ajax.

Here is the doc where you can find everything about sonata types and the choice field mask.

protected function configureFormFields(FormMapper $formMapper)
{
    $formMapper
        ->add('type', 'sonata_type_choice_field_mask', array(
            'choices' => array(
                //The list of available 'Type' here
                'choice1',
                'choice2'
            ),
            'map' => array(
                //What you want to display depending of the selected option
                'choice1' => array(
                    // List of the fields displayed if choice 1 is selected
                    'field1', 'field3'
                ),
                'choice2' => array(
                    // List of the fields displayed if choice 2 is selected
                    'field2', 'field3'
                )
            ),
            'placeholder' => 'Choose an option',
            'required' => true
        ))
        ->add('field1', 'entity', array(/* Options for entity1 goes here */))
        ->add('field2', 'entity', array(/* Options for entity2 goes here */))
        ->add('field3')
    ;
}
查看更多
家丑人穷心不美
3楼-- · 2020-02-29 20:11

If I have realy understand your need you have to use ajax of course, first you need to add new admin route to this EntityAdminController to do it you have to override the configureRoutes method and to add your new route like this :

protected function configureRoutes(RouteCollection $collection)
{
    $collection->add('reloadCities', 'realod-cities');
}

Then you have to create a new controller which gonna have the action definition for your new route like :

use Sonata\AdminBundle\Controller\CRUDController as BaseController;


class CitiesController extends BaseController
{
    public function reloadCitiesAction()
    {
        // some code
        return $this->render(...);
    }
}

Then you have to override the SonataAdminBundle:CRUD:edit.html.twig template and set your javascript event listener like this :

{% extends 'SonataAdminBundle:CRUD:edit.html.twig' %}

{% block form %}
    {{ parent() }}
<script type="text/javascript">

        $(document).ready(function () {
            countries.change(function () {
                $.ajax({
                    url: "{{ admin.generateUrl('reloadCities') }}",
                    data: {
                        'id': $(this).val(),
                        'uniquid': '{{ admin.uniqid }}'
                    },
                    method: 'POST',
                    success: function (html) {
                       // code...
                    },
                    error: function (data) {
                      // more code
                    }
                });
            });
        });
    </script>
查看更多
Deceive 欺骗
4楼-- · 2020-02-29 20:17

After researching forever to find a way to use dynamic dropdowns using ajax and sonata with symfony4 i would like to share my solution how it actually works for me.

In my case i have a district and this district has different cities. I have a company that first chooses a district and then depending of that it´s city.

What i did:

  1. Create your entity for the districts

  2. Create your entity for the cities

  3. Go in your main class (in my case this is the company entity and add two entities for the districts and cities

    /**
     * @ORM\ManyToOne(targetEntity="App\Wdm\MainBundle\Entity\Model\Cities",   inversedBy="id")
     */
    private $city;

    /**
    * @ORM\ManyToOne(targetEntity="App\Wdm\MainBundle\Entity\Model\Districts", inversedBy="id")
    */
     private $district;

  1. Update your database schema and fill the cities and districts fields with some example data

  2. Go into your AdminClass in the configureFormFields Function and add the following (make sure to use the 'choice_label' option correctly with the corresponding field in your district or city entity.

    protected function configureFormFields(FormMapper $formMapper)
    {         $formMapper
                  // Some other added fields

             ->add('district', EntityType::class, [
                 'choice_label' => 'name',
                 'class'       => Districts::class,
                 'placeholder' => '',
             ])

             ->add('city', EntityType::class, [
                 'choice_label' => 'name',
                 'class'       => Cities::class,
                 'placeholder' => '',
             ])       

             ;
  1. This should already work pretty well. You should now be able to have a dependent field. Let´s take a look to the AJAX magic now.

  2. Go into your AdminClass (same as the configureFields-Class) and add the following

 protected function configureRoutes(RouteCollection $collection)
    {    $collection->add('reloadCities', 'reload-cities');

    }
  1. Now you have a route that you can access from your ajax url. Now create a new Controller Class, wherever you want...
<?php

namespace App\Wdm\MainBundle\Controller;

use Symfony\Component\HttpFoundation\Request;
use Sonata\AdminBundle\Controller\CRUDController as BaseController;
use App\Wdm\MainBundle\Entity\Model\Cities;


class CitiesController extends BaseController
{
    public function reloadCitiesAction(Request $request)
    {   $districtid = $request->request->get('id');
        $cities = $this->getDoctrine()->getRepository(Cities::class)->findBy(array("district" => $districtid));
        return $this->render("company/cities.html.twig", array("cities" => $cities));
    }
}

... and don´t forget to register this controller in your services.yaml...

  admin.company:
        class: App\Wdm\MainBundle\Admin\CompanyAdmin
        arguments:
            - ~
            - App\Wdm\MainBundle\Entity\Model\Company
            - App\Wdm\MainBundle\Controller\CitiesController (THIS IS THE NEW ROW)

... and finally the little template that is being called in this function...

// THIS IS THE cities.html.twig

{% for city in cities %}
<option value="{{ city.id }}">{{ city.name }}</option>
{% endfor %}
  1. So far so good. We now got the logic that get´s the data from the ajax call and returns it to your sonata admin edit form. The only thing thats missing now is the jquery code that is needed in the edit template of sonata admin.

Go into your AdminClass and insert the following code (e.g. before configureFormFields)

 public function getTemplate($name)
    {
        switch ($name) {
            case 'edit':
                return 'company/cities_admin.html.twig';
                break;
            default:
                return parent::getTemplate($name);
                break;
        }
    }

Now we create this cities_admin.html.twig template that overrides the default template

{% extends 'SonataAdminBundle:CRUD:edit.html.twig' %}

{% block form %}
    {{ parent() }}
<script type="text/javascript">

    $(document).ready(function () {
        $("#ID_OF_YOUR_DISTRICT_SELECT_FIELD").change(function () {
            $.ajax({
                url: "{{ admin.generateUrl('reloadCities') }}",
                data: {
                    'id': $(this).val(),
                    'uniquid': '{{ admin.uniqid }}'
                },
                method: 'POST',
                success: function (html) {
                    $("#ID_OF_YOUR_CITY_SELECT_FIELD").html(html);
                },
                error: function (data) {
                    // more code
                }
            });
        });
    });
</script>

{% endblock %}

That´s it. Should work like a charm.

查看更多
做自己的国王
5楼-- · 2020-02-29 20:20

I have one question about your code : url: "{{ admin.generateUrl('reloadCities') }}",

Tell me if I wrong, but it seems the variable admin is callable thank to the template extends : {% extends 'SonataAdminBundle:CRUD:edit.html.twig' %}

Only, when I extends the template, I have this error :

Variable "base_template" does not exist.

Any idea ?

查看更多
登录 后发表回答