2016-05-24 5 views
2

Ich möchte eine Formularklasse zu Symfony2.8 (und später zu Symfony3) aktualisieren. Jetzt wird das Formular konvertiert mit Ausnahme eines Attributs, choice_list, das nicht mehr unterstützt wird. Und ich weiß nicht, wie ich das machen soll.Update Form mit 'Choice_list' zu Symfony> = 2.8

Ich habe das folgende Formular Typ, der auch als Service definiert ist:

class ExampleType extends AbstractType 
{ 

    /** @var Delegate */ 
    private $delegate; 

    public function __construct(Delegate $delegate) 
    { 
     $this->delegate = $delegate; 
    } 

    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder->add('list', ChoiceType::class, array(
      'choice_list' => new ExampleChoiceList($this->delegate), 
      'required'=>false) 
     ); 
    } 

    public function configureOptions(OptionsResolver $resolver) 
    { 
      $resolver->setDefaults(array(
       'data_class' => 'ExampleClass', 
      )); 
    } 
} 

Und ich habe die folgende Klasse für die Auswahlliste:

class ExampleChoiceList extends LazyChoiceList 
{ 

    /** @var Delegate */ 
    private $delegate; 

    public function __construct(Delegate $delegate) 
    { 
     $this->delegate = $delegate; 
    } 


    /** 
    * Loads the choice list 
    * Should be implemented by child classes. 
    * 
    * @return ChoiceListInterface The loaded choice list 
    */ 
    protected function loadChoiceList() 
    { 
     $persons = $this->delegate->getAllPersonsFromDatabase(); 
     $personsList = array(); 
     foreach ($persons as $person) { 
      $id = $person->getId(); 
      $personsList[$id] = (string) $person->getLastname().', '.$person->getFirstname(); 
     } 
     return new ArrayChoiceList($personsList); 
    } 


} 

Die Klasse ExampleChoiceList die Auswahlliste generieren wie ich es haben will, und bis jetzt hat es funktioniert. Aber das Attribut choice_list wird nicht mehr unterstützt, und meine Frage lautet: "Wie bekomme ich diese Konvertierung ohne zu viel Arbeit?". Ich lese, dass ich einfach choice verwenden sollte, aber wie bekomme ich das, was ich will (das spezifische Label aus der Datenbank) in Symfony 2.8. Ich hoffe, dass mir jemand helfen kann.

Antwort

0

Durch eine ChoiceListInterface verwenden Sie sind fast da.

Ich schlage vor, Sie ExampleChoiceList ändern Sie die Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface stattdessen zu implementieren, ist es Ihnen 3 Methoden implementieren erfordert:

<?php 
// src/AppBundle/Form/ChoiceList/Loader/ExampleChoiceLoader.php 

namespace AppBundle\Form\ChoiceList\Loader; 

use Acme\SomeBundle\Delegate; 
use Symfony\Component\Form\ArrayChoiceList; 
use Symfony\Component\Form\Loader\ChoiceLoaderInterface; 

class ExampleChoiceLoader implements ChoiceLoaderInterface 
{ 
    /** $var ArrayChoiceList */ 
    private $choiceList; 

    /** @var Delegate */ 
    private $delegate; 

    public function __construct(Delegate $delegate) 
    { 
     $this->delegate = $delegate; 
    } 

    /** 
    * Loads the choice list 
    * 
    * $value is a callable set by "choice_name" option 
    * 
    * @return ArrayChoiceList The loaded choice list 
    */ 
    public function loadChoiceList($value = null) 
    { 
     if (null !== $this->choiceList) { 
      return $this->choiceList; 
     } 

     $persons = $this->delegate->getAllPersonsFromDatabase(); 
     $personsList = array(); 
     foreach ($persons as $person) { 
      $label = (string) $person->getLastname().', '.$person->getFirstname(); 
      $personsList[$label] = (string) $person->getId(); 
      // So $label will be displayed and the id will be used as data 
      // "value" will be ids as strings and used for post 
      // this is just a suggestion though 
     } 

     return $this->choiceList = new ArrayChoiceList($personsList); 
    } 

    /** 
    * {@inheritdoc} 
    * 
    * $choices are entities or the underlying data you use in the field 
    */ 
    public function loadValuesForChoices(array $choices, $value = null) 
    { 
     // optimize when no data is preset 
     if (empty($choices)) { 
      return array(); 
     } 

     $values = array(); 
     foreach ($choices as $person) { 
      $values[] = (string) $person->getId(); 
     } 

     return $values; 
    } 

    /** 
    * {@inheritdoc} 
    * 
    * $values are the submitted string ids 
    * 
    */ 
    public function loadChoicesForValues(array $values, $value) 
    { 
     // optimize when nothing is submitted 
     if (empty($values)) { 
      return array(); 
     } 

     // get the entities from ids and return whatever data you need. 
     // e.g return $this->delegate->getPersonsByIds($values); 
    } 
} 

Register sowohl den Lader und die Art, wie Dienstleistungen, so dass sie injiziert werden:

# app/config/services.yml 

services: 
    # ... 
    app.delegate: 
     class: Acme\SomeBundle\Delegate 

    app.form.choice_loader.example: 
     class: AppBundle\Form\ChoiceList\Loader\ExampleChoiceLoader 
     arguments: ["@app.delegate"] 

    app.form.type.example: 
     class: AppBundle\Form\Type\ExampleType 
     arguments: ["@app.form.choice_loader.example"] 

Ändern Sie dann den Formulartyp, um den Loader zu verwenden:

<?php 
// src/AppBundle/Form/Type/ExampleType.php 

namespace AppBundle\Form\Type; 

use AppBundle\Form\ChoiceList\Loader\ExampleChoiceLoader; 
use Symfony\Component\Form\AbstractType; 
use Symfony\Component\Form\FormBuilderInterface; 

class ExampleType extends AbstractType 
{ 
    /** @var ExampleChoiceLoader */ 
    private $loader; 

    public function __construct(ExampleChoiceLoader $loader) 
    { 
     $this->loader = $loader; 
    } 

    public function buildForm(FormBuilderInterface $builder, array $options = array()) 
    { 
     $builder->add('list', ChoiceType::class, array(
      'choice_loader' => $this->loader, 
      'required' => false, 
     )); 
    } 

    // ... 

} 
+0

Vielen Dank für Ihre Hilfe. Ich generiere nicht für jeden Typ einen Service, ich habe '' choice_loader '=> new ExampleChoiceLoader ($ this-> delegate), 'now. Und ich schreibe eine Klasse für alle meine Fälle, die erweitert werden, so dass ich nur die Methode 'loadChoiceList' implementieren muss. Ich habe den Fall, dass ich 10 Auswahlliste von verschiedenen Typen habe (und ich möchte Dienste für alle verwenden), als ich auch für jeden der Lader implementieren und sie alle im Konstruktor setzen muss? Denn in diesem Fall erscheint es nur für die Wahllisten ein wenig überdimensioniert. – Mirar

+0

Wenn Sie einen Formulartyp als Dienst deklarieren, können Sie den FQCN wie gewohnt verwenden. Die Injektion wird von [DependencyInjectionExtension'] (https://github.com/symfony/symfony/blob/master/src/) verarbeitet. Symfony/Komponente/Formular/Erweiterung/DependencyInjection/DependencyInjectionExtension.php). Sie müssen also keine konstruierte Instanz des Formulartyps übergeben. – Heah

+0

So würde es nur eine Lader-Instanz in allen Ihren Typen verwenden. Vielleicht könnten Sie einen abstrakten Typ erstellen, den alle Typen, die den Loader benötigen, erweitern können. – Heah

0

Ja, 'choice_list' ist mit SYmfony 2.8 veraltet, aber Sie können stattdessen 'Auswahl' verwenden, die auch ein Array akzeptiert. Von documentation:

The choices option is an array, where the array key is the item's label and the array value is the item's value.

Sie müssen nur darauf achten, als in Symfony 3.0 werden Schlüssel und Werte inversed und in Symfony 2.8, ist der empfohlene Weg, um die neue inversed zu verwenden, und ‚choices_as_values‘ angeben => wahr.

also in Form Typ:

$builder->add('list', ChoiceType::class, array(
       'choices' => new ExampleChoiceList($this->delegate), 
       'choices_as_values' => true, 
       'required'=>false)); 

Und in ExampleChoiceList:

protected function loadChoiceList() 
    { 
     $persons = $this->delegate->getAllPersonsFromDatabase(); 
     $personsList = array(); 
     foreach ($persons as $person) { 
      $id = $person->getId(); 
      $personsList[(string) $person->getLastname().', '.$person->getFirstname()] = $id; // <== here 
     } 
     return new ArrayChoiceList($personsList); 
    } 

UPDATE:

Ok, so empfehle ich Ihnen keinen Choice verwenden bei alles, aber ein EntityType, wie Sie scheinen alle "Personen" aus der Datenbank zu holen. Und um "Nachname, Vorname" als Bezeichnung anzuzeigen, verwenden Sie die Option "Choice_label". Angenommen, Ihr Unternehmen ist ‚Person‘ genannt:

$builder->add('list', EntityType::class, array(
    'class' => 'AppBundle:Person', 
    'choice_label' => function ($person) { 
     return $person->getLastName() . ', ' . $person->getFirstName(); 
    } 
)); 
+0

Sorry, aber deine Lösung funktioniert nicht. Jetzt bekomme ich den folgenden Fehler: 'Die Option" Auswahlmöglichkeiten "mit Wert ExampleChoiceList wird voraussichtlich vom Typ" null "oder" Array "oder" \ Traversable "sein, ist aber vom Typ" ExampleChoiceList ". 500 Interner Server Fehler - InvalidOptionsException' – Mirar

+0

Sorry, nicht das Detail von ExampleChoiceList. Kannst du den relevanten Code von LazyChoiceList setzen, um zu sehen, was erweitert wird? – scandel

+0

Es ist von Symfony: 'Namespace Symfony \ Component \ Form \ ChoiceList'. Ich habe es verwendet, um die Daten für die 'Choice_list' zu bekommen, aber es scheint, dass es jetzt Probleme macht. Die LazyChoiceList implementiert 'ChoiceListInterface' – Mirar