2013-12-12 6 views
9

Sto costruendo un'app Symfony e utilizzando gli eventi di modulo con alcuni jquery/ajax per fare l'intera cosa "stato/località". Ho un piccolo problema però, sto usando il formato Provincia -> Città -> Periferia. Ora, per quanto posso dire il mio codice va bene, ma quando l'esecuzione colpisce la sezione in cui aggiungo un listener al "City" selezionare, getta un errore che dice quanto segue:Aggiungi listener di eventi per formare l'elemento aggiunto dall'ascoltatore di eventi

The child with the name "physicalCity" does not exist.

Questo, ovviamente, succede quando provo ad aggiungere un listener di eventi al campo appena creato, aggiungendo così un listener di eventi a un elemento creato da un listener di eventi?

Una sezione del codice è sotto ... Cosa sto facendo male? Qualsiasi aiuto sarebbe molto apprezzato!

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

risposta

14

Se qualcun altro ha un problema simile, alla fine ho azzeccato con gli abbonati di eventi per ogni campo, con l'aiuto di this site (tradurlo per quelli non-spagnolo gente parlare tra di noi).

Bazzialmente, quello che ho fatto è stato creare una nuova classe di Sottoscrittore per ogni campo, inclusa la provincia, e quindi ho appena creato un generatore di query all'interno di ognuno di essi per popolare i loro valori con quelli dei campi precedenti. Il codice è mostrato sotto.

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

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

ho dovuto aggiungere alcune cose in più in là, ma si ottiene la in sostanza.

Nel mio tipo di modulo ho semplicemente aggiunto il seguente:

$builder 
    ->addEventSubscriber(new AddProvinceFieldSubscriber($factory, 'postal')) 
    ->addEventSubscriber(new AddCityFieldSubscriber($factory, 'postal')) 
    ->addEventSubscriber(new AddSuburbFieldSubscriber($factory, 'postal')) 
//... 

e felice giorni! Spero che questo aiuti qualcuno.

Inoltre, ho aggiunto gli attributi data-show per semplificare il mio processo AJAX, nel caso in cui qualcuno si stesse chiedendo.

+2

Ottimo lavoro! Hai salvato la mia giornata, grazie! :) – Gianluca78

+0

Funziona anche mentre modifichi questo contenuto. Per me no. Potresti anche rispondere con quale versione di Symfony hai lavorato? – Jeet

+0

Sì, ha funzionato per me. Questo era Sf 2.4 Credo, non ricordo esattamente. Basta provare e giocare con i casi di decisione del generatore di query. Forse c'è qualcosa di strano nel tuo caso. E installa XDebug! Aiuta molto – iLikeBreakfast

Problemi correlati