From a47f0ef46cde74e2805b3378e97e82b748c33fbf Mon Sep 17 00:00:00 2001
From: Julian PEREZ-RAMIREZ <julian.perez-ramirez@imt-atlantique.net>
Date: Wed, 5 Mar 2025 16:15:40 +0100
Subject: [PATCH] adding first UserController methods

---
 server/web_app/composer.json                  |   1 +
 server/web_app/config/packages/validator.yaml |  11 ++
 .../web_app/src/Controller/UserController.php | 120 +++++++++++++++++-
 server/web_app/src/Entity/User.php            |  11 ++
 .../src/Interface/UserControllerInterface.php |   8 +-
 .../web_app/src/Repository/UserRepository.php |  31 +++++
 server/web_app/symfony.lock                   |  12 ++
 7 files changed, 186 insertions(+), 8 deletions(-)
 create mode 100644 server/web_app/config/packages/validator.yaml

diff --git a/server/web_app/composer.json b/server/web_app/composer.json
index 8fc8bf2..2075e56 100644
--- a/server/web_app/composer.json
+++ b/server/web_app/composer.json
@@ -18,6 +18,7 @@
         "symfony/runtime": "7.2.*",
         "symfony/security-bundle": "7.2.*",
         "symfony/security-core": "7.2.*",
+        "symfony/validator": "7.2.*",
         "symfony/yaml": "7.2.*"
     },
     "config": {
diff --git a/server/web_app/config/packages/validator.yaml b/server/web_app/config/packages/validator.yaml
new file mode 100644
index 0000000..dd47a6a
--- /dev/null
+++ b/server/web_app/config/packages/validator.yaml
@@ -0,0 +1,11 @@
+framework:
+    validation:
+        # Enables validator auto-mapping support.
+        # For instance, basic validation constraints will be inferred from Doctrine's metadata.
+        #auto_mapping:
+        #    App\Entity\: []
+
+when@test:
+    framework:
+        validation:
+            not_compromised_password: false
diff --git a/server/web_app/src/Controller/UserController.php b/server/web_app/src/Controller/UserController.php
index b80a3e4..0ebd3f9 100644
--- a/server/web_app/src/Controller/UserController.php
+++ b/server/web_app/src/Controller/UserController.php
@@ -3,17 +3,129 @@
 namespace App\Controller;
 
 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\Routing\Attribute\Route;
 
-final class UserController extends AbstractController
+use App\Interface\UserControllerInterface;
+use App\Entity\User;
+use App\Enum\UserRole;
+use App\Repository\UserRepository;
+use Doctrine\ORM\EntityManagerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
+use Symfony\Component\Validator\Validator\ValidatorInterface;
+
+#[Route('/users')]
+final class UserController extends AbstractController implements UserControllerInterface
 {
-    #[Route('/user', name: 'app_user')]
-    public function index(): JsonResponse
+    private EntityManagerInterface $entityManager;
+    private UserRepository $userRepository;
+    private UserPasswordHasherInterface $passwordHasher;
+    private ValidatorInterface $validator;
+
+    public function __construct(
+        EntityManagerInterface $entityManager,
+        UserRepository $userRepository,
+        UserPasswordHasherInterface $passwordHasher,
+        ValidatorInterface $validator
+    ) {
+        $this->entityManager = $entityManager;
+        $this->userRepository = $userRepository;
+        $this->passwordHasher = $passwordHasher;
+        $this->validator = $validator;
+    }
+
+    #[Route('', methods: ['POST'])]
+    public function createUser(Request $req): JsonResponse
     {
+        $data = json_decode($req->getContent(), true);
+
+        $user = new User();
+        $user->setUserName($data['userName'] ?? null);
+        $user->setName($data['name'] ?? null);
+        $user->setSurname($data['surname'] ?? null);
+        $user->setEmail($data['email'] ?? null);
+        $user->setRole(UserRole::from($data['role'] ?? 'ROLE_USER')); 
+        $user->setCreatedAt(new \DateTimeImmutable());
+
+        if (isset($data['password'])) {
+            $user->setPassword($this->passwordHasher->hashPassword($user, $data['password']));
+        }
+
+        // Validate the User entity
+        $errors = $this->validator->validate($user);
+        if (count($errors) > 0) {
+            $errorMessages = [];
+            foreach ($errors as $error) {
+                $errorMessages[] = $error->getMessage();
+            }
+            return new JsonResponse(['errors' => $errorMessages], Response::HTTP_BAD_REQUEST);
+        }
+
+        $this->entityManager->persist($user);
+        $this->entityManager->flush();
+
         return $this->json([
-            'message' => 'Welcome to your new controller!',
+            'message' => 'User created successfully',
             'path' => 'src/Controller/UserController.php',
+        ], Response::HTTP_CREATED);
+    }
+
+    #[Route('', methods: ['GET'])]
+    public function getAllUsers(): JsonResponse
+    {
+        $users = $this->userRepository->findAllUsers();
+        $userArray = array_map(fn($user) => ['id' => $user->getId(), 'username' => $user->getUsername()], $users);
+
+        return $this->json([
+            'users' => $userArray,
+            'path' => 'src/Controller/ItemController.php',
+        ]);
+    }
+
+    #[Route('/{id}', methods: ['GET'])]
+    public function getUserById(int $userId): JsonResponse
+    {
+        return $this->json([
+            'message' => 'Welcome to your new controller!',
+            'path' => 'src/Controller/ItemController.php',
+        ]);
+    }
+
+    #[Route('/{id}', methods: ['UPDATE'])]
+    public function updateUser(int $userId, Request $req): JsonResponse
+    {
+        return $this->json([
+            'message' => 'Welcome to your new controller!',
+            'path' => 'src/Controller/ItemController.php',
+        ]);
+    }
+
+    #[Route('/{id}', methods: ['DELETE'])]
+    public function deleteUser(int $userId): JsonResponse
+    {
+        return $this->json([
+            'message' => 'Welcome to your new controller!',
+            'path' => 'src/Controller/ItemController.php',
+        ]);
+    }
+
+    #[Route('/authenticate', methods: ['POST'])]
+    public function authenticateUser(string $username, string $password): JsonResponse
+    {
+        return $this->json([
+            'message' => 'Welcome to your new controller!',
+            'path' => 'src/Controller/ItemController.php',
+        ]);
+    }
+
+    #[Route('/authenticate/changePassword', methods: ['POST'])]
+    public function changePassword(int $userId, string $oldPassword, string $newPassword): JsonResponse
+    {
+        return $this->json([
+            'message' => 'Welcome to your new controller!',
+            'path' => 'src/Controller/ItemController.php',
         ]);
     }
 }
diff --git a/server/web_app/src/Entity/User.php b/server/web_app/src/Entity/User.php
index 2e9ce2f..e7b7731 100644
--- a/server/web_app/src/Entity/User.php
+++ b/server/web_app/src/Entity/User.php
@@ -7,6 +7,7 @@ use App\Repository\UserRepository;
 use Doctrine\ORM\Mapping as ORM;
 use Symfony\Component\Security\Core\User\UserInterface;
 use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
+use Symfony\Component\Validator\Constraints as Assert;
 
 #[ORM\Entity(repositoryClass: UserRepository::class)]
 class User implements UserInterface, PasswordAuthenticatedUserInterface
@@ -17,18 +18,28 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
     private ?int $id = null;
 
     #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\NotBlank(message: "Username cannot be empty.")]
+    #[Assert\Length(min: 3, max: 50, minMessage: "Username must be at least {{ limit }} characters long.")]
     private ?string $userName = null;
 
     #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\NotBlank(message: "Name cannot be empty.")]
+    #[Assert\Length(min: 2, max: 100, minMessage: "Name must be at least {{ limit }} characters long.")]
     private ?string $name = null;
 
     #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\NotBlank(message: "Surname cannot be empty.")]
+    #[Assert\Length(min: 2, max: 100, minMessage: "Surname must be at least {{ limit }} characters long.")]
     private ?string $surname = null;
 
     #[ORM\Column(length: 255, unique: true)]
+    #[Assert\NotBlank(message: "Email cannot be empty.")]
+    #[Assert\Email(message: "Invalid email format.")]
     private ?string $email = null;
 
     #[ORM\Column(length: 255)]
+    #[Assert\NotBlank(message: "Password cannot be empty.")]
+    #[Assert\Length(min: 8, minMessage: "Password must be at least {{ limit }} characters long.")]
     private ?string $password = null;
 
     #[ORM\Column(nullable: true)]
diff --git a/server/web_app/src/Interface/UserControllerInterface.php b/server/web_app/src/Interface/UserControllerInterface.php
index 3fc74b5..a8dbe3b 100644
--- a/server/web_app/src/Interface/UserControllerInterface.php
+++ b/server/web_app/src/Interface/UserControllerInterface.php
@@ -10,13 +10,13 @@ interface UserControllerInterface
 
     public function getAllUsers(): Response;
 
-    public function getUserById(int $user_id): Response;
+    public function getUserById(int $userId): Response;
 
-    public function updateUser(int $user_id, Request $req): Response;
+    public function updateUser(int $userId, Request $req): Response;
 
-    public function deleteUser(int $user_id): Response;
+    public function deleteUser(int $userId): Response;
 
     public function authenticateUser(string $username, string $password): Response;
 
-    public function changePassword(int $user_id, string $old_password, string $new_password): Response;
+    public function changePassword(int $userId, string $oldPassword, string $newPassword): Response;
 }
diff --git a/server/web_app/src/Repository/UserRepository.php b/server/web_app/src/Repository/UserRepository.php
index b29153b..8e1efc7 100644
--- a/server/web_app/src/Repository/UserRepository.php
+++ b/server/web_app/src/Repository/UserRepository.php
@@ -16,6 +16,37 @@ class UserRepository extends ServiceEntityRepository
         parent::__construct($registry, User::class);
     }
 
+    public function findAllUsers (): array {
+        return $this->findAll();
+    }
+
+    // Custom query method to find active users
+    public function findActiveUsers(): array
+    {
+        return $this->createQueryBuilder('u')
+            ->andWhere('u.isBlocked = :isBlocked')
+            ->setParameter('isBlocked', false)
+            ->getQuery()
+            ->getResult();
+    }
+
+    // Custom query method to find a user by email
+    public function findOneByEmail(string $email): ?User
+    {
+        return $this->findOneBy(['email' => $email]);
+    }
+
+    // Custom query method to find users by role
+    public function findByRole(UserRole $role): array
+    {
+        return $this->createQueryBuilder('u')
+            ->andWhere('u.role = :role')
+            ->setParameter('role', $role)
+            ->getQuery()
+            ->getResult();
+    }
+
+
     //    /**
     //     * @return User[] Returns an array of User objects
     //     */
diff --git a/server/web_app/symfony.lock b/server/web_app/symfony.lock
index 65f77fc..4ddf444 100644
--- a/server/web_app/symfony.lock
+++ b/server/web_app/symfony.lock
@@ -116,5 +116,17 @@
             "config/packages/security.yaml",
             "config/routes/security.yaml"
         ]
+    },
+    "symfony/validator": {
+        "version": "7.2",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "main",
+            "version": "7.0",
+            "ref": "8c1c4e28d26a124b0bb273f537ca8ce443472bfd"
+        },
+        "files": [
+            "config/packages/validator.yaml"
+        ]
     }
 }
-- 
GitLab