per far fronte a valori aggiunti dinamicamente utilizzano 'choice_loader'
opzione di tipo scelta. It's new in symfony 2.7 e purtroppo non ha alcun documento.
Fondamentalmente un servizio di esecuzione ChoiceLoaderInterface
che definisce tre funzioni:
loadValuesForChoices(array $choices, $value = null)
- è chiamato forma costruttiva e riceve i valori preimpostati oggetto legati in forma
loadChoiceList($value = null)
- è chiamato a vista di costruzione e dovrebbe restituire l'elenco completo delle scelte in generale
loadChoicesForValues(array $values, $value = null)
- viene chiamato sulla forma inviare e riceve i dati presentati
Ora la l'idea è di mantenere un ArrayChoiceList
come proprietà privata all'interno del loader di scelta. Sul modulo di costruzione loadValuesForChoices(...)
viene chiamato, qui aggiungiamo tutte le scelte preimpostate nella nostra lista scelta in modo che possano essere visualizzate all'utente. Nella vista build viene chiamato il numero loadChoiceList(...)
, ma non viene caricato nulla, viene semplicemente restituito l'elenco delle scelte personali creato in precedenza.
Ora l'utente interagisce con il modulo, alcune opzioni aggiuntive vengono caricate tramite un completamento automatico e inserite nel codice HTML. Al momento dell'invio del modulo vengono inviati i valori selezionati e dapprima nel modulo di controllo viene creato il modulo e successivamente viene chiamato $form->handleRequest(..)
loadChoicesForValues(...)
, ma i valori inviati potrebbero essere completamente diversi da quelli inclusi all'inizio.Quindi sostituiamo la nostra lista di scelta interna con una nuova contenente solo i valori inviati.
Il nostro modulo ora conserva perfettamente i dati aggiunti dal completamento automatico.
La parte difficile è che abbiamo bisogno di una nuova istanza del nostro caricatore scelto ogni volta che utilizziamo il tipo di modulo, altrimenti l'elenco di scelta interno terrebbe una combinazione di tutte le scelte.
Poiché l'obiettivo è scrivere un nuovo tipo di opzione di completamento automatico, in genere si utilizzerà l'iniezione di dipendenza per passare il caricatore scelto nel servizio di tipo. Ma per i tipi questo non è possibile se hai sempre bisogno di una nuova istanza, invece dobbiamo includerlo tramite le opzioni. L'impostazione del loader di scelta nelle opzioni predefinite non funziona, poiché sono anche memorizzati nella cache. Per risolvere questo problema è necessario scrivere una funzione anonima che ha bisogno di prendere le opzioni come parametri:
$resolver->setDefaults(array(
'choice_loader' => function (Options $options) {
return AutocompleteFactory::createChoiceLoader();
},
));
Edit: Qui è una versione ridotta della classe scelta loader:
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
class AutocompleteChoiceLoader implements ChoiceLoaderInterface
{
/** @var ChoiceListInterface */
private $choiceList;
public function loadValuesForChoices(array $choices, $value = null)
{
// is called on form creat with $choices containing the preset of the bound entity
$values = array();
foreach ($choices as $key => $choice) {
// we use a DataTransformer, thus only plain values arrive as choices which can be used directly as value
if (is_callable($value)) {
$values[$key] = (string)call_user_func($value, $choice, $key);
}
else {
$values[$key] = $choice;
}
}
// this has to be done by yourself: array(label => value)
$labeledValues = MyLabelService::getLabels($values);
// create internal choice list from loaded values
$this->choiceList = new ArrayChoiceList($labeledValues, $value);
return $values;
}
public function loadChoiceList($value = null)
{
// is called on form view create after loadValuesForChoices of form create
if ($this->choiceList instanceof ChoiceListInterface) {
return $this->choiceList;
}
// if no values preset yet return empty list
$this->choiceList = new ArrayChoiceList(array(), $value);
return $this->choiceList;
}
public function loadChoicesForValues(array $values, $value = null)
{
// is called on form submit after loadValuesForChoices of form create and loadChoiceList of form view create
$choices = array();
foreach ($values as $key => $val) {
// we use a DataTransformer, thus only plain values arrive as choices which can be used directly as value
if (is_callable($value)) {
$choices[$key] = (string)call_user_func($value, $val, $key);
}
else {
$choices[$key] = $val;
}
}
// this has to be done by yourself: array(label => value)
$labeledValues = MyLabelService::getLabels($values);
// reset internal choice list
$this->choiceList = new ArrayChoiceList($labeledValues, $value);
return $choices;
}
}
Oh, se solo sapessi quali sono le chiavi ei valori dell'array! –
@IanPhillips dipende da quale array intendi. Per i valori di ritorno delle funzioni dare un'occhiata al phpDoc di 'ChoiceLoaderInterface'. Le chiavi sono sempre come nell'array dei parametri e valutano scelte o valori. Nota che hai ancora bisogno di un [DataTransformer] (http://symfony.com/doc/current/cookbook/form/data_transformers.html) se lavori con entità! L'array utilizzato per creare l'interno 'ArrayChoiceList' dovrebbe contenere le successive etichette'
@IanPhillips Ho aggiunto una versione abbreviata del mio caricatore di scelta automatica – SBH