【问题标题】:Best way to make a Symfony2 Form with multi select in cascade在级联中使用多选制作 Symfony2 表单的最佳方法
【发布时间】:2012-09-07 12:28:17
【问题描述】:

我有 3 个实体(国家、地区、城市)

namespace ****\****Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

class Country
{
  private $id;

  private $name;

  /**
   * @var integer $regions
   * 
   * @ORM\OneToMany(targetEntity="Region", mappedBy="Country")
   */
  protected $regions;
  //...
}

class Region
{
  private $id;

  private $name;

  /**
   * @var integer $country
   *
   * @Assert\Type(type="****\***Bundle\Entity\Country")
   * @ORM\ManyToOne(targetEntity="Country", inversedBy="regions")
   * @ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=false)
   */
  private $country;

  /**
   * @ORM\OneToMany(targetEntity="City", mappedBy="Region")
   */
  protected $cities;
}

class City
{
    private $id;

    private $name;

    /**
     * @var integer $region
     *
     * @Assert\Type(type="****\****Bundle\Entity\Region")
     * @ORM\ManyToOne(targetEntity="Region", inversedBy="cities")
     * @ORM\JoinColumn(name="region_id", referencedColumnName="id", nullable=false)
     */
    private $region;

    /**
     * @ORM\OneToMany(targetEntity="Company", mappedBy="City")
     */
    protected $companys;
//...
}

这是我的城市表单类:

namespace ****\****Bundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class CityType extends AbstractType
{

  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $builder
            ->add('name')
            ->add('region');
  }

  public function setDefaultOptions(OptionsResolverInterface $resolver)
  {
    $resolver->setDefaults(array(
        'data_class' => '****\****Bundle\Entity\City',
    ));
  }

  public function getName()
  {
    return 'city';
  }
}

这将创建一个基本的 HTML5 表单,其中包含名称的文本框和所有区域都可用的选择框。

我的问题是添加第一个 SelectBox 的最佳方法是什么,这将允许我选择国家以过滤第二个 SelectBox 并减少区域的选择数量?

事件监听器? 事件调度器组件?

【问题讨论】:

    标签: forms symfony doctrine-orm symfony-2.1


    【解决方案1】:

    不,EventListener 和 Event dispatcher 用于发生在服务器上的事件,而不是在客户端发生的事件。您需要使用 Javascript。当其中一个选择框发生变化时,这应该触发一个 javascript 函数并进行 AJAX 调用并用该调用的结果填充另一个选择框,或者使用一些 javascript 代码来选择要在第二个框中显示的选项。

    查看here 了解一些想法

    【讨论】:

      【解决方案2】:

      正如 Carlos Granados 所说,您基本上有两种选择:

      1. 创建一个单独的 Symfony 操作,将国家作为参数,并以 XML 或 JSON 格式返回相关区域的列表。 (您可以使用 Symfony\Component\HttpFoundation\JsonResponse 发送 JSON 响应,但是没有对应的 XmlResponse 类)。每当用户更改当前选定的项目时,使用jQuery(或任何其他 JS 库甚至纯 Javascript)向服务器发送请求。在回调中(当您在 Javascript 中检索响应时),您可以更新区域选择框。您可能会发现jQuery Ajax 的文档很有趣。

      2. 您在 HTML 代码中存储所有国家及其相关区域的列表(您可以使用 JSON 或生成本机 Javascript 数组),每当用户更改国家选择框的值时,您只需替换区域选择框中的区域列表。

      第二种方法对表单的初始加载有较重的负担,因为必须加载所有国家及其相关地区(从数据库或文本文件或您存储它们的任何地方)并以易于使用的格式呈现JS读取。

      但是,第一种方法必须在用户每次选择另一个国家/地区时发送一个请求。此外,您必须执行另一个操作。

      【讨论】:

        【解决方案3】:

        我自己在表格上做这件事。 我更改了字段(产品)并更新了可以测量数量的单位。 我正在使用带有参数的宏来更轻松地对其进行调整。

        宏:

        {% macro javascript_filter_unit(event, selector) %}
        <script>
            $(function(){
                $('#usersection')
                        .on('{{ event }}', '{{ selector }}', function(e){
                            e.preventDefault();
                            if (!$(this).val()) return;
                            $.ajax({
                                $parent: $(this).closest('.child_collection'),
                                url: $(this).attr('data-url'),
                                type: "get",
                                dataType: "json",
                                data: {'id' : $(this).val(), 'repo': $(this).attr('data-repo'), parameter: $(this).attr('data-parameter')},
                                success: function (result) {
                                    if (result['success'])
                                    {
                                        var units = result['units'];
                                        this.$parent.find('.unit').eq(0).html(units);   
                                    }
                                }
                            });
                        })
            });
        
        </script>
        
        {% endmacro %}
        

        ajax 返回一个数组:array('success' => $value, 'units' => $html)。您使用 $html 代码并将其放在要更改的选择位置。 当然需要修改 ajax 调用的 javascript 代码以匹配您的字段。

        您像往常一样调用宏:

        {% import ':Model/Macros:_macros.html.twig' as macros %}
        {{ macros.javascript_filter_unit('change', '.unitTrigger') }}
        

        所以我有两个论据:事件,通常是一个选择的改变。和一个选择器,它的变化会触发 ajax 调用。

        希望对你有帮助。

        【讨论】:

          【解决方案4】:

          正如 Carlos Granados 所说,您必须使用客户端编程:Javascript。 我也遇到了和你一样的问题,并提出了一个非常适合我的解决方案,这里是the snippet on codepen,你可以从(Cascade Ajax Selects)中获得灵感

              //-------------------------------SELECT CASCADING-------------------------//
              var currentCities=[];
          // This is a demo API key that can only be used for a short period of time, and will be unavailable soon. You should rather request your API key (free)  from http://battuta.medunes.net/   
          var BATTUTA_KEY="00000000000000000000000000000000"
              // Populate country select box from battuta API
              url="http://battuta.medunes.net/api/country/all/?key="+BATTUTA_KEY+"&callback=?";
              $.getJSON(url,function(countries)
              {
                  console.log(countries);
                $('#country').material_select();
                  //loop through countries..
                  $.each(countries,function(key,country)
                  {
                      $("<option></option>")
                                      .attr("value",country.code)
                                      .append(country.name)
                                      .appendTo($("#country"));
          
                  }); 
                  // trigger "change" to fire the #state section update process
                  $("#country").material_select('update');
                  $("#country").trigger("change");
          
          
              });
          
              $("#country").on("change",function()
              {
          
                  countryCode=$("#country").val();
          
                  // Populate country select box from battuta API
                  url="http://battuta.medunes.net/api/region/"
                  +countryCode
                  +"/all/?key="+BATTUTA_KEY+"&callback=?";
          
                  $.getJSON(url,function(regions)
                  {
                      $("#region option").remove();
                      //loop through regions..
                      $.each(regions,function(key,region)
                      {
                          $("<option></option>")
                                          .attr("value",region.region)
                                          .append(region.region)
                                          .appendTo($("#region"));
                      });
                      // trigger "change" to fire the #state section update process
                      $("#region").material_select('update');
                      $("#region").trigger("change");
          
                  }); 
          
              });
              $("#region").on("change",function()
              {
          
                  // Populate country select box from battuta API
                  countryCode=$("#country").val();
                  region=$("#region").val();
                  url="http://battuta.medunes.net/api/city/"
                  +countryCode
                  +"/search/?region="
                  +region
                  +"&key="
                  +BATTUTA_KEY
                  +"&callback=?";
          
                  $.getJSON(url,function(cities)
                  {
                      currentCities=cities;
                      var i=0;
                      $("#city option").remove();
          
                      //loop through regions..
                      $.each(cities,function(key,city)
                      {
                          $("<option></option>")
                                          .attr("value",i++)
                                          .append(city.city)
                                  .appendTo($("#city"));
                      });
                      // trigger "change" to fire the #state section update process
                      $("#city").material_select('update');
                      $("#city").trigger("change");
          
                  }); 
          
              }); 
              $("#city").on("change",function()
              {
                currentIndex=$("#city").val();
                currentCity=currentCities[currentIndex];
                city=currentCity.city;
                region=currentCity.region;
                country=currentCity.country;
                lat=currentCity.latitude;
                lng=currentCity.longitude;
                $("#location").html('<i class="fa fa-map-marker"></i> <strong> '+city+"/"+region+"</strong>("+lat+","+lng+")");
              });
             //-------------------------------END OF SELECT CASCADING-------------------------//
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多