Voici comment dans un formulaire custom D8, avoir un ensemble de champ "multiples" dans un formulaire de config par exemple.
Dans mon exemple je n'ai que le champ « titre », mais c'est simplement pour alléger le snippet.
Le code en question :
<?php
namespace Drupal\monmodule\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\file\Entity\File;
/**
* Class PopoteConfigForm.
*/
class HomepageConfigForm extends ConfigFormBase {
protected function getEditableConfigNames() {
return [
'monmodule.homepage',
];
}
public function getFormId() {
return 'homepage_config_form';
}
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('monmodule.homepage');
$pomoted_items = $config->get('promoted', []);
$number_promoted = $form_state->getValue('number_promoted', count($pomoted_items));
$removed = $form_state->getValue('removed_promoted', []);
$form['number_promoted'] = [
'#type' => 'value',
'#value' => $number_promoted,
];
$form['removed'] = [
'#type' => 'value',
'#value' => $removed,
];
$form['promoted'] = [
'#type' => 'fieldset',
'#title' => t("À la une"),
'#tree' => TRUE,
];
for ($i = 1; $i <= $number_promoted; $i++) {
$item = array_shift($pomoted_items);
if (in_array($i, $removed)) {continue;}
$form['promoted'][$i] = [
'#type' => 'fieldset',
'#title' => t("À la une #@i", ['@i' => $i]),
];
$form['promoted'][$i]['title'] = [
'#type' => 'textfield',
'#title' => $this->t("Titre"),
'#default_value' => $item['title'],
];
$form['promoted'][$i]['remove_' . $i] = [
'#type' => 'submit',
'#value' => t("Supprimer l'élément #@i", ['@i' => $i]),
'#submit' => ['::removeItem'],
'#attributes' => [
'class' => ['button--danger'],
'data-toRemove' => $i,
]
];
}
$form['promoted']['add_item'] = [
'#type' => 'submit',
'#value' => t('Ajouter un autre élément'),
'#submit' => ['::addPromotedItem'],
];
return parent::buildForm($form, $form_state);
}
public function addPromotedItem(array &$form, FormStateInterface $form_state) {
$form_state->setValue('number_promoted', $form_state->getValue('number_promoted') + 1);
$form_state->setRebuild();
}
public function removeItem(array &$form, FormStateInterface $form_state) {
$removed = $form_state->getValue('removed_promoted', []);
$removed[] = $form_state->getTriggeringElement()['#attributes']['data-toRemove'];
$form_state->setValue('removed_promoted', $removed);
$form_state->setRebuild();
}
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$parsed = [];
$promoted = $form_state->getValue('promoted');
foreach ($promoted as $promoted_item) {
if(is_array($promoted_item)) {
$parsed[] = $promoted_item;
}
}
$this->config('monmodule.homepage')->set('promoted', $parsed)->save();
}
}
Commentaires
Salut,
Merci pour l'article !
A noter, pour les managed_file (et sûrement d'autres), un simple ajout de '#multiple' => TRUE suffit, cf https://api.drupal.org/comment/63301#comment-63301
Hello,
Petite problématique pour la suppression.
En effet, dans ton form, tu nommes ton champ `removed` alors qu'il devrait s'appeler `removed_promoted`.
La conséquence est qu'on ne peut pas supprimer plus d'un élément car, dans ta méthode `removeItem`, tu récupères la valeur de `removed_promoted` qui est vide car elle n'existe pas dans ton formulaire.
Retour d'expếrience : j'ai eu un gros souci de serialization lors de l'ajout d'un élément dans mon form (Serialization of ' Closure' is not allowed). Le problème vient de la mise en cache du formulaire et s'est résolu avec un $form_state->disableCache(); dans le buildForm().
Voilà, si ça peut servir...
à noter ce module contrib qui permet de faire des groupes de champs multiples : https://www.drupal.org/project/multivalue_form_element
Pour aller dans le sens d'Olivier, il existe également element_multiple (https://www.drupal.org/project/element_multiple) qui est un peu plus récent et me semble un peu plus avancé que multivalue_form_element.
Ajouter un commentaire