src/Form/CandidatureType.php line 28
<?phpnamespace App\Form;use App\Entity\Candidature;use App\Entity\Metier;use App\Entity\Etablissement;use App\Service\Constant;use Doctrine\ORM\EntityRepository;use Doctrine\ORM\EntityManagerInterface;use Symfony\Bridge\Doctrine\Form\Type\EntityType;use Symfony\Component\Form\AbstractType;use Symfony\Component\Form\Extension\Core\Type\ChoiceType;use Symfony\Component\Form\Extension\Core\Type\DateType;use Symfony\Component\Form\Extension\Core\Type\IntegerType;use Symfony\Component\Form\Extension\Core\Type\NumberType;use Symfony\Component\Form\Extension\Core\Type\TextType;use Symfony\Component\Form\Extension\Core\Type\FileType;use Symfony\Component\Form\Extension\Core\Type\HiddenType;use Symfony\Component\Form\FormBuilderInterface;use Symfony\Component\Form\FormInterface;use Symfony\Component\OptionsResolver\OptionsResolver;use Symfony\Component\Validator\Constraints\File;use Symfony\Component\Form\FormEvents;use Symfony\Component\Form\FormEvent;use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;class CandidatureType extends AbstractType{private $authorization;private $constant;private $entityManager;public function __construct(AuthorizationCheckerInterface $authorization,Constant $constant,EntityManagerInterface $entityManager) {$this->authorization = $authorization;$this->constant = $constant;$this->entityManager = $entityManager;}public function buildForm(FormBuilderInterface $builder, array $options): void{$evaluationOnly = $options['evaluation_only'];// Écouter l'événement PRE_SET_DATA pour configurer le formulaire avec les données existantes$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($evaluationOnly) {$form = $event->getForm();/** @var Candidature|null $data */$data = $event->getData();$etablissement = (!$evaluationOnly && $data) ? $data->getEtablissement() : null;$this->addFormFields($form, $etablissement, $data, $evaluationOnly);});// Écouter l'événement PRE_SUBMIT pour reconfigurer le formulaire avec les données soumises$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($evaluationOnly) {$form = $event->getForm();$data = $event->getData();$candidature = $form->getData();// Récupérer l'ID de l'établissement depuis les données soumises$etablissementId = (!$evaluationOnly && isset($data['etablissement'])) ? $data['etablissement'] : null;$etablissement = $etablissementId ? $this->entityManager->getRepository(Etablissement::class)->find($etablissementId) : null;$this->addFormFields($form, $etablissement, $candidature instanceof Candidature ? $candidature : null, $evaluationOnly);});}private function addFormFields(FormInterface $form, ?Etablissement $etablissement, ?Candidature $candidature = null, bool $evaluationOnly = false): void{if (!$evaluationOnly) {// Établissement (toujours disponible)$form->add('etablissement', EntityType::class, ['class' => Etablissement::class,'query_builder' => function (EntityRepository $er) {return $er->createQueryBuilder('e')->leftJoin('e.localite', 'l')->leftJoin('l.directionRegionale', 'd')->orderBy('e.nom', 'ASC');},'choice_label' => 'nom','choice_value' => 'id','attr' => ['data-metiers-target' => 'etablissement-select','class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition'],'label' => 'Établissement','placeholder' => '-- Choisissez un établissement --','required' => true]);// Configuration du métier (dynamique selon l'établissement)$metierOptions = ['class' => Metier::class,'choice_label' => 'nom','choice_value' => 'id','group_by' => 'secteur.nom','label' => 'Métier','required' => true,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']];if ($etablissement) {// Si un établissement est sélectionné, charger les métiers associés$metierOptions['query_builder'] = function (EntityRepository $er) use ($etablissement) {return $er->createQueryBuilder('m')->innerJoin('m.etablissementMetiers', 'em')->innerJoin('m.secteur', 's')->where('em.etablissement = :etablissement')->setParameter('etablissement', $etablissement)->orderBy('s.nom', 'ASC')->addOrderBy('m.nom', 'ASC');};$metierOptions['placeholder'] = '-- Sélectionnez un métier --';$metierOptions['attr']['disabled'] = false;} else {// Sans établissement, liste vide et champ désactivé$metierOptions['choices'] = [];$metierOptions['placeholder'] = '-- Choisissez d\'abord un établissement --';$metierOptions['attr']['disabled'] = true;}$form->add('metier', EntityType::class, $metierOptions);// Champ caché pour stocker la valeur du niveau$form->add('niveau', HiddenType::class, ['data' => $this->getNiveauRequis($etablissement)]);// Documents à téléchargerforeach ($this->constant->document_labels as $field => $config) {$form->add($field, FileType::class, ['label' => $config['text'],'mapped' => false,'required' => false,'attr' => ['data-field' => $field,'accept' => $config['accept'],'class' => 'hidden file-input'],'constraints' => [new File(['maxSize' => '2M','mimeTypes' => explode(',', str_replace(' ', '', $config['accept'])),'mimeTypesMessage' => 'Format ' . $config['formats'] . ' requis'])]]);}} // fin if (!$evaluationOnly)// Champs pour les évaluateurs (ENT, ADMIN)if ($this->authorization->isGranted('ROLE_ENT') || $this->authorization->isGranted('ROLE_ADMIN')) {$form->add('etustatut', ChoiceType::class, ['choices' => ['En attente' => null,'Accepté' => 2,'Rejeté' => 1],'label' => 'Étude de dossier','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']])->add('etucom', TextType::class, ['label' => 'Commentaire étude','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']])->add('entdate', DateType::class, ['widget' => 'single_text','label' => 'Date entretien','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']])->add('entlieu', TextType::class, ['label' => 'Lieu entretien','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']])->add('jury', IntegerType::class, ['attr' => ['min' => 1,'max' => 20,'class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition'],'label' => 'Numéro du jury','required' => false])->add('vague', IntegerType::class, ['attr' => ['min' => 1,'class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition'],'label' => 'Vague','required' => false]);// Notesfor ($i = 1; $i <= 9; $i++) {$noteOptions = ['attr' => ['min' => 0,'max' => 20,'step' => 0.5,'class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition'],'label' => 'Note ' . $i,'required' => false];$form->add('note' . $i, NumberType::class, $noteOptions + ['scale' => 1]);}$form->add('entcom', TextType::class, ['label' => 'Commentaire entretien','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']]);}// Champs Nom/Prénom du candidat (édition uniquement pour ENT/ADMIN, hors mode évaluation)if (!$evaluationOnly &&($this->authorization->isGranted('ROLE_ENT') || $this->authorization->isGranted('ROLE_ADMIN')) &&$candidature !== null && $candidature->getId() !== null) {$inputCls = 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition';$user = $candidature->getUser();$form->add('nom', TextType::class, ['mapped' => false,'required' => false,'label' => 'Nom','data' => $user ? ($user->getNom() ?? '') : '','attr' => ['class' => $inputCls]]);$form->add('prenoms', TextType::class, ['mapped' => false,'required' => false,'label' => 'Prénom(s)','data' => $user ? ($user->getPrenoms() ?? '') : '','attr' => ['class' => $inputCls]]);}// Champs pour le juryif ($this->authorization->isGranted('ROLE_JURY') ||$this->authorization->isGranted('ROLE_ADMIN') ||$this->authorization->isGranted('ROLE_ENT')) {$form->add('entstatut', ChoiceType::class, ['choices' => ['En attente' => null,'Admissible' => 2,'Non admissible' => 1],'label' => 'Décision du jury','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']])->add('visdate', DateType::class, ['widget' => 'single_text','label' => 'Date visite','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']])->add('vislieu', TextType::class, ['label' => 'Lieu visite','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']])->add('visstatut', ChoiceType::class, ['choices' => ['En attente' => null,'Effectué' => 2,'Non effectué' => 1],'label' => 'Statut visite','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']])->add('viscom', TextType::class, ['label' => 'Commentaire visite','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']])->add('resultat', ChoiceType::class, ['choices' => ['En attente' => null,'Admis' => 2,'Non admis' => 1],'label' => 'Résultat final','required' => false,'attr' => ['class' => 'w-full px-4 py-2 rounded-lg border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition']]);}}private function getNiveauRequis(?Etablissement $etablissement): string{if (!$etablissement) {return 'Sélectionnez d\'abord un établissement';}// Par défaut, on ne peut pas déterminer le niveau car il dépend du métier// Ce sera mis à jour dynamiquement via JavaScriptreturn 'Sélectionnez un métier';}public function configureOptions(OptionsResolver $resolver): void{$resolver->setDefaults(['data_class' => Candidature::class,'csrf_protection' => true,'csrf_field_name' => '_token','csrf_token_id' => 'candidature_item','evaluation_only' => false,]);}}