Each user will have their own cart. As soon as a product is added to the cart, an Order is created in a database and then associated with the user via an Order ID in the session.
We will not manage user accounts, so the cart will be always associated with an anonymous user - a user who does not have a user account.
Creating the Session Storage
The cart has to be stored in the session to keep the items in the user's cart during their visit. Once the session has expired, the cart will be cleared. A new cart will be created and stored when the user adds a product to the cart.
Create a CartSessionStorage
service to manage the cart in the session and add the following operations:
- Add a
setCart()
method to save the cart in thecart_id
session key by using the cart ID, - Add a
getCart()
method to retrieve the cart in the session. Get the cart ID from thecart_id
session key, fetch the cart object by using theOrderRepository
repository, and return it.
<?php
namespace App\Storage;
use App\Entity\Order;
use App\Repository\OrderRepository;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class CartSessionStorage
{
/**
* The request stack.
*
* @var RequestStack
*/
private $requestStack;
/**
* The cart repository.
*
* @var OrderRepository
*/
private $cartRepository;
/**
* @var string
*/
const CART_KEY_NAME = 'cart_id';
/**
* CartSessionStorage constructor.
*
* @param RequestStack $requestStack
* @param OrderRepository $cartRepository
*/
public function __construct(RequestStack $requestStack, OrderRepository $cartRepository)
{
$this->requestStack = $requestStack;
$this->cartRepository = $cartRepository;
}
/**
* Gets the cart in session.
*
* @return Order|null
*/
public function getCart(): ?Order
{
return $this->cartRepository->findOneBy([
'id' => $this->getCartId(),
'status' => Order::STATUS_CART
]);
}
/**
* Sets the cart in session.
*
* @param Order $cart
*/
public function setCart(Order $cart): void
{
$this->getSession()->set(self::CART_KEY_NAME, $cart->getId());
}
/**
* Returns the cart id.
*
* @return int|null
*/
private function getCartId(): ?int
{
return $this->getSession()->get(self::CART_KEY_NAME);
}
private function getSession(): SessionInterface
{
return $this->requestStack->getSession();
}
}
Creating the Cart Manager
Create the CartManager
class that helps us retrieve the current cart of a user and saving the cart.
<?php
namespace App\Manager;
/**
* Class CartManager
* @package App\Manager
*/
class CartManager
{
}
Retrieving the current cart
For each action performed on the cart, we will need to retrieve the current cart:
- If the cart is already associated with the user in the session, it's the current cart,
- If the cart does not exist in the session, a new cart is created and becomes the current cart. It will be associated with the user in the session after being persisted in the database.
Add a getCurrentCart()
method to the CartManager
and retrieve the current cart. Get the cart already associated with the user using the CartSessionStorage
. If the cart does not exist, create a new cart by using the OrderFactory
factory. Finally, return the cart.
<?php
namespace App\Manager;
use App\Entity\Order;
use App\Factory\OrderFactory;
use App\Storage\CartSessionStorage;
/**
* Class CartManager
* @package App\Manager
*/
class CartManager
{
/**
* @var CartSessionStorage
*/
private $cartSessionStorage;
/**
* @var OrderFactory
*/
private $cartFactory;
/**
* CartManager constructor.
*
* @param CartSessionStorage $cartStorage
* @param OrderFactory $orderFactory
*/
public function __construct(
CartSessionStorage $cartStorage,
OrderFactory $orderFactory
) {
$this->cartSessionStorage = $cartStorage;
$this->cartFactory = $orderFactory;
}
/**
* Gets the current cart.
*
* @return Order
*/
public function getCurrentCart(): Order
{
$cart = $this->cartSessionStorage->getCart();
if (!$cart) {
$cart = $this->cartFactory->create();
}
return $cart;
}
}
Saving the Cart
Rather than storing the whole object in the session, we will persist the cart in the database to associate it with the user by using the Order ID.
Add a save()
method to the CartManager
, persist the cart in the database by using the EntityManager
service, and store the cart in session by using the CartSessionStorage
service.
<?php
namespace App\Manager;
use App\Entity\Order;
use App\Factory\OrderFactory;
use App\Storage\CartSessionStorage;
use Doctrine\ORM\EntityManagerInterface;
/**
* Class CartManager
* @package App\Manager
*/
class CartManager
{
/**
* @var CartSessionStorage
*/
private $cartSessionStorage;
/**
* @var OrderFactory
*/
private $cartFactory;
/**
* @var EntityManagerInterface
*/
private $entityManager;
/**
* CartManager constructor.
*
* @param CartSessionStorage $cartStorage
* @param OrderFactory $orderFactory
* @param EntityManagerInterface $entityManager
*/
public function __construct(
CartSessionStorage $cartStorage,
OrderFactory $orderFactory,
EntityManagerInterface $entityManager
) {
$this->cartSessionStorage = $cartStorage;
$this->cartFactory = $orderFactory;
$this->entityManager = $entityManager;
}
// ...
/**
* Persists the cart in database and session.
*
* @param Order $cart
*/
public function save(Order $cart): void
{
// Persist in database
$this->entityManager->persist($cart);
$this->entityManager->flush();
// Persist in session
$this->cartSessionStorage->setCart($cart);
}
}
With this approach, you will be able to associate a cart with a database user if you want to have a cart for logged in users. It is also a good way to let the user manage their cart on different devices and create an abandoned cart workflow.
Now that we can save carts and retrieve the current cart of the visitor, we are ready to add products to the cart.
Top comments (9)
I have this error "argument "$session" of method "__construct()" references interface "Symfony\Component\HttpFoundation\Session\SessionInterface" but no such service exists. "
And i don't really know how to fix any idea ?
Hello. Could you check if the session is enabled in the configuration file
config/packages/framework.yaml
? You should have :More details on symfony.com/doc/current/session.ht...
Done! but still this message
Cannot autowire service "App\Storage\CartSessionStorage": argument "$session" of method "__construct()" references interface "Symfony\Component\HttpFoundation\Session\SessionInterface" but no such service exists. Did you create a class that implements this interface?
my framework.yaml
What version of Symfony are you using?
Symfony 6.0
This tutorial was written with Symfony 5 and it's not compatible with version 6 at this time. In Symfony 6, the Session service has been removed. To get the Session, you now need to inject the RequestStack service and use the new getSession() method.
You need to upgrade the
CartSessionStorage
like that :It worked! It seems that everything is good now.
Thanks!
Try this
PHP bin/console req http
Hi thanks for this great tutorial,
I am taking it as reference for my project. But I have some issues (probably beginner issues)
In my project, the user is required to build a cart, so I just use UserRepository as CartSessionStorage. Is that fine ?
Secondly, I don't know how Forms pass data. When I submit addtocart, I get "too few arguments to function entity OrderItem::__construct(), 0 passed ... at least 2 expected".
this error occurs before entering in the controller method. The arguments needed are Order from the current connected user and the current item. Maybe you have an idea. As I am beginner, I don't understand how forms work