21

次の関係を持つ国、州、都市の 3 つのエンティティがあります。

画像

都市を作成するとき、2 つのセレクターが必要です。1 つは国用で、もう 1 つは都市が属する州用です。これら 2 つのセレクターは連鎖する必要があるため、Country を変更すると、他のセレクターに表示される州が「フィルター処理」されます。

Form Events を使用してこれを行う方法を示すチュートリアルを見つけましたが、その例は私の場合ではありません。私のエンティティ City は、Country エンティティに直接関連していません (State を介して間接的に関連しています)。そのため、City フォーム (クラス CityType 内) で国フィールドを設定すると、ご覧のようにそのフィールドを宣言する'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');
    }
}

問題は、既存の都市を編集しようとすると、関連する国がフォームでデフォルトで選択されていないことです。行を削除すると、 'property_path'=>false(当然のことながら) エラー メッセージが表示されます。

クラス「Test\TestBundle\Entity\City」には、プロパティ「country」もメソッド「getCountry()」もメソッド「isCountry()」も存在しません

何か案は?

4

3 に答える 3

25

OK、私はついにそれを適切に行う方法を見つけました:

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

これが同じ状況に直面している他の誰かに役立つことを願っています。

于 2012-06-15T23:56:35.447 に答える
7

このアプローチへのリンクがダウンしているので、誰もがそれを使用できるように、私はあなたの優れた答えを補完することにしました:

次のjavascriptコマンドを実行するには:

url: Routing.generate('state_list'),

ここにあるFOSJsRoutingBundleをインストールする必要があります

注意:バンドルの「readme」セクションにインストール手順がありますが、何か足りないものがあります。これでdepsを使用する場合:

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

php bin/vendors update次の手順の前にを実行する必要があります。

このソリューションが機能するために、routing.ymlで必要なルートを見つけようとしています。発見したらすぐにこの回答を編集します。

于 2012-08-21T10:06:38.003 に答える
0

チェーン選択ボックス専用のFieldTypeが必要になります。また、渡されたパラメーターに基づいて子オプションを返すことができるxhrコントローラー。もちろん、property_pathはfalseに設定する必要があります。

于 2012-04-18T03:24:31.220 に答える