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