2013-12-12 4 views
9

Я создаю приложение 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); 
      } 
     ); 
} 

ответ

14

Если кто-то есть подобная проблема, я в конце концов получил это право с абонентами событий для каждого поля, с помощью this site (перевести его для тех, кто не-испаноязычных фолка среди нас).

Баскиально, что я сделал, это создать новый класс 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, на случай, если кто-то задается вопросом.

+2

Отличная работа! Вы спасли мой день, спасибо! :) – Gianluca78

+0

Работает ли это во время редактирования этого контента. Для меня это не так. Также не могли бы вы ответить на вопрос, с какой версией Symfony вы это работали? – Jeet

+0

Да, это сработало для меня. Это был Sf 2.4, я считаю, точно не помню. Просто попробуйте сыграть с решениями по построению запросов. Возможно, в вашем случае что-то странное. И установите XDebug! Помогает много. – iLikeBreakfast

Смежные вопросы