Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>If anybody else has a similar issue, I eventually got it right with event subscribers for each field, with help from <a href="http://showmethecode.es/php/symfony/symfony2-selects-dependientes-mediante-eventos/">this site</a> (translate it for those non-spanish speaking folk among us).</p> <p>Bascially, what I did was create a new Subscriber class for each field, including province, and then just created a query builder inside each of them to populate their values with those from the preceding fields. The code is shown below. </p> <p>AddProvinceFieldSubscriber.php</p> <pre><code>class AddProvinceFieldSubscriber implements EventSubscriberInterface { private $factory; private $fieldName; private $type; public function __construct(FormFactoryInterface $factory, $fieldName) { $this-&gt;factory = $factory; $this-&gt;fieldName = $fieldName . 'Province'; $this-&gt;type = $fieldName; } public static function getSubscribedEvents() { return array( FormEvents::PRE_SET_DATA =&gt; 'preSetData', FormEvents::PRE_SUBMIT =&gt; 'preSubmit', ); } private function addProvinceForm(FormInterface $form, $province) { $form-&gt;add($this-&gt;factory-&gt;createNamed($this-&gt;fieldName, 'entity', $province, array( 'class' =&gt; 'MyThing\MainBundle\Entity\Province', 'mapped' =&gt; false, 'empty_value' =&gt; 'Select a province', 'query_builder' =&gt; function (EntityRepository $repository) { $qb = $repository-&gt;createQueryBuilder('p'); return $qb; }, 'auto_initialize' =&gt; false, 'attr' =&gt; array( 'class' =&gt; 'province ' . $this-&gt;type .'-province', 'data-show' =&gt; $this-&gt;type . '-city', ) ))); } public function preSetData(FormEvent $event) { $form = $event-&gt;getForm(); $data = $event-&gt;getData(); if (null === $data) return; $fieldName = 'get' . ucwords($this-&gt;type) . 'Suburb'; $province = ($data-&gt;$fieldName()) ? $data-&gt;$fieldName()-&gt;getCity()-&gt;getProvince() : null; $this-&gt;addProvinceForm($form, $province); } public function preSubmit(FormEvent $event) { $form = $event-&gt;getForm(); $data = $event-&gt;getData(); if (null === $data) return; $province = array_key_exists($this-&gt;fieldName, $data) ? $data[$this-&gt;fieldName] : null; $this-&gt;addProvinceForm($form, $province); } } </code></pre> <p>AddCityFieldSubscriber.php</p> <pre><code>class AddCityFieldSubscriber implements EventSubscriberInterface { private $factory; private $fieldName; private $provinceName; private $suburbName; private $type; public function __construct(FormFactoryInterface $factory, $fieldName) { $this-&gt;factory = $factory; $this-&gt;fieldName = $fieldName . 'City'; $this-&gt;provinceName = $fieldName . 'Province'; $this-&gt;suburbName = $fieldName . 'Suburb'; $this-&gt;type = $fieldName; } public static function getSubscribedEvents() { return array( FormEvents::PRE_SET_DATA =&gt; 'preSetData', FormEvents::PRE_SUBMIT =&gt; 'preSubmit', ); } private function addCityForm(FormInterface $form, $city, $province) { $form-&gt;add($this-&gt;factory-&gt;createNamed($this-&gt;fieldName, 'entity', $city, array( 'class' =&gt; 'MyThing\MainBundle\Entity\City', 'empty_value' =&gt; 'Select a city', 'mapped' =&gt; false, 'query_builder' =&gt; function (EntityRepository $repository) use ($province) { $qb = $repository-&gt;createQueryBuilder('c') -&gt;innerJoin('c.province', 'province'); if ($province instanceof Province) { $qb-&gt;where('c.province = :province') -&gt;setParameter('province', $province); } elseif (is_numeric($province)) { $qb-&gt;where('province.id = :province') -&gt;setParameter('province', $province); } else { $qb-&gt;where('province.provinceName = :province') -&gt;setParameter('province', null); } return $qb; }, 'auto_initialize' =&gt; false, 'attr' =&gt; array( 'class' =&gt; 'city ' . $this-&gt;type . '-city', 'data-show' =&gt; $this-&gt;type . '-suburb', ) ))); } public function preSetData(FormEvent $event) { $data = $event-&gt;getData(); $form = $event-&gt;getForm(); if (null === $data) { return; } $fieldName = 'get' . ucwords($this-&gt;suburbName); $city = ($data-&gt;$fieldName()) ? $data-&gt;$fieldName()-&gt;getCity() : null; $province = ($city) ? $city-&gt;getProvince() : null; $this-&gt;addCityForm($form, $city, $province); } public function preSubmit(FormEvent $event) { $data = $event-&gt;getData(); $form = $event-&gt;getForm(); if (null === $data) return; $city = array_key_exists($this-&gt;fieldName, $data) ? $data[$this-&gt;fieldName] : null; $province = array_key_exists($this-&gt;provinceName, $data) ? $data[$this-&gt;provinceName] : null; $this-&gt;addCityForm($form, $city, $province); } } </code></pre> <p>And finally AddSuburbFieldSubscriber.php</p> <pre><code>class AddSuburbFieldSubscriber implements EventSubscriberInterface { private $factory; private $fieldName; private $type; public function __construct(FormFactoryInterface $factory, $fieldName) { $this-&gt;factory = $factory; $this-&gt;fieldName = $fieldName . 'Suburb'; $this-&gt;type = $fieldName; } public static function getSubscribedEvents() { return array( FormEvents::PRE_SET_DATA =&gt; 'preSetData', FormEvents::PRE_SUBMIT =&gt; 'preSubmit', ); } private function addSuburbForm(FormInterface $form, $city) { $form-&gt;add($this-&gt;factory-&gt;createNamed($this-&gt;fieldName, 'entity', null, array( 'class' =&gt; 'MyThing\MainBundle\Entity\Suburb', 'empty_value' =&gt; 'Select a suburb', 'query_builder' =&gt; function (EntityRepository $repository) use ($city) { $qb = $repository-&gt;createQueryBuilder('s') -&gt;innerJoin('s.city', 'city'); if ($city instanceof City) { $qb-&gt;where('s.city = :city') -&gt;setParameter('city', $city); } elseif (is_numeric($city)) { $qb-&gt;where('city.id = :city') -&gt;setParameter('city', $city); } else { $qb-&gt;where('city.cityName = :city') -&gt;setParameter('city', null); } $sql = $qb-&gt;getQuery()-&gt;getSQL(); return $qb; }, 'auto_initialize' =&gt; false, 'attr' =&gt; array( 'class' =&gt; 'suburb ' . $this-&gt;type . '-suburb', ), ))); } public function preSetData(FormEvent $event) { $data = $event-&gt;getData(); $form = $event-&gt;getForm(); if (null === $data) return; $fieldName = 'get' . ucwords($this-&gt;fieldName); $city = ($data-&gt;$fieldName()) ? $data-&gt;$fieldName()-&gt;getCity() : null; $this-&gt;addSuburbForm($form, $city); } public function preSubmit(FormEvent $event) { $data = $event-&gt;getData(); $form = $event-&gt;getForm(); if (null === $data) return; $city = array_key_exists($this-&gt;type . 'City', $data) ? $data[$this-&gt;type . 'City'] : null; $this-&gt;addSuburbForm($form, $city); } } </code></pre> <p>I had to add some extra stuff in there, but you get the gist of it. </p> <p>In my form type I simply added the following:</p> <pre><code>$builder -&gt;addEventSubscriber(new AddProvinceFieldSubscriber($factory, 'postal')) -&gt;addEventSubscriber(new AddCityFieldSubscriber($factory, 'postal')) -&gt;addEventSubscriber(new AddSuburbFieldSubscriber($factory, 'postal')) //... </code></pre> <p>And happy days! Hope this helps somebody. </p> <p>Also, I added the <code>data-show</code> attributes to simplify my AJAX process, just in case somebody was wondering. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload