fileUploader + edit profile
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
52
src/DataFixtures/UserFixtures.php
Normal file
52
src/DataFixtures/UserFixtures.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\DataFixtures;
|
||||
|
||||
use App\Entity\Participant;
|
||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
class UserFixtures extends Fixture
|
||||
{
|
||||
public function load(ObjectManager $manager, UserPasswordHasherInterface $userPasswordHasher): void
|
||||
{
|
||||
$olivier = new Participant();
|
||||
$olivier->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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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']
|
||||
]);
|
||||
}
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Participant>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
// /**
|
||||
|
||||
45
src/Service/FileUploader.php
Normal file
45
src/Service/FileUploader.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\String\Slugger\SluggerInterface;
|
||||
|
||||
class FileUploader
|
||||
{
|
||||
public function __construct(
|
||||
private string $targetDirectory,
|
||||
private SluggerInterface $slugger,
|
||||
) {
|
||||
}
|
||||
|
||||
public function upload(UploadedFile $file): string
|
||||
{
|
||||
$originalFilename = pathinfo($file->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
templates/profile/edit.html.twig
Normal file
24
templates/profile/edit.html.twig
Normal file
@@ -0,0 +1,24 @@
|
||||
{% extends 'main/base.html.twig' %}
|
||||
{% block head %}
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
{% block stylesheets %}
|
||||
{{ encore_entry_link_tags('app') }}
|
||||
{% endblock %}
|
||||
</head>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex flex-row justify-center items-stretch py-24">
|
||||
<div class="bg-white shadow-lg p-8 max-w-sm text-center flex-1">
|
||||
<h2 class="text-2xl font-bold text-center pb-6">Modifier votre profile</h2>
|
||||
{{ form_start(formProfile) }}
|
||||
{{ form_widget(formProfile) }}
|
||||
{% if formProfile.vars.data.imageFilename != null %}
|
||||
<img src="{{ asset('/upload/image/profile/' ~ formProfile.vars.value.imageFilename) }}" height="128" width="128">
|
||||
{% endif %}
|
||||
<button class="btnRegister text-white font-bold py-2 px-4 border-b-4 rounded mx-auto" type="submit">Actualiser les informations</button>
|
||||
{{ form_end(formProfile) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -13,13 +13,16 @@
|
||||
<div class="px-6 bg-white shadow-lg rounded-lg py-2">
|
||||
<div class="flex flex-wrap justify-center">
|
||||
<div class="w-full flex justify-center">
|
||||
{# <div class="relative">#}
|
||||
{# <img src="{{ profile.imageFilename ? asset('upload/image/profile/' ~ profile.imageFilename) : "" }}" class="shadow-xl rounded-full align-middle border-none absolute -m-16 -ml-20 lg:-ml-16 max-w-[150px]"/>#}
|
||||
{# </div>#}
|
||||
<div class="relative">
|
||||
<img src="{{ profile.fileName ? asset('upload/image/profile/' ~ profile.fileName) : "" }}" class="shadow-xl rounded-full align-middle border-none absolute -m-16 -ml-20 lg:-ml-16 max-w-[150px]"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-2">
|
||||
<h3 class="text-2xl text-slate-700 font-bold leading-normal mb-1">{{ profile.prenom }} {{ profile.nom }}</h3>
|
||||
<div class="text-sm mt-0 mb-2 text-slate-400 font-bold uppercase">
|
||||
<i class="fas fa-map-marker-alt mr-2 text-slate-400 opacity-75"></i>{{ profile.pseudo }}
|
||||
</div>
|
||||
<div class="text-xs mt-0 mb-2 text-slate-400 font-bold uppercase">
|
||||
<i class="fas fa-map-marker-alt mr-2 text-slate-400 opacity-75"></i>{{ profile.telephone }} - {{ profile.email }}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user