我有三个实体:国家,州和城市,具有以下关系:
图片http://i39.tinypic.com/15gc85u.png
在创建城市时,我想要两个选择器,一个用于国家,一个用于城市所属的州.这两个选择器需要链接,因此更改Country将"过滤"另一个选择器中显示的状态.
我找到了一个教程,展示了如何使用Form Events执行此操作,但他们的示例并不是我的情况.我的实体城市它不是直接关系到国家的实体(它们是通过国家间接的关系),因此,在城市形态设置国家字段时(类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');
}
}
Run Code Online (Sandbox Code Playgroud)
问题是,当我尝试编辑现有城市时,默认情况下不会在表单中选择相关的国家/地区.如果我删除该行'property_path'=>false(不足为奇)错误消息:
属性"country"和方法"getCountry()"以及方法"isCountry()"都不存在于"Test\TestBundle\Entity\City"类中
有任何想法吗?
Dav*_*eto 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');
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
});
}
});
});
Run Code Online (Sandbox Code Playgroud)
我希望这对面临同样情况的其他人有所帮助.
由于您与此方法的链接已关闭,我决定补充您的优秀答案,以便任何人都可以使用它:
为了执行以下javascript命令:
url: Routing.generate('state_list'),
Run Code Online (Sandbox Code Playgroud)
您需要安装可在此处找到的FOSJsRoutingBundle .
注意:在捆绑包的read me部分有安装说明,但有些东西丢失了.如果您使用deps:
[FOSJsRoutingBundle]
git=git://github.com/FriendsOfSymfony/FOSJsRoutingBundle.git
target=/bundles/FOS/JsRoutingBundle
Run Code Online (Sandbox Code Playgroud)
您必须php bin/vendors update在下一步之前运行.
我仍在尝试找出routing.yml中需要什么路由才能使此解决方案正常工作.一发现我就会编辑这个答案.