From 664f23abddfe3eddb48fbc09802e237be94b9f22 Mon Sep 17 00:00:00 2001
From: Julian PEREZ-RAMIREZ <julian.perez-ramirez@imt-atlantique.net>
Date: Wed, 12 Mar 2025 18:48:51 +0100
Subject: [PATCH] adding validation annotation and changing db scheme according
 to last feedback

---
 .../web_app/src/Controller/ItemController.php |  1 -
 .../web_app/src/DataFixtures/ItemFixtures.php |  6 -----
 .../src/DataFixtures/WishListFixtures.php     | 12 +++++-----
 server/web_app/src/Entity/Item.php            | 12 +++++++---
 server/web_app/src/Entity/Purchase.php        | 11 +++++----
 server/web_app/src/Entity/User.php            | 21 +++++++++-------
 server/web_app/src/Entity/WishList.php        | 24 +++++++++++++++----
 server/web_app/src/Entity/WishListMember.php  | 14 ++++++++---
 server/web_app/src/Enum/UserRole.php          |  5 ++++
 .../web_app/src/Repository/ItemRepository.php | 10 --------
 10 files changed, 71 insertions(+), 45 deletions(-)

diff --git a/server/web_app/src/Controller/ItemController.php b/server/web_app/src/Controller/ItemController.php
index c113d3c..b200cec 100644
--- a/server/web_app/src/Controller/ItemController.php
+++ b/server/web_app/src/Controller/ItemController.php
@@ -75,7 +75,6 @@ final class ItemController extends AbstractController
             'description' =>$item->getDescription(),
             'price' =>$item->getPrice(),
             'purchaseUrl' =>$item->getPurchaseUrl(),
-            'isBought' =>$item->isBought(),
             'createdAt' => $item->getCreatedAt()    
         ];
 
diff --git a/server/web_app/src/DataFixtures/ItemFixtures.php b/server/web_app/src/DataFixtures/ItemFixtures.php
index 0afd18e..f383d42 100644
--- a/server/web_app/src/DataFixtures/ItemFixtures.php
+++ b/server/web_app/src/DataFixtures/ItemFixtures.php
@@ -24,7 +24,6 @@ class ItemFixtures extends Fixture implements DependentFixtureInterface
                 'title' => 'Smartphone',
                 'description' => 'Latest model with high-end specs',
                 'price' => 799.99,
-                'is_bought' => true,
                 'purchase_url' => 'https://example.com/smartphone',
             ],
             [
@@ -32,7 +31,6 @@ class ItemFixtures extends Fixture implements DependentFixtureInterface
                 'title' => 'Laptop',
                 'description' => 'Powerful laptop for work and gaming',
                 'price' => 1299.49,
-                'is_bought' => true,
                 'purchase_url' => 'https://example.com/laptop',
             ],
             [
@@ -40,7 +38,6 @@ class ItemFixtures extends Fixture implements DependentFixtureInterface
                 'title' => 'Wireless Headphones',
                 'description' => 'Noise-canceling headphones with long battery life',
                 'price' => 199.99,
-                'is_bought' => false,
                 'purchase_url' => 'https://example.com/headphones',
             ],
             [
@@ -48,7 +45,6 @@ class ItemFixtures extends Fixture implements DependentFixtureInterface
                 'title' => 'Coffee Maker',
                 'description' => 'Automatic coffee maker with multiple settings',
                 'price' => 89.99,
-                'is_bought' => false,
                 'purchase_url' => 'https://example.com/coffee-maker',
             ],
             [
@@ -56,7 +52,6 @@ class ItemFixtures extends Fixture implements DependentFixtureInterface
                 'title' => 'Gaming Chair',
                 'description' => 'Ergonomic chair for long gaming sessions',
                 'price' => 249.99,
-                'is_bought' => false,
                 'purchase_url' => 'https://example.com/gaming-chair',
             ]
         ];
@@ -67,7 +62,6 @@ class ItemFixtures extends Fixture implements DependentFixtureInterface
             $item->setTitle($itemData['title']);
             $item->setDescription($itemData['description']);
             $item->setPrice($itemData['price']);
-            $item->setIsBought($itemData['is_bought']);
             $item->setPurchaseUrl($itemData['purchase_url']);
             $item->setCreatedAt(new \DateTimeImmutable());
 
diff --git a/server/web_app/src/DataFixtures/WishListFixtures.php b/server/web_app/src/DataFixtures/WishListFixtures.php
index 6fa0ae3..1a0a557 100644
--- a/server/web_app/src/DataFixtures/WishListFixtures.php
+++ b/server/web_app/src/DataFixtures/WishListFixtures.php
@@ -26,8 +26,8 @@ class WishListFixtures extends Fixture implements DependentFixtureInterface
                 'user' => $users[0],  // Assuming at least one user exists
                 'expiration_date' => new \DateTime('+30 days'),
                 'is_active' => true,
-                'url_view_mode' => 'http:://url_view_mode.com',
-                'url_edit_mode' => 'http:://url_edit_mode.com',
+                'url_view_mode' => 'http://url_view_mode.com',
+                'url_edit_mode' => 'http://url_edit_mode.com',
             ],
             [
                 'name' => 'Tech Gadgets Wishlist',
@@ -35,8 +35,8 @@ class WishListFixtures extends Fixture implements DependentFixtureInterface
                 'user' => $users[1],  // Use second user if available, else first user
                 'expiration_date' => new \DateTime('+60 days'),
                 'is_active' => true,
-                'url_view_mode' => 'http:://url_view_mode.com',
-                'url_edit_mode' => 'http:://url_edit_mode.com',
+                'url_view_mode' => 'http://url_view_mode.com',
+                'url_edit_mode' => 'http://url_edit_mode.com',
             ],
             [
                 'name' => 'Home Essentials Wishlist',
@@ -44,8 +44,8 @@ class WishListFixtures extends Fixture implements DependentFixtureInterface
                 'user' => $users[2],  // Use third user if available, else first user
                 'expiration_date' => new \DateTime('+90 days'),
                 'is_active' => false,
-                'url_view_mode' => 'http:://url_view_mode.com',
-                'url_edit_mode' => 'http:://url_edit_mode.com',
+                'url_view_mode' => 'http://url_view_mode.com',
+                'url_edit_mode' => 'http://url_edit_mode.com',
             ],
         ];
 
diff --git a/server/web_app/src/Entity/Item.php b/server/web_app/src/Entity/Item.php
index e1e13f7..84d0437 100644
--- a/server/web_app/src/Entity/Item.php
+++ b/server/web_app/src/Entity/Item.php
@@ -4,6 +4,7 @@ namespace App\Entity;
 
 use App\Repository\ItemRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Validator\Constraints as Assert;
 
 #[ORM\Entity(repositoryClass: ItemRepository::class)]
 class Item
@@ -15,24 +16,29 @@ class Item
 
     #[ORM\ManyToOne(targetEntity: WishList::class)]
     #[ORM\JoinColumn(nullable: false)]
+    #[Assert\NotNull(message: "WishList must be selected.")]
     private ?WishList $wishList = null;
 
     #[ORM\Column(length: 255)]
+    #[Assert\NotBlank(message: "Title cannot be empty.")]
+    #[Assert\Length(max: 255, maxMessage: "Title cannot be longer than 255 characters.")]
     private ?string $title = null;
 
     #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\Length(max: 255, maxMessage: "Description cannot be longer than 255 characters.")]
     private ?string $description = null;
 
     #[ORM\Column]
+    #[Assert\NotNull(message: "Price cannot be null.")]
+    #[Assert\Positive(message: "Price must be a positive number.")]
     private ?float $price = null;
 
     #[ORM\Column(length: 255, nullable: true)]
     private ?string $purchaseUrl = null;
 
-    #[ORM\Column(length: 255, nullable: true)]
-    private ?bool $isBought = null;
-
     #[ORM\Column]
+    #[Assert\NotNull(message: "CreatedAt must be set.")]
+    #[Assert\Type(type: \DateTimeImmutable::class, message: "CreatedAt must be a valid DateTimeImmutable instance.")]
     private ?\DateTimeImmutable $createdAt = null;
 
     public function getId(): ?int
diff --git a/server/web_app/src/Entity/Purchase.php b/server/web_app/src/Entity/Purchase.php
index 7f5df95..edb95c7 100644
--- a/server/web_app/src/Entity/Purchase.php
+++ b/server/web_app/src/Entity/Purchase.php
@@ -4,30 +4,33 @@ namespace App\Entity;
 
 use App\Repository\PurchaseRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Validator\Constraints as Assert;
 
 #[ORM\Entity(repositoryClass: PurchaseRepository::class)]
 class Purchase
 {
     #[ORM\Id]
-    #[ORM\GeneratedValue]
-    #[ORM\Column]
-    private ?int $id = null;
-
     #[ORM\ManyToOne(targetEntity: User::class)]
     #[ORM\JoinColumn(nullable: false)]
     private ?User $user = null;
 
+    #[ORM\id]
     #[ORM\ManyToOne(targetEntity: Item::class)]
     #[ORM\JoinColumn(nullable: false)]
     private ?Item $item = null;
 
     #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\Length(max: 255, maxMessage: "URL proof cannot exceed 255 characters.")]
+    #[Assert\Url(message: "URL proof must be a valid URL.")]
     private ?string $urlProof = null;
 
     #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\Length(max: 255, maxMessage: "Message cannot exceed 255 characters.")]
     private ?string $message = null;
 
     #[ORM\Column]
+    #[Assert\NotNull(message: "CreatedAt must be set.")]
+    #[Assert\Type(type: \DateTimeImmutable::class, message: "CreatedAt must be a valid DateTimeImmutable instance.")]
     private ?\DateTimeImmutable $createdAt = null;
 
     public function getId(): ?int
diff --git a/server/web_app/src/Entity/User.php b/server/web_app/src/Entity/User.php
index 3e31285..532b2fd 100644
--- a/server/web_app/src/Entity/User.php
+++ b/server/web_app/src/Entity/User.php
@@ -17,17 +17,17 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
     #[ORM\Column]
     private ?int $id = null;
 
-    #[ORM\Column(length: 255, nullable: true)]
+    #[ORM\Column(length: 255, unique: 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)]
+    #[ORM\Column(length: 255)]
     #[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)]
+    #[ORM\Column(length: 255)]
     #[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;
@@ -42,17 +42,22 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
     #[Assert\Length(min: 8, minMessage: "Password must be at least {{ limit }} characters long.")]
     private ?string $password = null;
 
-    #[ORM\Column(nullable: true)]
-    private ?bool $isBlocked = null;
+    #[ORM\Column]
+    #[ORM\Column(type: 'boolean', options: ['default' => false])]
+    #[Assert\NotNull(message: "Blocked status cannot be null.")]
+    private ?bool $isBlocked = false;
 
-    #[ORM\Column(enumType: UserRole::class)]
+    #[ORM\Column(enumType: UserRole::class, options: ['default' => 'user'])]
     #[Assert\Choice(
-        callback: [UserRole::class, 'cases'],
+        callback: [UserRole::class, 'getValues'],
         message: "Invalid role value. Allowed values: {{ choices }}."
     )]
-    private ?UserRole $role = null;
+    private UserRole $role = UserRole::USER;
+
 
     #[ORM\Column]
+    #[Assert\NotNull(message: "CreatedAt must be set.")]
+    #[Assert\Type(type: \DateTimeImmutable::class, message: "CreatedAt must be a valid DateTimeImmutable instance.")]
     private ?\DateTimeImmutable $createdAt = null;
 
     public function getId(): ?int
diff --git a/server/web_app/src/Entity/WishList.php b/server/web_app/src/Entity/WishList.php
index 5bb2b14..8849ca9 100644
--- a/server/web_app/src/Entity/WishList.php
+++ b/server/web_app/src/Entity/WishList.php
@@ -7,6 +7,7 @@ use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\DBAL\Types\Types;
 use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Validator\Constraints as Assert;
 
 #[ORM\Entity(repositoryClass: WishListRepository::class)]
 class WishList
@@ -18,28 +19,43 @@ class WishList
 
     #[ORM\ManyToOne(targetEntity: User::class)]
     #[ORM\JoinColumn(nullable: false)]
+    #[Assert\NotNull(message: "User must be provided.")]
     private ?User $user = null;
 
     #[ORM\Column(length: 255)]
+    #[Assert\NotBlank(message: "Name cannot be empty.")]
+    #[Assert\Length(max: 255, maxMessage: "Name cannot be longer than 255 characters.")]
     private ?string $name = null;
 
-    #[ORM\Column(length: 255)]
+    #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\Length(max: 255, maxMessage: "Description cannot be longer than 255 characters.")]
     private ?string $description = null;
 
     #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
+    #[Assert\Type(type: \DateTimeInterface::class, message: "Expiration date must be a valid date.")]
+    #[Assert\GreaterThan("today", message: "Expiration date must be in the future.")]
     private ?\DateTimeInterface $expirationDate = null;
 
-    #[ORM\Column]
-    private ?bool $isActive = null;
+    #[ORM\Column(options: ['default' => true])]
+    #[Assert\NotNull(message: "isActive status must be set.")]
+    #[Assert\Type(type: 'bool', message: "isActive must be a boolean value.")]
+    private ?bool $isActive = true;
 
-    //add getters and setters 
     #[ORM\Column(length: 255)]
+    #[Assert\NotBlank(message: "View mode URL cannot be empty.")]
+    #[Assert\Length(max: 255, maxMessage: "View mode URL cannot be longer than 255 characters.")]
+    #[Assert\Url(message: "View mode URL must be a valid URL.")]
     private ?string $urlViewMode = null;
 
+    #[Assert\NotBlank(message: "Edit mode URL cannot be empty.")]
+    #[Assert\Length(max: 255, maxMessage: "Edit mode URL cannot be longer than 255 characters.")]
+    #[Assert\Url(message: "Edit mode URL must be a valid URL.")]
     #[ORM\Column(length: 255)]
     private ?string $urlEditMode = null;
 
     #[ORM\Column]
+    #[Assert\NotNull(message: "CreatedAt must be set.")]
+    #[Assert\Type(type: \DateTimeImmutable::class, message: "CreatedAt must be a valid DateTimeImmutable instance.")]
     private ?\DateTimeImmutable $createdAt = null;
 
     public function __construct()
diff --git a/server/web_app/src/Entity/WishListMember.php b/server/web_app/src/Entity/WishListMember.php
index cc617a2..07e3275 100644
--- a/server/web_app/src/Entity/WishListMember.php
+++ b/server/web_app/src/Entity/WishListMember.php
@@ -4,6 +4,7 @@ namespace App\Entity;
 
 use App\Repository\WishListMemberRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Validator\Constraints as Assert;
 
 #[ORM\Entity(repositoryClass: WishListMemberRepository::class)]
 class WishListMember
@@ -15,19 +16,26 @@ class WishListMember
 
     #[ORM\ManyToOne(targetEntity: WishList::class)]
     #[ORM\JoinColumn(nullable: false)]
+    #[Assert\NotNull(message: "WishList must be provided.")]
     private ?WishList $wishList = null;
 
     #[ORM\ManyToOne(targetEntity: User::class)]
     #[ORM\JoinColumn(nullable: false)]
+    #[Assert\NotNull(message: "User must be provided.")]
     private ?User $user = null;
 
     #[ORM\Column]
-    private ?bool $canEdit = null;
+    #[Assert\NotNull(message: "Edit permission (canEdit) must be set.")]
+    #[Assert\Type(type: 'bool', message: "canEdit must be a boolean value.")]
+    private ?bool $canEdit = false;
 
-    #[ORM\Column(nullable: true)]
-    private ?bool $isAccepted = null;
+    #[ORM\Column(nullable: true, options: ['default' => false])]
+    #[Assert\Type(type: 'bool', message: "isAccepted must be a boolean value.")]
+    private ?bool $isAccepted = false;
 
     #[ORM\Column]
+    #[Assert\NotNull(message: "CreatedAt must be set.")]
+    #[Assert\Type(type: \DateTimeImmutable::class, message: "CreatedAt must be a valid DateTimeImmutable instance.")]
     private ?\DateTimeImmutable $createdAt = null;
 
     public function getId(): ?int
diff --git a/server/web_app/src/Enum/UserRole.php b/server/web_app/src/Enum/UserRole.php
index 4b04056..f348d5a 100644
--- a/server/web_app/src/Enum/UserRole.php
+++ b/server/web_app/src/Enum/UserRole.php
@@ -14,6 +14,11 @@ enum UserRole: string
             self::USER => 'Regular User'
         };
     }
+
+    public static function getValues(): array
+    {
+        return array_column(self::cases(), 'value');
+    }
 }
 
 ?>
\ No newline at end of file
diff --git a/server/web_app/src/Repository/ItemRepository.php b/server/web_app/src/Repository/ItemRepository.php
index e148efa..48e7dca 100644
--- a/server/web_app/src/Repository/ItemRepository.php
+++ b/server/web_app/src/Repository/ItemRepository.php
@@ -20,16 +20,6 @@ class ItemRepository extends ServiceEntityRepository
         return $this->findAll();
     }
 
-    // Custom query method to find active users
-    public function findByStatus($isBought): array
-    {
-        return $this->createQueryBuilder('u')
-            ->andWhere('u.isBought = :isBought')
-            ->setParameter('isBought', $isBought)
-            ->getQuery()
-            ->getResult();
-    }
-
     // Custom query method to find a user by id
     public function findOneById(int $id): ?Item
     {
-- 
GitLab