I want to send information from different input fields : 1st language, 2nd language, 3rd language... (this could be infinite). I built my form with Symfony in where I am asking to the user what languages he wants to learn.
I have several questions about it :
- Do I need to change string field to an array field ?
- In UserType.php, do I need to use CollectionType class ?
Also with JavaScript, I am able to clone the select tag that I am interested :
- Do I need to do it ?
Thank you in advance if you could give a hand.
registration.html.twig
{% extends 'base.html.twig' %}
{% block title %}Incris-toi !{% endblock %}
{% block main %}
{{ form_start(userform) }}
<div class="alert alert-danger text-center" role="alert">
{{ form_errors(userform.email) }}
{{ form_errors(userform.password) }}
{{ form_errors(userform.gender) }}
{{ form_errors(userform.firstname) }}
{{ form_errors(userform.lastname) }}
{{ form_errors(userform.birthdate) }}
{{ form_errors(userform.occupation) }}
{{ form_errors(userform.nationality) }}
{{ form_errors(userform.nativelanguage) }}
{{ form_errors(userform.wishedlanguages) }}
</div>
<div class="form container">
<div class="formpage">
<div class="form-floating mb-3">
{{ form_widget(userform.email, {'attr' : {'placeholder' : 'Mon adresse e-mail', 'class' : 'form-control'}}) }}
{{ form_label(userform.email, 'Mon adresse e-mail', {'label_attr' : {'class' : 'label'}}) }}
</div>
<div class="form-floating mb-3">
{{ form_widget(userform.password.first, {'attr' : {'placeholder' : 'Mon mot de passe', 'class' : 'form-control'}}) }}
{{ form_label(userform.password.first, 'Mon mot de passe', {'label_attr' : {'class' : 'label'}}) }}
</div>
<div class="form-floating">
{{ form_widget(userform.password.second, {'attr' : {'placeholder' : 'Confirmation de mon mot de passe', 'class' : 'form-control'}}) }}
{{ form_label(userform.password.second, 'Confirmation de mon mot de passe', {'label_attr' : {'class' : 'label'}}) }}
</div>
<div class="form-checkbox">
{{ form_widget(userform.roles) }}
<span class="checkbox"></span>
<span class="checkmark"></span>
</div>
<div>
<button type="button" class="next btn btn-lg btn-outline-primary mt-4 d-flex mx-auto">Je m'inscris</button>
</div>
</div>
<div class="formpage">
<div class="mb-3">
<p>Es-tu un homme ou une femme ?</p>
{{ form_widget(userform.gender, {'attr' : {'placeholder' : 'Es-tu un homme ou une femme ?', 'class' : 'form-control'}}) }}
</div>
<div class="form-floating mb-3">
{{ form_widget(userform.firstname, {'attr' : {'placeholder' : 'Quel est ton prénom ?', 'class' : 'form-control'}}) }}
{{ form_label(userform.firstname, 'Quel est ton prénom ?', {'label_attr' : {'class' : 'label'}}) }}
</div>
<div class="form-floating mb-3">
{{ form_widget(userform.lastname, {'attr' : {'placeholder' : 'Quel est ton nom de famille ?', 'class' : 'form-control'}}) }}
{{ form_label(userform.lastname, 'Quel est ton nom de famille ?', {'label_attr' : {'class' : 'label'}}) }}
</div>
<div class="form-floating mb-3">
<p>Quelle est ta date de naissance ?</p>
{{ form_widget(userform.birthdate) }}
</div>
<div class="d-flex">
<button type="button" class="prev btn btn-lg btn-outline-secondary mt-4 d-flex align-items-center me-auto"><<span class="previous">Précédent</span></button>
<button type="button" class="next btn btn-lg btn-outline-primary mt-4 d-flex ms-auto">Suivant</button>
</div>
</div>
<div class="formpage">
<div class="form-floating mb-3">
{{ form_widget(userform.occupation, {'attr' : {'placeholder' : 'Quelle est ton occupation (ton métier, tes études…) ?', 'class' : 'form-control'}}) }}
{{ form_label(userform.occupation, 'Quelle est ton occupation (ton métier, tes études…) ?', {'label_attr' : {'class' : 'label'}}) }}
</div>
<div class="mb-3">
<p>De quel pays es-tu originaire ?</p>
{{ form_widget(userform.nationality) }}
</div>
<div class="mb-3">
<p>Quelle est ta langue maternelle ?</p>
{{ form_widget(userform.nativelanguage) }}
</div>
<div class="d-flex">
<button type="button" class="prev btn btn-lg btn-outline-secondary mt-4 d-flex align-items-center me-auto"><<span class="previous">Précédent</span></button>
<button type="button" class="next optional btn btn-lg btn-outline-primary mt-4 d-flex ms-auto">Suivant</button>
<button class="btn optional-validation btn-lg btn-outline-primary mt-4 d-flex ms-auto">Je valide</button>
</div>
</div>
<div class="formpage">
<div class="mb-3">
<p>Quelle langue souhaites-tu pratiquer ?</p>
<div id="wishedlanguageslist">
{{ form_widget(userform.wishedlanguages, {'attr' : {'class' : 'wishedlanguage form-control mb-3', 'novalidate': 'novalidate'}}) }}
</div>
<div class="d-flex justify-content-between">
<button type="button" class="question-button btn btn-outline-secondary" id="addwishedlanguage">+ Ajouter une langue</button>
<button type="button" class="question-button btn btn-outline-secondary" id="removewishedlanguage">- Supprimer la dernière langue</button>
</div>
</div>
<div class="d-flex">
<button type="button" class="prev btn btn-lg btn-outline-secondary mt-4 d-flex align-items-center me-auto"><<span class="previous">Précédent</span></button>
{{ form_row(userform.save, {'attr' : {'class' : 'btn btn-lg btn-outline-primary mt-4 d-flex ms-auto'}}) }}
</div>
</div>
</div>
{{ form_end(userform) }}
{% endblock %}
{% block js %}
<script src="../assets/js/scripts.js"></script>
{% endblock %}
UserType.php
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\CountryType;
use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('gender', ChoiceType::class, [
'choices' => [
'Je suis ...' => '',
'un homme' => 'male',
'une femme' =>'female',
'non-binaire' => 'non-binary'
]
])
->add('lastname')
->add('firstname')
->add('birthdate', BirthdayType::class, [
'placeholder' => [
'year' => 'Année', 'month' => 'Mois', 'day' => 'Jour',
],
'choice_translation_domain' => true
])
->add('occupation')
->add('nationality', CountryType::class, [
'placeholder' => 'Je choisis un pays',
])
->add('nativelanguage', LanguageType::class, [
'placeholder' => 'Je choisis ta langue maternelle',
])
->add('wishedlanguages', CollectionType::class, [
/* 'placeholder' => 'Je choisis une langue étrangère', */
'entry_type' => LanguageType::class,
'entry_options' => [
'attr' => ['class' => 'wishedlanguage'],
],
'required' => false,
'compound' => true,
])
->add(
$builder
->get('wishedlanguages')
->setRequired(false)
)
->add('email')
->add('password', PasswordType::class, [
'mapped' => false
])
->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'Les deux mots de passe doivent être identiques.',
'options' => ['attr' => ['class' => 'password-field']],
'required' => true,
'first_options' => ['label' => 'Password'],
'second_options' => ['label' => 'Repeat Password'],
])
->add('roles', CheckboxType::class, [
'label' => 'Je m\'inscris uniquement en tant qu\'organisateur.',
'required' => false,
'compound' => true,
])
->add(
$builder
->get('roles')
->addModelTransformer(new CallbackTransformer(
function ($arrayAsBool) {
return in_array("ROLE_ADMIN", $arrayAsBool);
},
function ($boolAsArray) {
return $boolAsArray ? ["ROLE_ADMIN"] : ["ROLE_USER"];
}))
)
->add('save', SubmitType::class, [
'label' => 'Je valide',
])
;
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
'translation_domain' => 'forms'
]);
}
}
User.php
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
/**
* @var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
#[ORM\Column(length: 255)]
private ?string $gender = null;
#[ORM\Column(length: 255)]
private ?string $lastname = null;
#[ORM\Column(length: 255)]
private ?string $firstname = null;
#[ORM\Column(type: Types::DATE_MUTABLE)]
private ?\DateTimeInterface $birthdate = null;
#[ORM\Column(length: 255)]
private ?string $occupation = null;
#[ORM\Column(length: 255)]
private ?string $nationality = null;
#[ORM\Column(length: 255)]
private ?string $nativelanguage = null;
#[ORM\Column(type: Types::ARRAY, nullable: true)]
private array $wishedlanguages = [];
#[ORM\Column(length: 255, nullable: true)]
private ?string $photo = null;
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getGender(): ?string
{
return $this->gender;
}
public function setGender(string $gender): self
{
$this->gender = $gender;
return $this;
}
public function getLastname(): ?string
{
return $this->lastname;
}
public function setLastname(string $lastname): self
{
$this->lastname = $lastname;
return $this;
}
public function getFirstname(): ?string
{
return $this->firstname;
}
public function setFirstname(string $firstname): self
{
$this->firstname = $firstname;
return $this;
}
public function getBirthdate(): ?\DateTimeInterface
{
return $this->birthdate;
}
public function setBirthdate(\DateTimeInterface $birthdate): self
{
$this->birthdate = $birthdate;
return $this;
}
public function getOccupation(): ?string
{
return $this->occupation;
}
public function setOccupation(string $occupation): self
{
$this->occupation = $occupation;
return $this;
}
public function getNationality(): ?string
{
return $this->nationality;
}
public function setNationality(string $nationality): self
{
$this->nationality = $nationality;
return $this;
}
public function getNativelanguage(): ?string
{
return $this->nativelanguage;
}
public function setNativelanguage(string $nativelanguage): self
{
$this->nativelanguage = $nativelanguage;
return $this;
}
public function getWishedlanguages(): array
{
return $this->wishedlanguages;
}
public function setWishedlanguages(?array $wishedlanguages): self
{
$this->wishedlanguages = $wishedlanguages;
return $this;
}
public function getPhoto(): ?string
{
return $this->photo;
}
public function setPhoto(?string $photo): self
{
$this->photo = $photo;
return $this;
}
}