From 4f1bdc2078e9c571bbcb4db5b88abef2d4b85ec6 Mon Sep 17 00:00:00 2001 From: Floran Pagliai Date: Fri, 23 May 2025 21:34:00 +0200 Subject: [PATCH] feat: add to cart --- symfony/README.md | 43 ++++++ symfony/src/Controller/CartController.php | 134 +++++++++++++++++++ symfony/src/Controller/ProductController.php | 16 +++ symfony/src/Entity/Cart.php | 83 ++++++++++++ symfony/src/Repository/CartRepository.php | 71 ++++++++++ symfony/templates/cart/view.html.twig | 115 ++++++++++++++++ symfony/templates/product/list.html.twig | 17 ++- symfony/templates/product/show.html.twig | 73 ++++++++++ 8 files changed, 550 insertions(+), 2 deletions(-) create mode 100644 symfony/README.md create mode 100644 symfony/src/Controller/CartController.php create mode 100644 symfony/src/Entity/Cart.php create mode 100644 symfony/src/Repository/CartRepository.php create mode 100644 symfony/templates/cart/view.html.twig create mode 100644 symfony/templates/product/show.html.twig diff --git a/symfony/README.md b/symfony/README.md new file mode 100644 index 0000000..a8145f1 --- /dev/null +++ b/symfony/README.md @@ -0,0 +1,43 @@ +# Codenudge Symfony demo + +## Features + +### Product Listing +The application includes a feature to display a list of products from the database. + +- **URL**: `/products` +- **Controller**: `ProductController::index` +- **Entity**: `Product` with properties: + - id (integer) + - name (string) + - price (float) + - description (text, nullable) + +### Shopping Cart +The application includes a feature to add products to a shopping cart. + +- **URLs**: + - `/cart` - View cart contents + - `/cart/add/{id}` - Add product to cart + - `/cart/remove/{id}` - Remove product from cart + - `/cart/clear` - Clear cart +- **Controllers**: + - `CartController::viewCart` + - `CartController::addToCart` + - `CartController::removeFromCart` + - `CartController::clearCart` +- **Entity**: `Cart` with properties: + - id (integer) + - sessionId (string) + - items (array) + - createdAt (datetime) + +### How to Use +1. Access the `/products` URL in your browser +2. View the list of products displayed in a table format +3. If no products exist, a message will be displayed +4. Click "Add to Cart" button to add a product to your cart +5. Specify the quantity of the product to add +6. View your cart by clicking "View Cart" button +7. Update quantities or remove items from your cart +8. Clear your cart by clicking "Clear Cart" button diff --git a/symfony/src/Controller/CartController.php b/symfony/src/Controller/CartController.php new file mode 100644 index 0000000..cf56726 --- /dev/null +++ b/symfony/src/Controller/CartController.php @@ -0,0 +1,134 @@ +cartRepository = $cartRepository; + $this->productRepository = $productRepository; + $this->session = $session; + } + + /** + * @Route("/cart", name="cart_view") + */ + public function viewCart() + { + $sessionId = $this->session->getId() ?: 'default_session'; + + $cartData = $this->cartRepository->findCartBySessionId($sessionId); + if (empty($cartData)) { + return $this->render('cart/view.html.twig', [ + 'items' => [], + 'total' => 0, + ]); + } + + $cart = $cartData[0]; + $items = json_decode($cart['items'], true); + + $products = []; + $total = 0; + foreach ($items as $productId => $quantity) { + $product = $this->productRepository->find($productId); + if ($product) { + $products[] = [ + 'product' => $product, + 'quantity' => $quantity, + 'subtotal' => $product->getPrice() * $quantity, + ]; + $total += $product->getPrice() * $quantity; + } + } + + return $this->render('cart/view.html.twig', [ + 'items' => $products, + 'total' => $total, + ]); + } + + /** + * @Route("/cart/add/{id}", name="cart_add") + */ + public function addToCart($id, Request $request) + { + $product = $this->productRepository->find($id); + + if (!$product) { + $this->addFlash('error', 'Product not found!'); + return $this->redirectToRoute('product_list'); + } + + $quantity = $request->query->get('quantity', 1); + + if (!is_numeric($quantity)) { + $quantity = 1; + } + + $sessionId = $this->session->getId() ?: 'default_session'; + + $this->cartRepository->addItemToCart($sessionId, $id, $quantity); + + $this->addFlash('success', 'Product added to cart!'); + return $this->redirectToRoute('product_list'); + } + + /** + * @Route("/cart/remove/{id}", name="cart_remove") + */ + public function removeFromCart($id) + { + $sessionId = $this->session->getId(); + + $cart = $this->cartRepository->findOneBy(['sessionId' => $sessionId]); + + if ($cart) { + $items = $cart->getItems(); + + unset($items[$id]); + + $cart->setItems($items); + + $entityManager = $this->getDoctrine()->getManager(); + $entityManager->persist($cart); + $entityManager->flush(); + } + return $this->redirectToRoute('cart_view'); + } + + /** + * @Route("/cart/clear", name="cart_clear") + */ + public function clearCart() + { + $sessionId = $this->session->getId(); + $cart = $this->cartRepository->findOneBy(['sessionId' => $sessionId]); + + if ($cart) { + $entityManager = $this->getDoctrine()->getManager(); + $entityManager->remove($cart); + $entityManager->flush(); + } + + return $this->redirectToRoute('product_list'); + } +} diff --git a/symfony/src/Controller/ProductController.php b/symfony/src/Controller/ProductController.php index 88dd491..e56309b 100644 --- a/symfony/src/Controller/ProductController.php +++ b/symfony/src/Controller/ProductController.php @@ -21,4 +21,20 @@ public function index(ProductRepository $productRepository) 'products' => $products, ]); } + + /** + * @Route("/product/{id}", name="product_show") + */ + public function show($id, ProductRepository $productRepository) + { + $product = $productRepository->find($id); + + if (!$product) { + throw new \Exception('Product not found!'); + } + + return $this->render('product/show.html.twig', [ + 'product' => $product, + ]); + } } diff --git a/symfony/src/Entity/Cart.php b/symfony/src/Entity/Cart.php new file mode 100644 index 0000000..2286b7d --- /dev/null +++ b/symfony/src/Entity/Cart.php @@ -0,0 +1,83 @@ +createdAt = new \DateTime('now'); + } + + public function getId() + { + return $this->id; + } + + public function getSessionId() + { + return $this->sessionId; + } + + public function setSessionId($sessionId) + { + $this->sessionId = $sessionId; + return $this; + } + + public function getItems() + { + return $this->items; + } + + public function setItems($items) + { + $this->items = $items; + return $this; + } + + public function addItem($productId, $quantity) + { + $this->items[$productId] = $quantity; + return $this; + } + + public function removeItem($productId) + { + unset($this->items[$productId]); + return $this; + } + + public function getCreatedAt() + { + return $this->createdAt; + } +} diff --git a/symfony/src/Repository/CartRepository.php b/symfony/src/Repository/CartRepository.php new file mode 100644 index 0000000..10511a2 --- /dev/null +++ b/symfony/src/Repository/CartRepository.php @@ -0,0 +1,71 @@ +entityManager = $entityManager; + } + + public function findCartBySessionId($sessionId) + { + $conn = $this->entityManager->getConnection(); + $sql = 'SELECT * FROM cart WHERE session_id = "' . $sessionId . '"'; + $stmt = $conn->prepare($sql); + $resultSet = $stmt->executeQuery(); + return $resultSet->fetchAllAssociative(); + } + + public function saveCart($sessionId, $items) + { + $cart = $this->findOneBy(['sessionId' => $sessionId]); + + if (!$cart) { + $cart = new Cart(); + $cart->setSessionId($sessionId); + } + + $cart->setItems($items); + $this->entityManager->persist($cart); + $this->entityManager->flush(); + + return $cart; + } + + public function addItemToCart($sessionId, $productId, $quantity) + { + $cart = $this->findOneBy(['sessionId' => $sessionId]); + + if (!$cart) { + $cart = new Cart(); + $cart->setSessionId($sessionId); + } + + $items = $cart->getItems(); + $items[$productId] = $quantity; + $cart->setItems($items); + $this->entityManager->persist($cart); + $this->entityManager->flush(); + + return $cart; + } + + public function clearAllCarts() + { + $conn = $this->entityManager->getConnection(); + $sql = 'DELETE FROM cart'; + $stmt = $conn->prepare($sql); + + $stmt->executeQuery(); + } +} diff --git a/symfony/templates/cart/view.html.twig b/symfony/templates/cart/view.html.twig new file mode 100644 index 0000000..b8336d8 --- /dev/null +++ b/symfony/templates/cart/view.html.twig @@ -0,0 +1,115 @@ +{% extends 'base.html.twig' %} + +{% block title %}Shopping Cart{% endblock %} + +{% block body %} +
+

Shopping Cart

+ + + + {% if items is empty %} +
+ Your cart is empty. +
+ {% else %} + + + + + + + + + + + + {% for item in items %} + + + + + + + + {% endfor %} + + + + + + + + +
ProductPriceQuantitySubtotalActions
{{ item.product.name }}${{ item.product.price }} +
+ + +
+
${{ item.subtotal }} + Remove +
Total:${{ total }}
+ +
+
+ + Clear Cart +
+
+ + +
+
+ + +
+

Recommended Products

+
+
+
+ Recommended Product 1 +
+
Recommended Product 1
+

$99.99

+ View +
+
+
+
+
+ Recommended Product 2 +
+
Recommended Product 2
+

$79.99

+ View +
+
+
+
+
+ Recommended Product 3 +
+
Recommended Product 3
+

$89.99

+ View +
+
+
+
+
+ {% endif %} +
+ + + +{% endblock %} diff --git a/symfony/templates/product/list.html.twig b/symfony/templates/product/list.html.twig index 4402ce7..059ef55 100644 --- a/symfony/templates/product/list.html.twig +++ b/symfony/templates/product/list.html.twig @@ -5,7 +5,11 @@ {% block body %}

Products

- + +
+ View Cart +
+ {% if products is empty %}

No products found.

{% else %} @@ -15,6 +19,7 @@ Name Price Description + Actions @@ -23,10 +28,18 @@ {{ product.name }} {{ product.price }} {{ product.description }} + + View + +
+ + +
+ {% endfor %} {% endif %}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/symfony/templates/product/show.html.twig b/symfony/templates/product/show.html.twig new file mode 100644 index 0000000..e9a26e0 --- /dev/null +++ b/symfony/templates/product/show.html.twig @@ -0,0 +1,73 @@ +{% extends 'base.html.twig' %} + +{% block title %}{{ product.name }}{% endblock %} + +{% block body %} +
+

{{ product.name }}

+ + + +
+
+ {{ product.name }} +
+
+
+
+
Product Details
+

Price: ${{ product.price }}

+

Description: {{ product.description }}

+ +
+
+ + +
+ +
+
+
+
+
+ +
+

Related Products

+
+
+
+ Related Product 1 +
+
Related Product 1
+

$99.99

+ View +
+
+
+
+
+ Related Product 2 +
+
Related Product 2
+

$79.99

+ View +
+
+
+
+
+ Related Product 3 +
+
Related Product 3
+

$89.99

+ View +
+
+
+
+
+
+{% endblock %}