2016-07-26 2 views
0

FormEvents에 약간의 문제가 있습니다. 3 개의 필드가 동적으로 채워지 길 원합니다. 프로젝트 3 박스 : 프로젝트> 박스> 셀, 사용자가 프로젝트를 선택하고, 박스리스트가 업데이트되고, 박스를 선택하고, 셀리스트가 업데이트됩니다. 이 개 필드 ... 실제로 사용자에 대한양식 이벤트로 제출 된 양식의 동적 생성

을 위해, 나는 문서처럼 FormEvent를 사용

(http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data)

말을하지만 동적으로 업데이트 한 필드에,이, 일의, 문제했습니다하지만 프로젝트를 선택할 수 있으며 그가 할 때, 상자 필드가 업데이트됩니다. 하지만, 상자를 선택하면 셀 필드가 업데이트되지 않았습니다 ...

하지만 작동하도록 허용 한 무언가를 찾았습니다. -> add()와 inversed - > add(). 하지만 나는 그것을 원하지 않는다. 나는 그것을 변경할 때

$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 --', 
    )) 
; 

그리고 :

내 코드는

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 --', 
    )) 
; 

그것은 일입니다 ...하지만 시작에 상자 빈 목록을 원하고, 내가 프로젝트를 원하는 상자 앞에 ...

약간의 정밀도가이 양식은 CollectionType으로 다른 형식으로 포함됩니다. 이 유형의

모든 코드 :

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

감사합니다 당신의 도움에 많이 :)

답변

0

당신은 $builder->addEventListener를 사용해야합니다. 여러 필드의 경우 동적 필드를 FormEvents::PRE_SET_DATA 이벤트 핸들러 내에두면됩니다. 또한 자식 필드 선택 옵션을 가져 오는 데 설명 된대로 부모 필드 데이터를 사용합니다.

저는이 접근법을 계층 적 필드에서 국가, 주 및 도시 엔티티를 생성하는 데 사용했습니다. 도움이되는지 또는 더 많은 정보가 필요한지 알려주십시오.

편집 : 더 큰 로직의 경우 eventSubscriber을 사용하면 코드를 깨끗하게 유지할 수 있으며 다른 곳에서 양식의 동적 부분을 다시 사용할 수 있습니다.

종속적 인 여러 계층 필드의 경우 eventSubscriber 클래스의 조건을 통해 추가하면됩니다. 에 설명 된대로 내가 동적 HTML 필드를 대체하지 않습니다 코드와

업데이트 : 여기

참고

심포니 2.7에서 나를 위해 일한 코드에 산책하다 문서 대신 jQuery를 사용하여 선택한 부모 옵션에 따라 하위 옵션을 수집하고 그 옵션을 채 웁니다. 제출 된 양식은 eventSubscriber 컨텍스트에 따라 올바른 하위 옵션을 인식합니다.(모든 3 개 필드가 경우) 부모 양식 유형에

을하는 3 개 필드를 정의하는 eventSubscriber를 대신 호출 다음에 정의 된

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

eventSubscriber 만들기를 그래서 여기 당신이 그것을 할 수있는 방법입니다 문서, 여기에 파일 이름은 당신의 제출에 오류에 직면해서는 안 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); 
      } 
     )); 
    } 
} 

이 후, 당신은 명시 적으로 부모 옵션의 변화에 ​​자식 옵션을 업데이트해야 jQuery events를 작성할 필요가있다 형태.

참고 : 위의 코드는 여기에서 게시 할 수 있도록 추출되고 변경되었습니다. 필요하다면 namespace 및 변수를 처리하십시오.

+0

감사합니다. 다만, EventListener에서 필드를 생성 할 때,이 질문에 리스너를 어떻게 추가합니까? PRE_SET_DATA에 필드를 추가 할 때 "$ builder-> get ('project') -> addEventListener ("필드가 아직 존재하지 않기 때문입니다. :/ – mpiot

+0

그리고 시도하면 '$ event-> getData(); 할 때 데이터가 아님'builder-> addEventListener (FormEvents :: POST_SUBMIT, function (FormEvent $ event) {', – mpiot

+0

) 링크를 클릭하십시오. 여전히 도움이 필요하면 의견을 말하십시오. 나중에 코드 조각을 추가 할 수 있습니다. – Jeet