Les formulaires de Symfony ne se présentent pas comme un simple input HTML dont on récupèrerait les champs en POST.
La base des form Symfony, c'est de créer des "Types" qui vont monter le formulaire.
À l'intérieur seront présentes toutes les informations nécessaire au formulaire : fonctionne avec ou sans les entity, prend les class, le label, les vérifications, en somme énormément de paramètres.
Formulaire1 : petit formulaire Nom / prénom .
$form1 = $this->formFactory->create(Form1Type::class);
Le fameux "Form1Type" qui nous interesse particulièrement.
namespace Bundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Validator\Constraints as Assert;
class Form1Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('prenom', Type\TextType::class)
->add('nom', Type\TextType::class)
;
}
}
Donc un formType qu'est-ce que c'est ?
Une base identique à tous (abstract type etc etc).
Le builder et nos champs sont dans les "->add".
Les champs : nom, Type du champ (parmi les X disponibles, mais au besoin on peut créer le nôtre).
Et enfin des paramètres, beaucoup que l'on va pouvoir voir par la suite.
On va rajouter un peu de style et modifier quelques éléments de notre form.
Pour ça rien de plus simple, label, attributs, class, data, placeholder.
Chaque Type auquel le champ est attribué a sa propre liste d'options possibles + une base commune FormType.
Exemple TextType
Formulaire2 : formulaire 1 pimpé
$builder
->add('prenom', Type\TextType::class,
['label' => 'Prénom',
'attr' => ['class' => 'form-control',
'data-anglais' => 'FirstName',
'placeholder' => 'Votre Prenom ici'],
'required' => false,
])
->add('nom', Type\TextType::class,
['label' => 'Nom de famille *',
'attr' => ['class' => 'form-control',
'data-anglais' => 'Name'],
'required' => true,
'data' => 'Nom pré-rempli'
])
;
N'hésitez pas à fouiller pour bien repérer tout ce qu'on a rajouté, et en rajouter encore vous-même.
En HTML, on fait les input etc pour remplir une entity, on est obligé de récupérer chaque donnée et de set.
Mais pas avec Symfony ! Le formulaire peut être directement lié à l'entity.
J'ai créé une entity "produit" qui contient : name, price, magasin, type (désolé pour le franglais)
Formulaire3 : Mettons un petit formulaire pour créer un nouveau produit
class Form3Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', Type\TextType::class, ['attr' => ['class' => 'form-control']])
->add('price', Type\IntegerType::class, ['attr' => ['class' => 'form-control']])
->add('magasin', Type\TextType::class, ['attr' => ['class' => 'form-control']])
->add('type', Type\ChoiceType::class, [
'attr' => ['class' => 'form-control'],
'placeholder' => '',
'choices' => [
'viande' => 'viande',
'poisson' => 'poisson',
'fruits' => 'fruits',
'legume' => 'legume',
'pains' => 'pains',
'frais' => 'frais',
'surgelés' => 'surgelés',
'epicerie' => 'epicerie',
'boissons' => 'boissons',
'hygiène' => 'hygiène',
'animalerie' => 'animalerie',
'textile' => 'textile']])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Produit'
]);
}
}
J'ai aussi rajouté un élément intéressant pour forcer des types précis.
La précision est d'autant plus importante avec les formulaires dont on implique une entity, car les champs doivent correspondre à l'entity, et le formulaire est soumis à certaines règles.
On peut facilement repérer tout un tas d'erreurs.
Formulaire4 n'a pas le bon champs "name"
Ou Formulaire5 a un champs supplémentaire qui n'appartient pas à produit.
Ou encore un formulaire dont on aurait pas passé l'entity produit ressemblera à n'importe quel autre, c'est à la sauvegarde de celui-ci que les ennuis vont commencer.
A partir de là on commence vraiment à s'amuser.
Pour cette partie, je vais créer l'entity produit2 et magasin
Formulaire6 : Formulaire 3 ave ajout de l'entity magasin pour le champ magasin.
class Form6Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', Type\TextType::class, ['attr' => ['class' => 'form-control']])
->add('price', Type\IntegerType::class, ['attr' => ['class' => 'form-control']])
->add('magasin', EntityType::class, [
'attr' => ['class' => 'form-control'],
'class' => 'AppBundle\Entity\Magasin',
'placeholder' => ''])
->add('type', Type\ChoiceType::class, [
'attr' => ['class' => 'form-control'],
'placeholder' => '',
'choices' => [
'viande' => 'viande',
'poisson' => 'poisson',
'fruits' => 'fruits',
'legume' => 'legume',
'pains' => 'pains',
'frais' => 'frais',
'surgelés' => 'surgelés',
'epicerie' => 'epicerie',
'boissons' => 'boissons',
'hygiène' => 'hygiène',
'animalerie' => 'animalerie',
'textile' => 'textile']])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Produit2'
]);
}
}
À la création de ce formulaire je l'ai lié à l'entity Produit2, ce qu'on savait déjà faire.
Mais en plus dedans, le champs Magasin a été lié à l'entity Magasin.
C'est aussi modifiable, et malléable autant qu'on veut. Pensez toujours à regarder la documentation officielle.
Dans EntityType je fouille un peu et je trouve l'option 'query_builder' qui va me permettre de changer l'affichage du Formulaire7.
Encore un peu plus compliqué : maintenant on va essayer de mettre plusieurs Magasins pour un produit.
Pour ça j'ai encore créé un nouveau produit3 ainsi qu'un magasin2, qui seront en relation ManyToMany. Je vais ensuite utiliser les Collections pour le Formulaire8
Détail : cette partie est en cour de "stylisation", désolé pour le moment, j'ai du mal avec le plugin.
class Form8Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', Type\TextType::class, ['attr' => ['class' => 'form-control']])
->add('price', Type\IntegerType::class, ['attr' => ['class' => 'form-control']])
->add('magasins', Type\CollectionType::class, [
'entry_type' => MagType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'attr' => array(
'class' => 'my-selector',
),
])
->add('type', Type\ChoiceType::class, [
'attr' => ['class' => 'form-control'],
'placeholder' => '',
'choices' => [
'viande' => 'viande',
'poisson' => 'poisson',
'fruits' => 'fruits',
'legume' => 'legume',
'pains' => 'pains',
'frais' => 'frais',
'surgelés' => 'surgelés',
'epicerie' => 'epicerie',
'boissons' => 'boissons',
'hygiène' => 'hygiène',
'animalerie' => 'animalerie',
'textile' => 'textile']])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Produit3'
]);
}
}
class MagType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', EntityType::class, [
'attr' => ['class' => 'form-control'],
'class' => 'AppBundle\Entity\Magasin',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.name', 'ASC');
}])
/*->add('name', EntityType::class, [
'attr' => ['class' => 'form-control']
])*/
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Magasin2'
]);
}
}
La collection est un vaste sujet, mais pour essayer de faire simple, c'est un include d'un autre formulaire dans le formulaire.
Pour le gérer plus facilement et rajouter / modifier etc plus facilement j'ai fait appel à un vendor recommandé par symfony : Symfony Collection.
Il est très vaste et très intéressant pour la gestion JS des collections, l'ajout / suppression etc de l'include directement en page.
Nous avons vu là beaucoup de choses qui constituent les formulaires Symfony.
Le détail du traitement de données et le retour de ces formulaires sera vu une prochaine fois.
Bonne continuation.