From 7091bd40941a9fc07cd8b55c40283fc335f89642 Mon Sep 17 00:00:00 2001 From: Olivier PARPAILLON Date: Wed, 20 Nov 2024 10:53:29 +0100 Subject: [PATCH] fileUploader + edit profile --- config/services.yaml | 4 ++ ...19145530.php => Version20241120092127.php} | 6 +- src/Controller/ProfileController.php | 55 ++++++++++++---- src/DataFixtures/UserFixtures.php | 52 ++++++++++++++++ src/Entity/Participant.php | 13 ++++ src/Form/RegistrationFormType.php | 62 +++++++++++++++---- src/Repository/ParticipantRepository.php | 19 +++++- src/Service/FileUploader.php | 45 ++++++++++++++ templates/profile/edit.html.twig | 24 +++++++ templates/profile/view.html.twig | 9 ++- 10 files changed, 259 insertions(+), 30 deletions(-) rename migrations/{Version20241119145530.php => Version20241120092127.php} (71%) create mode 100644 src/DataFixtures/UserFixtures.php create mode 100644 src/Service/FileUploader.php create mode 100644 templates/profile/edit.html.twig diff --git a/config/services.yaml b/config/services.yaml index 2d6a76f..fbf1bdd 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -20,5 +20,9 @@ services: - '../src/Entity/' - '../src/Kernel.php' + App\Service\FileUploader: + arguments: + $targetDirectory: '../public/upload/image/profile/' + # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/migrations/Version20241119145530.php b/migrations/Version20241120092127.php similarity index 71% rename from migrations/Version20241119145530.php rename to migrations/Version20241120092127.php index fc4121b..5d982b8 100644 --- a/migrations/Version20241119145530.php +++ b/migrations/Version20241120092127.php @@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ -final class Version20241119145530 extends AbstractMigration +final class Version20241120092127 extends AbstractMigration { public function getDescription(): string { @@ -20,12 +20,12 @@ final class Version20241119145530 extends AbstractMigration public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_PSEUDO ON participant (pseudo)'); + $this->addSql('ALTER TABLE participant ADD file_name VARCHAR(255) DEFAULT NULL'); } public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP INDEX UNIQ_IDENTIFIER_PSEUDO ON participant'); + $this->addSql('ALTER TABLE participant DROP file_name'); } } diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index 5a2e7de..78e1d38 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -2,6 +2,8 @@ namespace App\Controller; +use App\Entity\Participant; +use App\Service\FileUploader; use App\Form\RegistrationFormType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; @@ -11,26 +13,57 @@ use App\Repository\ParticipantRepository; class ProfileController extends AbstractController { + private FileUploader $fileUploader; + private ParticipantRepository $profileRepo; + public function __construct(FileUploader $fileUploader, ParticipantRepository $profileRepo) { + $this->fileUploader = $fileUploader; + $this->profileRepo = $profileRepo; + } #[Route('/profile/{uuid}', name: 'profile_view', methods: ['GET'])] - public function viewProfile(string $uuid, ParticipantRepository $participantRepository): Response + public function viewProfile(string $uuid, ParticipantRepository $profileRepo): Response { - $currentProfile = $participantRepository->findOneBy(['idParticipant' => $uuid]); + $currentProfile = $profileRepo->findOneBy(['idParticipant' => $uuid]); return $this->render('profile/view.html.twig', [ 'profile' => $currentProfile, ]); } #[Route('/profile/edit/{uuid}', name: 'profile_edit', methods: ['GET', 'POST'])] - public function editProfile(string $uuid, ParticipantRepository $participantRepository, Request $request): Response + public function editProfile(string $uuid, Request $request): Response { - $currentProfile = $participantRepository->findOneBy(['idParticipant' => $uuid]); - $form = $this->createForm(RegistrationFormType::class, $currentProfile); - $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - + try { + $profile = $this->profileRepo->findOneBy(['idParticipant' => $uuid]); + if (!$profile === $this->getUser()) { + $this->addFlash('error', "Vous ne pouvez pas modifier un profil qui n'est pas le votre"); + return $this->redirectToRoute('profile_view',['uuid' => $profile->getIdParticipant()]); + } + $form = $this->createForm(RegistrationFormType::class, $profile); + $form->handleRequest($request); + if ($form->isSubmitted() && $form->isValid()) { + $imageFile = $form->get('image')->getData(); + if (($form->has('deleteImage') && $form['deleteImage']->getData()) || $imageFile) { + $this->fileUploader->delete($profile->getFileName(), '/upload/image/profile'); + if ($imageFile) { + $imageFilename = $this->fileUploader->upload($imageFile); + $profile->setFileName($imageFilename); + } else { + $profile->setFileName(''); + } + } + $profileToUpdate = $this->profileRepo->update($profile); + if (!$profileToUpdate) { + throw $this->createNotFoundException('No profile found'); + } + $this->addFlash('success', 'Votre profile est bien à jour'); + return $this->redirectToRoute('profile_view',['uuid' => $profile->getIdParticipant()]); + } + return $this->render('profile/edit.html.twig', [ + 'formProfile' => $form, + ]); + } catch(\Exception $e) { + $formProfile = $this->createForm(RegistrationFormType::class, $profile); + $this->addFlash('error', $e->getMessage()); + return $this->render('profile/edit.html.twig', ['formProfile' => $formProfile]); } - return $this->render('profile/view.html.twig', [ - 'profile' => $currentProfile, - ]); } } diff --git a/src/DataFixtures/UserFixtures.php b/src/DataFixtures/UserFixtures.php new file mode 100644 index 0000000..7205a72 --- /dev/null +++ b/src/DataFixtures/UserFixtures.php @@ -0,0 +1,52 @@ +setPrenom('Olivier'); + $olivier->setNom('Parpaillon'); + $olivier->setPseudo('Parpaillax'); + $olivier->setTelephone('0675794302'); + $olivier->setEmail('olivier@gmail.com'); + $olivier->setRoles(['ROLE_USER', 'ROLE_ADMIN']); + $olivier->setAdministrateur(true); + $olivier->setActif(false); + $olivier->setPassword($userPasswordHasher->hashPassword($olivier, 'test-44')); + $manager->persist($olivier); + + $johan = new Participant(); + $johan->setPrenom('Johan'); + $johan->setNom('Leroy'); + $johan->setPseudo('Jojo'); + $johan->setTelephone('0785421565'); + $johan->setEmail('johan@gmail.com'); + $johan->setRoles(['ROLE_USER', 'ROLE_ADMIN']); + $johan->setAdministrateur(true); + $johan->setActif(false); + $johan->setPassword($userPasswordHasher->hashPassword($johan, 'test-44')); + $manager->persist($johan); + + $marvin = new Participant(); + $marvin->setPrenom('Marvin'); + $marvin->setNom('Epiphana'); + $marvin->setPseudo('Marv1'); + $marvin->setTelephone('0645258535'); + $marvin->setEmail('marvin@gmail.com'); + $marvin->setRoles(['ROLE_USER', 'ROLE_ADMIN']); + $marvin->setAdministrateur(true); + $marvin->setActif(false); + $marvin->setPassword($userPasswordHasher->hashPassword($marvin, 'test-44')); + $manager->persist($marvin); + + $manager->flush(); + } +} diff --git a/src/Entity/Participant.php b/src/Entity/Participant.php index f9d1f7d..b8273be 100644 --- a/src/Entity/Participant.php +++ b/src/Entity/Participant.php @@ -47,6 +47,9 @@ class Participant implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\Column] private ?string $password = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $fileName = null; + #[ORM\ManyToOne(targetEntity: Site::class, inversedBy: 'participants')] #[ORM\JoinColumn(name: 'site_id', referencedColumnName: 'id_site', nullable: true)] private ?Site $site = null; @@ -249,4 +252,14 @@ class Participant implements UserInterface, PasswordAuthenticatedUserInterface return $this; } + + public function getFileName(): ?string + { + return $this->fileName; + } + + public function setFileName(?string $fileName): void + { + $this->fileName = $fileName; + } } diff --git a/src/Form/RegistrationFormType.php b/src/Form/RegistrationFormType.php index 40bff5d..ad25c4b 100644 --- a/src/Form/RegistrationFormType.php +++ b/src/Form/RegistrationFormType.php @@ -4,31 +4,31 @@ namespace App\Form; use App\Entity\Participant; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\EmailType; +use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\Extension\Core\Type\PasswordType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\File; class RegistrationFormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder - ->add('email', EmailType::class, [ - 'label' => 'Email', + ->add('nom', TextType::class, [ + 'label' => 'Nom', 'label_attr' => ['class' => 'text-gray-700 font-bold'], 'attr' => [ 'class' => 'w-full mb-4 px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:border-blue-500', - 'placeholder' => 'Adresse e-mail', - ], - 'constraints' => [ - new NotBlank([ - 'message' => 'Please enter an email address', - ]), + 'placeholder' => 'Nom', ], ]) ->add('prenom', TextType::class, [ @@ -47,15 +47,20 @@ class RegistrationFormType extends AbstractType 'placeholder' => 'Pseudo', ], ]) - ->add('nom', TextType::class, [ - 'label' => 'Nom', + ->add('email', EmailType::class, [ + 'label' => 'Email', 'label_attr' => ['class' => 'text-gray-700 font-bold'], 'attr' => [ 'class' => 'w-full mb-4 px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:border-blue-500', - 'placeholder' => 'Nom', + 'placeholder' => 'Adresse e-mail', + ], + 'constraints' => [ + new NotBlank([ + 'message' => 'Please enter an email address', + ]), ], ]) - ->add('telephone', IntegerType::class, [ + ->add('telephone', TextType::class, [ 'label' => 'Numéro de téléphone', 'label_attr' => ['class' => 'text-gray-700 font-bold'], 'attr' => [ @@ -85,6 +90,39 @@ class RegistrationFormType extends AbstractType ]), ], ]) + ->add('image', FileType::class, [ + 'label' => 'Image', + 'mapped' => false, + 'required' => false, + 'attr' => [ + 'class' => 'w-full mb-4 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:border-blue-500', + ], + 'label_attr' => ['class' => 'text-gray-700 font-bold'], + 'constraints' => [ + new File([ + 'maxSize' => '1024k', + 'mimeTypes' => [ + 'image/png', + 'image/jpeg', + ], + 'mimeTypesMessage' => 'Please upload a valid image', + ]) + ], + ]) + ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $profile = $event->getData(); + if ($profile && $profile->getFileName()) { + $form = $event->getForm(); + $form->add('deleteImage', CheckboxType::class, [ + 'required' => false, + 'mapped' => false, + 'label' => 'Supprimer l\'image', + 'attr' => [ + 'class' => 'w-4 h-4 mb-4 border-gray-300 rounded mx-2', + ], 'label_attr' => ['class' => 'text-gray-700 font-bold px-4'] + ]); + } + }) ; } diff --git a/src/Repository/ParticipantRepository.php b/src/Repository/ParticipantRepository.php index daa2330..4c69fdf 100644 --- a/src/Repository/ParticipantRepository.php +++ b/src/Repository/ParticipantRepository.php @@ -5,15 +5,32 @@ namespace App\Repository; use App\Entity\Participant; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; +use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; /** * @extends ServiceEntityRepository */ class ParticipantRepository extends ServiceEntityRepository { - public function __construct(ManagerRegistry $registry) + private UserPasswordHasherInterface $userPasswordHasher; + public function __construct(ManagerRegistry $registry, UserPasswordHasherInterface $userPasswordHasher) { parent::__construct($registry, Participant::class); + $this->userPasswordHasher = $userPasswordHasher; + } + + public function update(Participant $profile): ?Participant + { + $newProfile = $this->findOneBy(['idParticipant' => $profile->getIdParticipant()]); + $newProfile->setPrenom($profile->getPrenom()); + $newProfile->setNom($profile->getNom()); + $newProfile->setEmail($profile->getEmail()); + $newProfile->setTelephone($profile->getTelephone()); + $newProfile->setPseudo($profile->getPseudo()); + $newProfile->setFileName($profile->getFileName()); + $newProfile->setPassword($this->userPasswordHasher->hashPassword($newProfile, $profile->getPassword())); + $this->getEntityManager()->flush(); + return $newProfile; } // /** diff --git a/src/Service/FileUploader.php b/src/Service/FileUploader.php new file mode 100644 index 0000000..99c3486 --- /dev/null +++ b/src/Service/FileUploader.php @@ -0,0 +1,45 @@ +getClientOriginalName(), PATHINFO_FILENAME); + $safeFilename = $this->slugger->slug($originalFilename); + $fileName = $safeFilename.'-'.uniqid().'.'.$file->guessExtension(); + + try { + $file->move($this->getTargetDirectory(), $fileName); + } catch (FileException $e) { + // ... handle exception if something happens during file upload + } + + return $fileName; + } + + public function getTargetDirectory(): string + { + return $this->targetDirectory; + } + + public function delete(?string $filename, string $rep): void + { + if (null != $filename) { + if (file_exists($rep . '/' . $filename)) { + unlink($rep . '/' . $filename); + } + } + } +} \ No newline at end of file diff --git a/templates/profile/edit.html.twig b/templates/profile/edit.html.twig new file mode 100644 index 0000000..251d262 --- /dev/null +++ b/templates/profile/edit.html.twig @@ -0,0 +1,24 @@ +{% extends 'main/base.html.twig' %} +{% block head %} + + + {% block stylesheets %} + {{ encore_entry_link_tags('app') }} + {% endblock %} + +{% endblock %} + +{% block content %} +
+
+

Modifier votre profile

+ {{ form_start(formProfile) }} + {{ form_widget(formProfile) }} + {% if formProfile.vars.data.imageFilename != null %} + + {% endif %} + + {{ form_end(formProfile) }} +
+
+{% endblock %} \ No newline at end of file diff --git a/templates/profile/view.html.twig b/templates/profile/view.html.twig index 44568fb..dec7138 100644 --- a/templates/profile/view.html.twig +++ b/templates/profile/view.html.twig @@ -13,13 +13,16 @@
-{#
#} -{# #} -{#
#} +
+ +

{{ profile.prenom }} {{ profile.nom }}

+
+ {{ profile.pseudo }} +
{{ profile.telephone }} - {{ profile.email }}