-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add to cart #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| <?php | ||
|
|
||
| namespace App\Controller; | ||
|
|
||
| use App\Entity\Cart; | ||
| use App\Entity\Product; | ||
| use App\Repository\CartRepository; | ||
| use App\Repository\ProductRepository; | ||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
| use Symfony\Component\HttpFoundation\Request; | ||
| use Symfony\Component\HttpFoundation\Response; | ||
| use Symfony\Component\HttpFoundation\Session\SessionInterface; | ||
| use Symfony\Component\Routing\Annotation\Route; | ||
|
|
||
| class CartController extends AbstractController | ||
| { | ||
| private $cartRepository; | ||
| private $productRepository; | ||
| private $session; | ||
|
|
||
| public function __construct( | ||
| CartRepository $cartRepository, | ||
| ProductRepository $productRepository, | ||
| SessionInterface $session | ||
| ) { | ||
| $this->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]); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bug (high): Inconsistency between CartController.removeFromCart() and CartController.viewCart(). The removeFromCart method uses findOneBy() which returns a Cart entity, but viewCart uses findCartBySessionId() which returns an array of data. This inconsistency will cause errors when users try to remove items from their cart because the code expects different data structures in different methods. Fix by using consistent repository methods: public function removeFromCart($id)
{
$sessionId = $this->session->getId();
$cartData = $this->cartRepository->findCartBySessionId($sessionId);
if (!empty($cartData)) {
$cart = $cartData[0];
$items = json_decode($cart['items'], true);
unset($items[$id]);
// Update the cart with modified items
$this->cartRepository->saveCart($sessionId, $items);
}
return $this->redirectToRoute('cart_view');
}Help us improve our suggestions - react with 👍 if it was helpful, 👎 if it needs work |
||
|
|
||
| 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'); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| <?php | ||
|
|
||
| namespace App\Entity; | ||
|
|
||
| use App\Repository\CartRepository; | ||
| use Doctrine\ORM\Mapping as ORM; | ||
|
|
||
| /** | ||
| * @ORM\Entity(repositoryClass="App\Repository\CartRepository") | ||
| */ | ||
| class Cart | ||
| { | ||
| /** | ||
| * @ORM\Id | ||
| * @ORM\GeneratedValue | ||
| * @ORM\Column(type="integer") | ||
| */ | ||
| private $id; | ||
|
|
||
| /** | ||
| * @ORM\Column(type="string", length=255) | ||
| */ | ||
| private $sessionId; | ||
|
|
||
| /** | ||
| * @ORM\Column(type="array") | ||
| */ | ||
| private $items = []; | ||
|
|
||
| /** | ||
| * @ORM\Column(type="datetime") | ||
| */ | ||
| private $createdAt; | ||
|
|
||
| public function __construct() | ||
| { | ||
| $this->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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| <?php | ||
|
|
||
| namespace App\Repository; | ||
|
|
||
| use App\Entity\Cart; | ||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||
| use Doctrine\Persistence\ManagerRegistry; | ||
| use Doctrine\ORM\EntityManagerInterface; | ||
|
|
||
| class CartRepository extends ServiceEntityRepository | ||
| { | ||
| private $entityManager; | ||
|
|
||
| public function __construct(ManagerRegistry $registry, EntityManagerInterface $entityManager) | ||
| { | ||
| parent::__construct($registry, Cart::class); | ||
| $this->entityManager = $entityManager; | ||
| } | ||
|
|
||
| public function findCartBySessionId($sessionId) | ||
| { | ||
| $conn = $this->entityManager->getConnection(); | ||
| $sql = 'SELECT * FROM cart WHERE session_id = "' . $sessionId . '"'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. security (critical): SQL Injection vulnerability in the findCartBySessionId method. The sessionId parameter is directly concatenated into the SQL query without proper escaping or parameterization. This allows attackers to inject malicious SQL code by manipulating the session ID, potentially leading to unauthorized data access or modification. Fix by using parameterized queries: $sql = 'SELECT * FROM cart WHERE session_id = :sessionId';
$stmt = $conn->prepare($sql);
$stmt->bindValue('sessionId', $sessionId);
$resultSet = $stmt->executeQuery();Alternatively, use Doctrine's query builder: return $this->createQueryBuilder('c')
->where('c.sessionId = :sessionId')
->setParameter('sessionId', $sessionId)
->getQuery()
->getResult();Help us improve our suggestions - react with 👍 if it was helpful, 👎 if it needs work |
||
| $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(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
security (medium): The addToCart method retrieves the quantity from query parameters but doesn't properly validate it. While there is a check for numeric values, it doesn't enforce positive integers or handle potential overflow issues.
Fix by adding proper validation:
Help us improve our suggestions - react with 👍 if it was helpful, 👎 if it needs work