diff --git a/api/migrations/Version20231118070207.php b/api/migrations/Version20231118070207.php new file mode 100644 index 0000000..dfd2db8 --- /dev/null +++ b/api/migrations/Version20231118070207.php @@ -0,0 +1,37 @@ +addSql('CREATE TABLE favorites (id INT AUTO_INCREMENT NOT NULL, fruit_id INT NOT NULL, date_added DATETIME DEFAULT NULL, UNIQUE INDEX UNIQ_E46960F5BAC115F0 (fruit_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE favorites ADD CONSTRAINT FK_E46960F5BAC115F0 FOREIGN KEY (fruit_id) REFERENCES fruits (id)'); + $this->addSql('ALTER TABLE favorite DROP FOREIGN KEY FK_68C58ED9BAC115F0'); + $this->addSql('DROP TABLE favorite'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE favorite (id INT AUTO_INCREMENT NOT NULL, fruit_id INT NOT NULL, date_added DATETIME DEFAULT NULL, UNIQUE INDEX UNIQ_68C58ED9BAC115F0 (fruit_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + $this->addSql('ALTER TABLE favorite ADD CONSTRAINT FK_68C58ED9BAC115F0 FOREIGN KEY (fruit_id) REFERENCES fruits (id) ON UPDATE NO ACTION ON DELETE NO ACTION'); + $this->addSql('ALTER TABLE favorites DROP FOREIGN KEY FK_E46960F5BAC115F0'); + $this->addSql('DROP TABLE favorites'); + } +} diff --git a/api/src/Controller/IndexController.php b/api/src/Controller/IndexController.php index 08479e1..c924f4e 100644 --- a/api/src/Controller/IndexController.php +++ b/api/src/Controller/IndexController.php @@ -8,8 +8,10 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use App\Repository\FruitRepository; +use App\Repository\FavoriteRepository; use Symfony\Component\Routing\Annotation\Route; use App\Entity\Fruit; +use App\Entity\Favorite; use Symfony\Component\Validator\Validator\ValidatorInterface; use Doctrine\ORM\EntityManagerInterface; @@ -22,6 +24,9 @@ public function __construct(private EntityManagerInterface $em) /** * Retrieve all fruits based on request parameter * + * @param \Symfony\Component\HttpFoundation\Request $request + * @param \App\Repository\FruitRepository $fruits + * * @return \Symfony\Component\HttpFoundation\JsonResponse */ #[Route('/', name: 'fruits', methods: ['GET'])] @@ -45,6 +50,9 @@ public function all(Request $request, FruitRepository $fruits): JsonResponse /** * Creates an instance of Fruit * + * @param \Symfony\Component\HttpFoundation\Request $request + * @param \Symfony\Component\Validator\Validator\ValidatorInterface $validator + * * @return \Symfony\Component\HttpFoundation\JsonResponse */ #[Route('/fruit', name: 'fruit_add', methods: ['POST', 'PUT'])] @@ -76,6 +84,9 @@ public function fruitNew(Request $request, ValidatorInterface $validator): JsonR /** * Get or patch a single Fruit instance based on Fruit ID provided * + * @param \Symfony\Component\HttpFoundation\Request $request + * @param \App\Repository\FruitRepository $fruits + * * @return \Symfony\Component\HttpFoundation\JsonResponse */ #[Route('/fruit/{id}', name: 'fruit', methods: ['GET', 'POST', 'PATCH', 'DELETE'])] @@ -119,9 +130,50 @@ public function fruit(Request $request, FruitRepository $fruits) return $this->json($fruit->toArray()); } + } + + /** + * Add fruit to favorites + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @param \App\Repository\FruitRepository $fruits + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + */ + #[Route('/favorite/{id}', name: 'favorite.add', methods: ['POST'])] + public function addToFavorite( + Request $request, + FruitRepository $fruits, + FavoriteRepository $favorites, + ) { + $id = (int) $request->attributes->get('id'); + $fruit = $fruits->find($id); + + // @INFO: Throw a 404 if a resource is not found + // @INFO: Ensure that request method is DELETE + if ( + !$fruit instanceof Fruit || + $request->getMethod() !== Request::METHOD_POST + ) { + return $this->json([], Response::HTTP_NOT_FOUND); + } + + $favorite = $favorites->findOneBy(['fruit' => $id]); + + + if (!$favorite instanceof Favorite) { + $favorite = (new Favorite()) + ->setFruit($fruit) + ->setDateAdded(new \DateTime('now')); + + $this->em->persist($favorite); + $this->em->flush(); + } - // @INFO: Default 404 return - return $this->json([], Response::HTTP_NOT_FOUND); + return $this->json([ + 'fruit' => $favorite->getFruit()->toArray(), + 'date_added' => $favorite->getId(), + ]); } /** diff --git a/api/src/Entity/Favorite.php b/api/src/Entity/Favorite.php index efe2cce..64988ce 100644 --- a/api/src/Entity/Favorite.php +++ b/api/src/Entity/Favorite.php @@ -7,8 +7,13 @@ use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: FavoriteRepository::class)] +#[ORM\Table(name: 'favorites')] class Favorite { + /** + * INFO: Set a maximum number of items in the favorites table + */ + public const MAX_FAVORITE = 10; #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] @@ -49,4 +54,9 @@ public function setDateAdded(?\DateTimeInterface $dateAdded): static return $this; } + + public function toArray(): array + { + return get_object_vars($this); + } } diff --git a/api/src/Entity/Fruit.php b/api/src/Entity/Fruit.php index b890bb7..7d5a392 100644 --- a/api/src/Entity/Fruit.php +++ b/api/src/Entity/Fruit.php @@ -258,4 +258,21 @@ public function toArray(): array { return get_object_vars($this); } + + public function getFavorite(): ?Favorite + { + return $this->favorite; + } + + public function setFavorite(Favorite $favorite): static + { + // set the owning side of the relation if necessary + if ($favorite->getFruit() !== $this) { + $favorite->setFruit($this); + } + + $this->favorite = $favorite; + + return $this; + } }