Symfony2 Sonata admin根据所选值动态更改输入数据

时间:2016-01-04 09:03:48

标签: php jquery forms symfony sonata-admin

对于我的一个对象,我需要创建一些动态表单渲染...但我无法弄清楚如何在Sonata Admin中执行此操作。例如,当我创建一个对象时,我有一个字段类型。在这个字段中,我选择了一个我的对象将要成为的类型。现在当我根据类型选择我想要显示字段的类型时。例如,如果我选择“Carousel”类型,我想显示一个选择所有对象表单实体Gallery的字段。如果我选择“产品”类型,我想显示所有产品的字段以便从中选择...我该怎么做?

现在我有这个:

/**
 * @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()
    ;
}

我已经覆盖了编辑模板:

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

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

            });

        });
    </script>

{% endblock %}

正如你所看到的,画廊现在是硬编码的。

我现在无法弄清楚如何执行此操作...如何说如果选择的值是这个,请在​​字段中使用该实体...问题是在Sonata中呈现表单的方式非常复杂..我不明白..

也许我应该使用ajax?但是,当我发送一个值并获得响应时如何添加一个字段而不刷新?

任何帮助表示感谢。

4 个答案:

答案 0 :(得分:2)

Sonata为您提供了'sonata_type_choice_field_mask&#39;类型允许您根据此参数的值来动态更改表单上显示的字段&#39; sonata_type_choice_field_mask&#39;输入所以你不必使用ajax。

Here is the doc您可以在其中找到有关奏鸣曲类型和选择字段掩码的所有内容。

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')
    ;
}

答案 1 :(得分:1)

如果我真的了解你的需要你当然必须使用ajax,首先你需要向这个EntityAdminController添加新的管理路由来执行它,你必须覆盖configureRoutes方法并添加你的新路由:

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

然后你必须创建一个新的控制器,它将为你的新路线设置动作定义,如:

use Sonata\AdminBundle\Controller\CRUDController as BaseController;


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

然后你必须覆盖SonataAdminBundle:CRUD:edit.html.twig模板并设置你的javascript事件监听器:

{% 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>

答案 2 :(得分:0)

在进行了永久性的研究以找到一种使用symfony4的ajax和sonata使用动态下拉菜单的方法之后,我想分享我的解决方案对我的实际作用。

就我而言,我有一个地区,这个地区有不同的城市。我有一家公司,首先选择一个地区,然后再选择该地区的城市。

我做了什么:

  1. 为地区创建实体

  2. 为城市创建实体

  3. 进入您的主类(在我的情况下,这是公司实体,并为地区和城市添加两个实体

    /**
     * @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. 更新数据库架构,并使用一些示例数据填充城市和地区字段

  2. 在configureFormFields函数中进入AdminClass并添加以下内容(请确保将'choice_label'选项与您所在地区或城市实体中的相应字段正确使用。

    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. 这应该已经可以很好地工作了。您现在应该可以有一个从属字段。现在让我们来看看AJAX魔术。

  2. 进入您的AdminClass(与configureFields-Class相同)并添加以下内容

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

    }
  1. 现在,您有了一条可以从ajax URL访问的路由。现在,随时随地创建一个新的Controller类...
<?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));
    }
}

...并且不要忘记在您的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)

...最后是在此函数中调用的小模板...

// THIS IS THE cities.html.twig

{% for city in cities %}
<option value="{{ city.id }}">{{ city.name }}</option>
{% endfor %}
  1. 到目前为止一切顺利。现在,我们得到了从ajax调用中获取数据并将其返回到您的奏鸣曲管理员编辑表单的逻辑。现在唯一缺少的是奏鸣曲管理员编辑模板中所需的jquery代码。

进入您的AdminClass并插入以下代码(例如,在configureFormFields之前)

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

现在,我们将创建覆盖默认模板的city_admin.html.twig模板

{% 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 %}

就是这样。应该像魅力一样工作。

答案 3 :(得分:0)

我对您的代码有一个疑问:url: "{{ admin.generateUrl('reloadCities') }}",

如果我错了,请告诉我,但是变量admin可以调用,这要感谢模板扩展:{% extends 'SonataAdminBundle:CRUD:edit.html.twig' %}

仅当我扩展模板时,出现此错误:

  

变量“ base_template”不存在。

有什么想法吗?