Я создаю приложение Symfony и использую события формы с некоторым jquery / ajax, чтобы сделать все «состояние / местность». У меня есть небольшая проблема, хотя, я использую формат Province -> City -> Suburb. Теперь, насколько я могу сказать, мой код в порядке, но когда выполнение попадает в раздел, где я добавляю слушателя к элементу «Город», он выдает сообщение об ошибке:
The child with the name "physicalCity" does not exist.
Это, очевидно, происходит, когда я пытаюсь добавить слушателя событий в вновь созданное поле, добавляя, таким образом, прослушиватель событий к элементу, созданному прослушивателем событий?
Раздел кода ниже … Что я делаю неправильно? Любая помощь будет очень высоко ценится!
public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('schoolName') ->add('physicalProvince', 'entity', array( 'mapped' => false, 'class' => 'MY\MainBundle\Entity\Province', 'empty_value' => 'Select a province', 'attr' => array( 'class' => 'province', 'data-show' => 'physical-city', ) )); /* * For the physical cities */ $physicalCityModifier = function(FormInterface $form, Province $province = null) { if (null !== $province) $cities = $province->getCities(); else $cities = array(); $form->add('physicalCity', 'entity', array( 'mapped' => false, 'class' => 'MY\MainBundle\Entity\City', 'empty_value' => 'Select a province first', 'choices' => $cities, 'attr' => array( 'class' => 'city physical-city', 'data-show' => 'physical-suburb' ) )); }; $builder->addEventListener( FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($physicalCityModifier) { $data = $event->getData(); if (is_object($data->getPhysicalSuburb())) $province = $data->getPhysicalSuburb()->getCity()->getProvince(); else $province = null; $physicalCityModifier($event->getForm(), $province); } ); $builder->get('physicalProvince')->addEventListener( FormEvents::POST_SUBMIT, function (FormEvent $event) use ($physicalCityModifier) { $province = $event->getForm()->getData(); $physicalCityModifier($event->getForm()->getParent(), $province); } ); /* * For the physical suburbs */ $physicalSuburbModifier = function(FormInterface $form, City $city = null) { if (null !== $city) $suburbs = $city->getSuburbs(); else $suburbs = array(); $form->add('physicalSuburb', null, array( 'choices' => $suburbs, 'empty_value' => 'Select a city first', 'attr' => array( 'class' => 'physical-suburb' ), )); }; $builder->addEventListener( FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($physicalSuburbModifier) { $data = $event->getData(); if (is_object($data->getCity())) $city = $data->getCity(); else $city = null; $physicalSuburbModifier($event->getForm(), $city); } ); $builder->get('physicalCity')->addEventListener( FormEvents::POST_SUBMIT, function(FormEvent $event) use ($physicalSuburbModifier) { $city = $event->getForm()->getData(); $physicalSuburbModifier($event->getForm()->getParent(), $city); } ); }
Если у кого-то еще есть подобная проблема, я в конечном итоге правильно ее понял с подписчиками событий для каждого поля, с помощью этого сайта (переведите его для тех, кто не говорит по-испански, среди нас).
Басциально, что я сделал, это создать новый класс Subscriber для каждого поля, включая провинцию, а затем просто создать построитель запросов внутри каждого из них, чтобы заполнить их значения теми, что указаны в предыдущих полях. Код показан ниже.
AddProvinceFieldSubscriber.php
class AddProvinceFieldSubscriber implements EventSubscriberInterface { private $factory; private $fieldName; private $type; public function __construct(FormFactoryInterface $factory, $fieldName) { $this->factory = $factory; $this->fieldName = $fieldName . 'Province'; $this->type = $fieldName; } public static function getSubscribedEvents() { return array( FormEvents::PRE_SET_DATA => 'preSetData', FormEvents::PRE_SUBMIT => 'preSubmit', ); } private function addProvinceForm(FormInterface $form, $province) { $form->add($this->factory->createNamed($this->fieldName, 'entity', $province, array( 'class' => 'MyThing\MainBundle\Entity\Province', 'mapped' => false, 'empty_value' => 'Select a province', 'query_builder' => function (EntityRepository $repository) { $qb = $repository->createQueryBuilder('p'); return $qb; }, 'auto_initialize' => false, 'attr' => array( 'class' => 'province ' . $this->type .'-province', 'data-show' => $this->type . '-city', ) ))); } public function preSetData(FormEvent $event) { $form = $event->getForm(); $data = $event->getData(); if (null === $data) return; $fieldName = 'get' . ucwords($this->type) . 'Suburb'; $province = ($data->$fieldName()) ? $data->$fieldName()->getCity()->getProvince() : null; $this->addProvinceForm($form, $province); } public function preSubmit(FormEvent $event) { $form = $event->getForm(); $data = $event->getData(); if (null === $data) return; $province = array_key_exists($this->fieldName, $data) ? $data[$this->fieldName] : null; $this->addProvinceForm($form, $province); } }
AddCityFieldSubscriber.php
class AddCityFieldSubscriber implements EventSubscriberInterface { private $factory; private $fieldName; private $provinceName; private $suburbName; private $type; public function __construct(FormFactoryInterface $factory, $fieldName) { $this->factory = $factory; $this->fieldName = $fieldName . 'City'; $this->provinceName = $fieldName . 'Province'; $this->suburbName = $fieldName . 'Suburb'; $this->type = $fieldName; } public static function getSubscribedEvents() { return array( FormEvents::PRE_SET_DATA => 'preSetData', FormEvents::PRE_SUBMIT => 'preSubmit', ); } private function addCityForm(FormInterface $form, $city, $province) { $form->add($this->factory->createNamed($this->fieldName, 'entity', $city, array( 'class' => 'MyThing\MainBundle\Entity\City', 'empty_value' => 'Select a city', 'mapped' => false, 'query_builder' => function (EntityRepository $repository) use ($province) { $qb = $repository->createQueryBuilder('c') ->innerJoin('c.province', 'province'); if ($province instanceof Province) { $qb->where('c.province = :province') ->setParameter('province', $province); } elseif (is_numeric($province)) { $qb->where('province.id = :province') ->setParameter('province', $province); } else { $qb->where('province.provinceName = :province') ->setParameter('province', null); } return $qb; }, 'auto_initialize' => false, 'attr' => array( 'class' => 'city ' . $this->type . '-city', 'data-show' => $this->type . '-suburb', ) ))); } public function preSetData(FormEvent $event) { $data = $event->getData(); $form = $event->getForm(); if (null === $data) { return; } $fieldName = 'get' . ucwords($this->suburbName); $city = ($data->$fieldName()) ? $data->$fieldName()->getCity() : null; $province = ($city) ? $city->getProvince() : null; $this->addCityForm($form, $city, $province); } public function preSubmit(FormEvent $event) { $data = $event->getData(); $form = $event->getForm(); if (null === $data) return; $city = array_key_exists($this->fieldName, $data) ? $data[$this->fieldName] : null; $province = array_key_exists($this->provinceName, $data) ? $data[$this->provinceName] : null; $this->addCityForm($form, $city, $province); } }
И, наконец, AddSuburbFieldSubscriber.php
class AddSuburbFieldSubscriber implements EventSubscriberInterface { private $factory; private $fieldName; private $type; public function __construct(FormFactoryInterface $factory, $fieldName) { $this->factory = $factory; $this->fieldName = $fieldName . 'Suburb'; $this->type = $fieldName; } public static function getSubscribedEvents() { return array( FormEvents::PRE_SET_DATA => 'preSetData', FormEvents::PRE_SUBMIT => 'preSubmit', ); } private function addSuburbForm(FormInterface $form, $city) { $form->add($this->factory->createNamed($this->fieldName, 'entity', null, array( 'class' => 'MyThing\MainBundle\Entity\Suburb', 'empty_value' => 'Select a suburb', 'query_builder' => function (EntityRepository $repository) use ($city) { $qb = $repository->createQueryBuilder('s') ->innerJoin('s.city', 'city'); if ($city instanceof City) { $qb->where('s.city = :city') ->setParameter('city', $city); } elseif (is_numeric($city)) { $qb->where('city.id = :city') ->setParameter('city', $city); } else { $qb->where('city.cityName = :city') ->setParameter('city', null); } $sql = $qb->getQuery()->getSQL(); return $qb; }, 'auto_initialize' => false, 'attr' => array( 'class' => 'suburb ' . $this->type . '-suburb', ), ))); } public function preSetData(FormEvent $event) { $data = $event->getData(); $form = $event->getForm(); if (null === $data) return; $fieldName = 'get' . ucwords($this->fieldName); $city = ($data->$fieldName()) ? $data->$fieldName()->getCity() : null; $this->addSuburbForm($form, $city); } public function preSubmit(FormEvent $event) { $data = $event->getData(); $form = $event->getForm(); if (null === $data) return; $city = array_key_exists($this->type . 'City', $data) ? $data[$this->type . 'City'] : null; $this->addSuburbForm($form, $city); } }
Я должен был добавить туда лишний материал, но вы получите его суть.
В моей форме типа я просто добавил следующее:
$builder ->addEventSubscriber(new AddProvinceFieldSubscriber($factory, 'postal')) ->addEventSubscriber(new AddCityFieldSubscriber($factory, 'postal')) ->addEventSubscriber(new AddSuburbFieldSubscriber($factory, 'postal')) //...
И счастливые дни! Надеюсь, это поможет кому-то.
Кроме того, я добавил атрибуты data-show
чтобы упростить мой процесс AJAX, на случай, если кто-то задается вопросом.