我有三个实体:国家,州和城市,具有以下关系:
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”类中
有什么想法吗?
答案 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。