Skip to content
Snippets Groups Projects
Commit 6b39a0a0 authored by user's avatar user
Browse files

User V2

parent c1f7051b
No related branches found
No related tags found
No related merge requests found
Showing
with 310 additions and 22 deletions
......@@ -29,4 +29,6 @@ MESSENGER_TRANSPORT_DSN=sync://
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
DATABASE_URL="mysql://root:root@172.18.0.3:3306/wishlist?serverVersion=8.0"
###< doctrine/doctrine-bundle ###
APP_SECRET=3d9f431b0fd768e0cb2e8b139c66e9fd67ecbd49762f6a345d459e2f115f61b4
......@@ -24,9 +24,9 @@ security:
remember_me:
secret: '%kernel.secret%'
access_control:
- { path: ^/admin, allow_if: "user and user.isAdmin == true" }
- { path: ^/locked, allow_if: "user and user.isLocked == true" }
# access_control:
# - { path: ^/admin, allow_if: "user and user.isAdmin == true" }
# - { path: ^/locked, allow_if: "user and user.isLocked == true" }
when@test:
security:
......
......@@ -5,6 +5,10 @@ namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\Item;
use App\Entity\Wishlist;
final class AdminController extends AbstractController
{
......@@ -19,9 +23,17 @@ final class AdminController extends AbstractController
#[Route('/admin/dashboard', name: 'admin_dashboard')]
public function dashboard(EntityManagerInterface $entityManager): Response
{
$topItems = $entityManager->getRepository(WishlistItem::class)->findTopExpensiveItems();
$topItems = $entityManager->getRepository(Item::class)->findTopExpensiveItems();
$topWishlists = $entityManager->getRepository(Wishlist::class)->findTopWishlistsByValue();
if (!$topItems) {
$topItems = [];
}
if (!$topWishlists) {
$topWishlists = [];
}
return $this->render('admin/dashboard.html.twig', [
'topItems' => $topItems,
'topWishlists' => $topWishlists,
......
<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\RegistrationFormType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
class RegistrationController extends AbstractController
{
#[Route('/register', name: 'register')]
public function register(Request $request, UserPasswordHasherInterface $passwordHasher, EntityManagerInterface $entityManager): Response
{
$user = new User();
$form = $this->createForm(RegistrationFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Hacher le mot de passe
$hashedPassword = $passwordHasher->hashPassword($user, $user->getPassword());
$user->setPassword($hashedPassword);
// Sauvegarder l'utilisateur
$entityManager->persist($user);
$entityManager->flush();
// Rediriger vers la page de connexion
return $this->redirectToRoute('login');
}
return $this->render('registration/register.html.twig', [
'registrationForm' => $form->createView(),
]);
}
}
\ No newline at end of file
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Doctrine\ORM\EntityManagerInterface;
class SecurityController extends AbstractController
{
#[Route('/login', name: 'login')]
public function login(AuthenticationUtils $authenticationUtils): Response
{
// Récupère les erreurs de connexion, s'il y en a
$error = $authenticationUtils->getLastAuthenticationError();
// Récupère le dernier email saisi par l'utilisateur
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
]);
}
#[Route('/logout', name: 'logout')]
public function logout(): void
{
// Ce contrôleur peut rester vide, Symfony gère la déconnexion automatiquement
}
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Doctrine\ORM\EntityManagerInterface;
final class UserController extends AbstractController
{
......
......@@ -5,6 +5,8 @@ namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface
......@@ -53,6 +55,20 @@ class User implements UserInterface
#[ORM\Column(length: 255, nullable: true)]
private ?string $image = null;
#[ORM\OneToMany(mappedBy: 'owner', targetEntity: Wishlist::class)]
private Collection $wishlists;
#[ORM\OneToMany(mappedBy: 'invitedUser', targetEntity: Item::class)]
private Collection $invitations;
public function __construct()
{
$this->wishlists = new ArrayCollection();
$this->invitations = new ArrayCollection();
$this->roles = ['ROLE_USER'];
$this->isLocked = false;
}
public function getId(): ?int
{
return $this->id;
......@@ -142,11 +158,23 @@ class User implements UserInterface
return $this;
}
public function __construct()
public function getWishlists(): Collection
{
$this->wishlists = new ArrayCollection();
$this->purchasedItems = new ArrayCollection();
$this->roles = ['ROLE_USER'];
$this->isLocked = false;
return $this->wishlists;
}
// public function getInvitations(): Collection
// {
// return $this->invitations;
// }
// public function addInvitation(Item $invitation): static
// {
// if (!$this->invitations->contains($invitation)) {
// $this->invitations[] = $invitation;
// $invitation->setInvitedUser($this);
// }
// }
}
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
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\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\File;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class RegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('firstName', TextType::class, [
'label' => 'Prénom',
'constraints' => [
new NotBlank(),
],
])
->add('lastName', TextType::class, [
'label' => 'Nom',
'constraints' => [
new NotBlank(),
],
])
->add('email', EmailType::class, [
'label' => 'Email',
'constraints' => [
new NotBlank(),
],
])
->add('password', PasswordType::class, [
'label' => 'Mot de passe',
'constraints' => [
new NotBlank(),
new Length(['min' => 6]),
],
])
->add('image', FileType::class, [
'label' => 'Image de profil (optionnel)',
'required' => false,
'mapped' => false,
'constraints' => [
new File([
'maxSize' => '2M',
'mimeTypes' => [
'image/jpeg',
'image/png',
],
'mimeTypesMessage' => 'Veuillez télécharger une image valide (JPEG ou PNG).',
]),
],
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
\ No newline at end of file
......@@ -16,6 +16,15 @@ class ItemRepository extends ServiceEntityRepository
parent::__construct($registry, Item::class);
}
public function findTopExpensiveItems(): array
{
return $this->createQueryBuilder('i') // Alias "i" pour Item
->orderBy('i.price', 'DESC') // Trier par prix décroissant
->setMaxResults(3) // Limiter à 3 résultats
->getQuery()
->getResult(); // Exécuter la requête
}
// /**
// * @return Item[] Returns an array of Item objects
// */
......
......@@ -32,6 +32,16 @@ public function findLockedUsers(): array
->getQuery()
->getResult();
}
public function findByEmail(string $email): ?User
{
return $this->createQueryBuilder('u')
->where('u.email = :email')
->setParameter('email', $email)
->getQuery()
->getOneOrNullResult();
}
// /**
// * @return User[] Returns an array of User objects
// */
......
......@@ -60,7 +60,7 @@ class WishlistRepository extends ServiceEntityRepository
public function findTopWishlistsByValue(): array
{
return $this->createQueryBuilder('u')
->join('u.wishlists', 'w')
->join('u.wishlist', 'w')
->join('w.items', 'i')
->where('i.isPurchased = true')
->groupBy('w.id')
......
......@@ -6,10 +6,16 @@
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
{% block stylesheets %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">
<link href="{{ asset('css/style.css') }}" rel="stylesheet">
{% endblock %}
{% block javascripts %}
{% block importmap %}{{ importmap('app') }}{% endblock %}
{% block importmap %}{{ importmap('app') }}
{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script>
{% endblock %}
</head>
<body>
......@@ -22,17 +28,7 @@
{% endfor %}
{% endfor %}
{% block stylesheets %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">
<link href="{{ asset('css/style.css') }}" rel="stylesheet">
{% endblock %}
{% block javascripts %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script>
{% endblock %}
{% block body %}{% endblock %}
</body>
</html>
{% extends 'base.html.twig' %}
{% block title %}Hello RegistrationController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code>/home/user/www/Exercices/Wishlist-application/src/Controller/RegistrationController.php</code></li>
<li>Your template at <code>/home/user/www/Exercices/Wishlist-application/templates/registration/index.html.twig</code></li>
</ul>
</div>
{% endblock %}
{% extends 'base.html.twig' %}
{% block title %}Inscription{% endblock %}
{% block body %}
<div class="container mt-5">
<h1>Inscription</h1>
{{ form_start(registrationForm) }}
{{ form_row(registrationForm.firstName) }}
{{ form_row(registrationForm.lastName) }}
{{ form_row(registrationForm.email) }}
{{ form_row(registrationForm.password) }}
{{ form_row(registrationForm.image) }}
<button type="submit" class="btn btn-primary">S'inscrire</button>
{{ form_end(registrationForm) }}
</div>
{% endblock %}
\ No newline at end of file
{% extends 'base.html.twig' %}
{% block title %}Hello SecurityController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code>/home/user/www/Exercices/Wishlist-application/src/Controller/SecurityController.php</code></li>
<li>Your template at <code>/home/user/www/Exercices/Wishlist-application/templates/security/index.html.twig</code></li>
</ul>
</div>
{% endblock %}
{% extends 'base.html.twig' %}
{% block title %}Connexion{% endblock %}
{% block body %}
<div class="container mt-5">
<h1>Connexion</h1>
{% if error %}
<div class="alert alert-danger">
{{ error.messageKey|trans(error.messageData, 'security') }}
</div>
{% endif %}
<form method="post" action="{{ path('login') }}">
<div class="mb-3">
<label for="username" class="form-label">Email</label>
<input type="text" name="_username" id="username" class="form-control" value="{{ last_username }}" required autofocus>
</div>
<div class="mb-3">
<label for="password" class="form-label">Mot de passe</label>
<input type="password" name="_password" id="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Se connecter</button>
</form>
</div>
{% endblock %}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment