175 lines
4.5 KiB
PHP
175 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Domain\Models\Entities;
|
|
|
|
use Domain\Models\ValueObjects\BasketProduct;
|
|
use Domain\Models\ValueObjects\Entities\BasketId;
|
|
use Domain\Models\ValueObjects\Entities\ProductId;
|
|
use Money\Money;
|
|
use Shared\DDD\Entity;
|
|
|
|
/** @implements \Shared\DDD\Entity<\Domain\Models\ValueObjects\Entities\BasketId> */
|
|
final class Basket implements Entity
|
|
{
|
|
/**
|
|
* @var array<string,\Domain\Models\ValueObjects\BasketProduct>
|
|
*/
|
|
private array $items = [];
|
|
|
|
/**
|
|
* @param array<array-key,\Domain\Models\ValueObjects\BasketProduct> $items
|
|
*/
|
|
public function __construct(
|
|
public readonly BasketId $id,
|
|
array $items = []
|
|
) {
|
|
foreach ($items as $item) {
|
|
if (! $item instanceof BasketProduct) {
|
|
throw new \DomainException('All items shall be of type BasketProduct');
|
|
}
|
|
$this->items[(string) $item->id->value] = $item;
|
|
}
|
|
}
|
|
|
|
#[\Override]
|
|
public function id(): BasketId
|
|
{
|
|
return $this->id;
|
|
}
|
|
|
|
public function has(ProductId $itemId): bool
|
|
{
|
|
return array_key_exists((string) $itemId->value, $this->items);
|
|
}
|
|
|
|
public function addOrUpdate(BasketProduct $item): void
|
|
{
|
|
if ($this->has($item->id)) {
|
|
$existingItem = $this->items[(string) $item->id->value];
|
|
$updatedItem = $this->cloneWithQuantity(
|
|
$existingItem,
|
|
$existingItem->qty + $item->qty
|
|
);
|
|
|
|
$this->items[(string) $item->id->value] = $updatedItem;
|
|
|
|
return;
|
|
}
|
|
|
|
$this->items[(string) $item->id->value] = $item;
|
|
}
|
|
|
|
public function remove(ProductId $itemId, int $qty): void
|
|
{
|
|
if ($qty < 1) {
|
|
throw new \DomainException('Cannot remove quantity less than 1');
|
|
}
|
|
|
|
if (! $this->has($itemId)) {
|
|
throw new \DomainException('Basket item does not exists');
|
|
}
|
|
|
|
$existingItem = $this->items[(string) $itemId->value];
|
|
|
|
if ($existingItem->qty <= $qty) {
|
|
$this->removeLine($itemId);
|
|
|
|
return;
|
|
}
|
|
|
|
$updatedItem = $this->cloneWithQuantity(
|
|
$existingItem,
|
|
$existingItem->qty - $qty
|
|
);
|
|
|
|
$this->items[(string) $itemId->value] = $updatedItem;
|
|
}
|
|
|
|
public function removeLine(ProductId $itemId): void
|
|
{
|
|
if (! $this->has($itemId)) {
|
|
throw new \DomainException('Basket item does not exists');
|
|
}
|
|
|
|
unset($this->items[(string) $itemId->value]);
|
|
}
|
|
|
|
/**
|
|
* @return array<string,\Domain\Models\ValueObjects\BasketProduct>
|
|
*/
|
|
public function items(): array
|
|
{
|
|
return $this->items;
|
|
}
|
|
|
|
public function totalTaxIncluded(): Money
|
|
{
|
|
return array_reduce(
|
|
$this->items,
|
|
fn (Money $total, BasketProduct $item) => $total->add($item->totalTtc()),
|
|
Money::EUR(0)
|
|
);
|
|
}
|
|
|
|
public function totalVat(): Money
|
|
{
|
|
return array_reduce(
|
|
$this->items,
|
|
fn (Money $total, BasketProduct $item) => $total->add($item->taxTotal()),
|
|
Money::EUR(0)
|
|
);
|
|
}
|
|
|
|
public function totalRaw(): Money
|
|
{
|
|
return array_reduce(
|
|
$this->items,
|
|
fn (Money $total, BasketProduct $item) => $total->add($item->subtotalHt()),
|
|
Money::EUR(0)
|
|
);
|
|
}
|
|
|
|
public function totalDiscount(): Money
|
|
{
|
|
return array_reduce(
|
|
$this->items,
|
|
fn (Money $total, BasketProduct $item) => $total->add($item->discountTotal()),
|
|
Money::EUR(0)
|
|
);
|
|
}
|
|
|
|
public function totalWeight(): int
|
|
{
|
|
return (int) array_sum(array_map(fn (BasketProduct $item) => $item->weightTotal(), $this->items));
|
|
}
|
|
|
|
public function countItems(): int
|
|
{
|
|
return (int) array_sum(array_map(fn (BasketProduct $item) => $item->qty, $this->items));
|
|
}
|
|
|
|
private function cloneWithQuantity(BasketProduct $item, int $qty): BasketProduct
|
|
{
|
|
return new BasketProduct(
|
|
$item->id,
|
|
$item->name,
|
|
$qty,
|
|
$item->unitPriceHt,
|
|
$item->unitDiscount,
|
|
$item->taxRate,
|
|
$item->weightGrams,
|
|
$item->brand,
|
|
$item->incoId,
|
|
$item->orderUnit,
|
|
$item->slug,
|
|
$item->picture,
|
|
$item->description,
|
|
$item->weightVariable,
|
|
$item->discountRate,
|
|
$item->priceKgTtc,
|
|
[...$item->options]
|
|
);
|
|
}
|
|
}
|