symfony2链式选择器

时间:2012-04-17 06:25:29

标签: symfony

我有三个实体:国家,州和城市,具有以下关系:

Image http://i39.tinypic.com/15gc85u.png

创建城市时,我需要两个选择器,一个用于国家,一个用于城市所属的州。这两个选择器需要链接,因此更改Country将“过滤”另一个选择器中显示的状态。

我发现tutorial显示了如何使用表单事件执行此操作,但他们的示例并不是我的情况。我的实体城市与国家实体没有直接关系(它们通过国家间接相关),因此,当以城市形式设置国家字段时(在CityType类中),我被迫将该字段声明为{{1}你可以在下面的代码中看到:

'property_path'=>false

问题在于,当我尝试编辑现有城市时,默认情况下不会在表单中选择相关的国家/地区。如果我删除了行class CityType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder->add('country', 'entity', array( 'class'=>'TestBundle:Country', 'property'=>'name', 'property_path'=>false //Country is not directly related to City )); $builder->add('name'); $factory = $builder->getFormFactory(); $refreshStates = function ($form, $country) use ($factory) { $form->add($factory->createNamed('entity', 'state', null, array( 'class' => 'Test\TestBundle\Entity\State', 'property' => 'name', 'query_builder' => function (EntityRepository $repository) use ($country) { $qb = $repository->createQueryBuilder('state') ->innerJoin('state.country', 'country'); if($country instanceof Country) { $qb->where('state.country = :country') ->setParameter('country', $country); } elseif(is_numeric($country)) { $qb->where('country.id = :country') ->setParameter('country', $country); } else { $qb->where('country.name = :country') ->setParameter('country', "Venezuela");; } return $qb; } ))); }; $builder->addEventListener(FormEvents::PRE_SET_DATA, function (DataEvent $event) use ($refreshStates) { $form = $event->getForm(); $data = $event->getData(); if($data == null) return; if($data instanceof City){ if($data->getId()) { //An existing City $refreshStates($form, $data->getState()->getCountry()); }else{ //A new City $refreshStates($form, null); } } }); $builder->addEventListener(FormEvents::PRE_BIND, function (DataEvent $event) use ($refreshStates) { $form = $event->getForm(); $data = $event->getData(); if(array_key_exists('country', $data)) { $refreshStates($form, $data['country']); } }); } public function getName() { return 'city'; } public function getDefaultOptions(array $options) { return array('data_class' => 'Test\TestBundle\Entity\City'); } } ,我得到(不足为奇)错误消息:

属性“country”和方法“getCountry()”以及方法“isCountry()”都不存在于“Test \ TestBundle \ Entity \ City”类中

有什么想法吗?

3 个答案:

答案 0 :(得分:25)

好的,我终于想出了如何正确地做到这一点:

namespace Test\TestBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Event\DataEvent;

use Test\TestBundle\Entity\Country;
use Test\TestBundle\Entity\State;
use Test\TestBundle\Entity\City;


class CityType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('name');

        $factory = $builder->getFormFactory();

        $refreshStates = function ($form, $country) use ($factory) {
            $form->add($factory->createNamed('entity','state', null, array(
                'class'         => 'Test\TestBundle\Entity\State',
                'property'      => 'name',
                'empty_value'   => '-- Select a state --',
                'query_builder' => function (EntityRepository $repository) use ($country) {
                    $qb = $repository->createQueryBuilder('state')
                        ->innerJoin('state.country', 'country');

                    if ($country instanceof Country) {
                        $qb->where('state.country = :country')
                            ->setParameter('country', $country);
                    } elseif (is_numeric($country)) {
                        $qb->where('country.id = :country')
                            ->setParameter('country', $country);
                    } else {
                        $qb->where('country.name = :country')
                            ->setParameter('country', null);
                    }

                    return $qb;
               })
           ));
        };

        $setCountry = function ($form, $country) use ($factory) {
            $form->add($factory->createNamed('entity', 'country', null, array(
                'class'         => 'TestBundle:Country', 
                'property'      => 'name', 
                'property_path' => false,
                'empty_value'   => '-- Select a country --',
                'data'          => $country,
            )));
        };

        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (DataEvent $event) use ($refreshStates, $setCountry) {
            $form = $event->getForm();
            $data = $event->getData();

            if ($data == null) {
                return;
            }

            if ($data instanceof City) {
                $country = ($data->getId()) ? $data->getState()->getCountry() : null ;
                $refreshStates($form, $country);
                $setCountry($form, $country);
            }
        });

        $builder->addEventListener(FormEvents::PRE_BIND, function (DataEvent $event) use ($refreshStates) {
            $form = $event->getForm();
            $data = $event->getData();

            if(array_key_exists('country', $data)) {
                $refreshStates($form, $data['country']);
            }
        });
    }

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

    public function getDefaultOptions(array $options)
    {
        return array('data_class' => 'Test\TestBundle\Entity\City');
    }
}

jQuery AJAX选择器

$(document).ready(function () {
    $('#city_country').change(function(){
        $('#city_state option:gt(0)').remove();
        if($(this).val()){
            $.ajax({
                type: "GET",
                data: "country_id=" + $(this).val(),
                url: Routing.generate('state_list'),
                success: function(data){
                    $('#city_state').append(data);
                }
            });
        }
    });
});

我希望这对面临同样情况的其他人有所帮助。

答案 1 :(得分:7)

由于您使用此方法的链接已关闭,我决定补充您的优秀答案,以便任何人都可以使用它:

为了执行以下javascript命令:

url: Routing.generate('state_list'),

您需要安装可在here中找到的FOSJsRoutingBundle。

注意:在捆绑包的“读我”部分中有安装说明,但缺少某些内容。如果您使用deps:

[FOSJsRoutingBundle]
git=git://github.com/FriendsOfSymfony/FOSJsRoutingBundle.git
target=/bundles/FOS/JsRoutingBundle

您必须在接下来的步骤之前运行php bin/vendors update

我仍在尝试找出routing.yml中需要什么路由才能使此解决方案正常工作。一发现我就会编辑这个答案。

答案 2 :(得分:0)

链接的选择框需要专用的FieldType。还有一个xhr控制器,它可以根据传递的参数返回子选项。 Ofcourse property_path应该设置为false。