<?php
final readonly class Product
{
public function __construct(
public string $id,
public string $name,
public string $description,
public int $price = 10,
) {
}
}
final class Cart
{
/**
* @param Product[] $products
*/
public function __construct(
public string $id,
public array $products = [],
public int $total = 0,
public int $quantity = 0,
) {
}
public function addProduct(Product $product): void
{
$this->products[] = $product;
$this->quantity++;
$this->total += $product->price;
}
}
final readonly class ProductRepository
{
public function find(string $id): Product
{
// Simulate loading from database
return new Product($id, "product-$id", "description-$id");
}
public function findByIds(array $ids): array
{
// Simulate loading from database
$products = [];
foreach ($ids as $id) {
$products[] = $this->find($id);
}
return $products;
}
}
final readonly class CartRepository
{
private ReflectionClass $reflector;
public function __construct(
private ProductRepository $productRepository,
) {
$this->reflector = new ReflectionClass(Cart::class);
}
public function serialize(Cart $cart): string
{
return json_encode([
'id' => $cart->id,
'products' => array_column($cart->products, 'id'),
'total' => $cart->total,
'quantity' => $cart->quantity,
]);
}
public function unserialize(string $data): Cart
{
var_dump('...Unserializing cart...');
$previousCart = json_decode($data, true);
$newCart = $this->reflector->newLazyGhost(function(Cart $newCart) use ($previousCart): void {
var_dump('...Loading products...');
$products = $this->productRepository->findByIds($previousCart['products']);
$this->reflector->getProperty('products')->setValue($newCart, $products);
});
$this->reflector->getProperty('id')->setRawValueWithoutLazyInitialization($newCart, $previousCart['id']);
$this->reflector->getProperty('total')->setRawValueWithoutLazyInitialization($newCart, $previousCart['total']);
$this->reflector->getProperty('quantity')->setRawValueWithoutLazyInitialization($newCart, $previousCart['quantity']);
return $newCart;
}
}
$productRepository = new ProductRepository();
$repository = new CartRepository($productRepository);
$cart = new Cart('#cart-01');
$cart->addProduct($productRepository->find('1'));
$cart->addProduct($productRepository->find('2'));
$cart->addProduct($productRepository->find('3'));
// Usually, the $serialized string would be stored in a database or in the
// session
$serialized = $repository->serialize($cart);
// A new HTTP request is made and the $serialized string is retrieved from the
// DB or the sessions
$newCart = $repository->unserialize($serialized);
var_dump($newCart);
var_dump($newCart->total);
$newCart->products;
var_dump($newCart);