2016-07-26 8 views
0

Ich habe ein kleines Problem mit FormEvents, ich möchte 3 Felder dynamisch ausgefüllt werden. Ich erkläre, ich habe 3 Felder: Projekt> Box> Zelle, der Benutzer wählt ein Projekt, die Boxliste wird aktualisiert, er wählt eine Box, die Zellliste wird aktualisiert.Dynamische Generierung für übermittelte Formulare mit Formularereignissen

, es zu tun, ich benutze FormEvent wie die Dokumentation sagen (http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data)

Aber ich habe ein Problem, für nur ein einziges Feld dynamisch aktualisiert, es Arbeit ist, aber nicht für zwei Felder ... Eigentlich ein Benutzer kann ein Projekt auswählen, und wenn er es macht, wird das Feld Feld aktualisiert. Aber, wenn er eine Box wählte, wurde das Zellenfeld nicht aktualisiert ...

Aber ich habe etwas gefunden, die erlauben es zu arbeiten, nur etwas in einem ändern -> add() und umgekehrt - > hinzufügen(). Aber ich will es nicht.

Mein Code ist:

$builder 
    ->add('project', EntityType::class, array(
     'class' => 'AppBundle\Entity\Project', 
     'choice_label' => 'name', 
     'placeholder' => '-- select a project --', 
     'mapped' => false, 
    )) 
    ->add('box', EntityType::class, array(
     'class' => 'AppBundle\Entity\Box', 
     'choice_label' => 'name', 
     'placeholder' => '-- select a box --', 
     'choices' => [], 
    )) 
    ->add('cell', ChoiceType::class, array(
     'placeholder' => '-- select a cell --', 
    )) 
; 

Und wenn ich es ändern:

builder 
    ->add('box', EntityType::class, array(
     'class' => 'AppBundle\Entity\Box', 
     'choice_label' => 'name', 
     'placeholder' => '-- select a box --', 
     // 'choices' => [], 
    )) 
    ->add('project', EntityType::class, array(
     'class' => 'AppBundle\Entity\Project', 
     'choice_label' => 'name', 
     'placeholder' => '-- select a project --', 
     'mapped' => false, 
    )) 

    ->add('cell', ChoiceType::class, array(
     'placeholder' => '-- select a cell --', 
    )) 
; 

Es ist Arbeit ... Aber ich will eine leere Liste für Feld am Start, und ich möchte Projekt vor der Box ...

Ein wenig Präzision, diese Form ist in einer anderen Form als CollectionType eingebettet.

Der gesamte Code dieser Art:

<?php 

namespace AppBundle\Form; 

use Symfony\Bridge\Doctrine\Form\Type\EntityType; 
use Symfony\Component\Form\AbstractType; 
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; 
use Symfony\Component\Form\FormBuilderInterface; 
use Symfony\Component\Form\FormEvent; 
use Symfony\Component\Form\FormEvents; 
use Symfony\Component\Form\FormInterface; 
use Symfony\Component\OptionsResolver\OptionsResolver; 

class TubeType extends AbstractType 
{ 
    /** 
    * @param FormBuilderInterface $builder 
    * @param array $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('project', EntityType::class, array(
       'class' => 'AppBundle\Entity\Project', 
       'choice_label' => 'name', 
       'placeholder' => '-- select a project --', 
       'mapped' => false, 
      )) 
      ->add('box', EntityType::class, array(
       'class' => 'AppBundle\Entity\Box', 
       'choice_label' => 'name', 
       'placeholder' => '-- select a box --', 
       'choices' => [], 
      )) 
      ->add('cell', ChoiceType::class, array(
       'placeholder' => '-- select a cell --', 
      )) 
     ; 

     // MODIFIER 
     $boxModifier = function (FormInterface $form, $project) { 
      $boxes = (null === $project) ? [] : $project->getBoxes(); 

      $form->add('box', EntityType::class, array(
       'class' => 'AppBundle\Entity\Box', 
       'choice_label' => 'name', 
       'placeholder' => '-- select a box --', 
       'choices' => $boxes, 
      )); 
     }; 

     $cellModifier = function(FormInterface $form, $box) { 
      $cells = (null === $box) ? [] : $box->getEmptyCells(); 

      $form->add('cell', ChoiceType::class, array(
       'placeholder' => '-- select a cell --', 
       'choices' => $cells, 
      )); 
     }; 

     // FORM EVENT LISTENER 
     $builder->get('project')->addEventListener(
      FormEvents::POST_SUBMIT, 
      function(FormEvent $event) use ($boxModifier) { 
       $project = $event->getForm()->getData(); 

       $boxModifier($event->getForm()->getParent(), $project); 
      } 
     ); 

     $builder->get('box')->addEventListener(
      FormEvents::POST_SUBMIT, 
      function(FormEvent $event) use ($cellModifier) { 
       $box = $event->getForm()->getData(); 

       $cellModifier($event->getForm()->getParent(), $box); 
      } 
     ); 
    } 

    /** 
    * @param OptionsResolver $resolver 
    */ 
    public function configureOptions(OptionsResolver $resolver) 
    { 
     $resolver->setDefaults(array(
      'data_class' => 'AppBundle\Entity\Tube' 
     )); 
    } 
} 

Vielen Dank an Ihre Hilfe :)

Antwort

0

Sie $builder->addEventListener verwenden sollten. Für mehrere Felder müssen Sie nur dynamische Felder innerhalb des Ereignishandlers FormEvents::PRE_SET_DATA haben. Verwenden Sie auch übergeordnete Felddaten, wie im Dokument erläutert, um untergeordnete Feldauswahlmöglichkeiten abzurufen.

Ich habe diesen Ansatz verwendet, um Land, Staat und Stadt Entitäten in hierarchischen Feldern zu generieren. Lassen Sie es mich wissen, wenn es hilft oder Sie mehr Informationen benötigen.

EDIT: Für größere Logik können Sie eventSubscriber verwenden, die Ihren Code sauber halten und Sie können auch dynamische Teil des Formulars für woanders wieder verwenden.

Für mehrere abhängige hierarchische Felder fügen Sie sie einfach über Bedingungen in der Klasse eventSubscriber hinzu.

-Update mit Codeausschnitt:

Hier ist ein Spaziergang durch auf Code-Schnipsel, das für mich in Symfony 2.7

arbeitete

Hinweis: Ich habe nicht das dynamische HTML-Feld ersetzen, wie in das Dokument, stattdessen über jQuery ich einfach Kind-Optionen wie ausgewählte Eltern Option sammeln und diese ausfüllen. Bei der Übergabe erkennt das Formular korrekte untergeordnete Optionen gemäß eventSubscriber Kontext.Also hier ist, wie Sie könnte es tun:

In Ihrer Eltern Formulartyp (wo Sie alle drei Felder haben) rufen eine eventSubscriber stattdessen diese drei Felder definieren:

$builder->add(); // all other fields.. 
$builder->addEventSubscriber(new DynamicFieldsSubscriber()); 

eine eventSubscriber erstellen, wie in der Definition doc, hier der Dateiname DynamicFieldsSubscriber

<?php 
namespace YourBundle\Form\EventListener; 

use Symfony\Component\Form\FormEvent; 
use Symfony\Component\Form\FormEvents; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Doctrine\ORM\EntityRepository; 
use Symfony\Component\Form\FormInterface; 

class DynamicFieldsSubscriber implements EventSubscriberInterface 
{ 

    /** 
    * Define the events we need to subscribe 
    * @return type 
    */ 
    public static function getSubscribedEvents() 
    { 
     return array(
      FormEvents::PRE_SET_DATA => 'preSetData', // check preSetData method below 
      FormEvents::PRE_SUBMIT => 'preSubmitData', // check preSubmitData method below 
     ); 
    } 

    /** 
    * Handling form fields before form renders. 
    * @param FormEvent $event 
    */ 
    public function preSetData(FormEvent $event) 
    { 
     $location = $event->getData(); 
     // Location is the main entity which is obviously form's (data_class) 
     $form = $event->getForm(); 

     $country = ""; 
     $state = ""; 
     $district = ""; 

     if ($location) { 
      // collect preliminary values for 3 fields. 
      $country = $location->getCountry(); 
      $state = $location->getState(); 
      $district = $location->getDistrict(); 
     } 
     // Add country field as its static. 
     $form->add('country', 'entity', array(
      'class' => 'YourBundle:Country', 
      'label' => 'Select Country', 
      'empty_value' => ' -- Select Country -- ', 
      'required' => true, 
      'query_builder' => function (EntityRepository $er) { 
       return $er->createQueryBuilder('c') 
         ->where('c.status = ?1') 
         ->setParameter(1, 1); 
      } 
     )); 
     // Now add all child fields. 
     $this->addStateField($form, $country); 
     $this->addDistrictField($form, $state); 
    } 

    /** 
    * Handling Form fields before form submits. 
    * @param FormEvent $event 
    */ 
    public function preSubmitData(FormEvent $event) 
    { 
     $form = $event->getForm(); 
     $data = $event->getData(); 

     // Here $data will be in array format. 

     // Add property field if parent entity data is available. 
     $country = isset($data['country']) ? $data['country'] : $data['country']; 
     $state = isset($data['state']) ? $data['state'] : $data['state']; 
     $district = isset($data['district']) ? $data['district'] : $data['district']; 

     // Call methods to add child fields. 
     $this->addStateField($form, $country); 
     $this->addDistrictField($form, $state); 
    } 

    /** 
    * Method to Add State Field. (first dynamic field.) 
    * @param FormInterface $form 
    * @param type $country 
    */ 
    private function addStateField(FormInterface $form, $country = null) 
    { 
     $countryCode = (is_object($country)) ? $country->getCountryCode() : $country; 
     // $countryCode is dynamic here, collected from the event based data flow. 
     $form->add('state', 'entity', array(
      'class' => 'YourBundle:State', 
      'label' => 'Select State', 
      'empty_value' => ' -- Select State -- ', 
      'required' => true, 
      'attr' => array('class' => 'state'), 
      'query_builder' => function (EntityRepository $er) use($countryCode) { 
       return $er->createQueryBuilder('u') 
         ->where('u.countryCode = :countrycode') 
         ->setParameter('countrycode', $countryCode); 
      } 
     )); 
    } 

    /** 
    * Method to Add District Field, (second dynamic field) 
    * @param FormInterface $form 
    * @param type $state 
    */ 
    private function addDistrictField(FormInterface $form, $state = null) 
    { 
     $stateCode = (is_object($state)) ? $state->getStatecode() : $state; 
     // $stateCode is dynamic in here collected from event based data flow. 
     $form->add('district', 'entity', array(
      'class' => 'YourBundle:District', 
      'label' => 'Select District', 
      'empty_value' => ' -- Select District -- ', 
      'required' => true, 
      'attr' => array('class' => 'district'), 
      'query_builder' => function (EntityRepository $er) use($stateCode) { 
       return $er->createQueryBuilder('s') 
         ->where('s.stateCode = :statecode') 
         ->setParameter('statecode', $stateCode); 
      } 
     )); 
    } 
} 

Danach benötigen Sie jQuery events zu schreiben, die Optionen Kind aktualisieren sollte explizit auf Änderung der Mutter Option, Sie sollten keine Fehler Gesicht auf Vorlage von die Form.

Hinweis: Der obige Code wird extrahiert und für die Veröffentlichung hier geändert. Achten Sie auf namespace und Variablen wo immer erforderlich.

+0

Danke, aber nur eine Frage, wenn Sie Felder in EventListener generieren, wie fügen Sie einen Listener auf diesem? Da, wenn ich Felder in PRE_SET_DATA hinzufügen, kann ich nicht tun "$ Builder-> get ('Projekt') -> addEventListener (" weil das Feld noch nicht existiert.:/ – mpiot

+0

Und wenn ich es versuche benutze 'builder-> addEventListener (FormEvents :: POST_SUBMIT, function (FormEvent $ event) {', Ich habe keine Daten wenn ich '$ event-> getData();' – mpiot

+0

Ich habe meine Antwort aktualisiert, gehe durch das doc Link in der Antwort. Wenn Sie noch Hilfe benötigen, kommentieren. Ich kann später ein Code-Snippet hinzufügen. – Jeet

Verwandte Themen