Cet article a été écrit il y a plus de quatre ans, son contenu n'est peut-être plus d'actualité.
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 (5)
Comments
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.