File: D:/HostingSpaces/SBogers10/vangogh.komma.pro/app/Komma/Shop/Cart/ShoppingCartService.php
<?php
namespace App\Komma\Shop\Cart;
use App\Komma\Shop\Discounts\Actions\ModifyPriceAction;
use App\Komma\Shop\Discounts\DiscountableInterface;
use App\Komma\Shop\Discounts\Discount;
use App\Komma\Shop\Discounts\DiscountServiceInterface;
use App\Komma\Shop\Discounts\DiscountTypes;
use App\Komma\Shop\Products\Product\Product;
use App\Komma\Shop\Products\ProductableInterface;
/**
* Represents a shopping cart
*
* Class ShoppingCartService
* @package App\Komma\Shop\Cart
*/
class ShoppingCartService implements ShoppingCartServiceInterface, HasCouponCodesInterface
{
use HasCouponCodesTrait {
addCouponCode as traitAddCouponCode;
removeCouponCode as traitRemoveCouponCode;
}
/**
* @var string the session root variable where to store shopping cart related data
*/
public static $sessionVariable = 'ShoppingCartService';
/**
* Indicates whether or not if the ShoppingCartService restores all items in the shopping cart by looking at the session variable
*
* @var bool
*/
public static $restoreFromSession = true;
/**
* @var ShoppingCartItemInterface[] $items
*/
private $items;
/**
* @var Discount[] $discounts
*/
private $discounts;
/**
* @var DiscountServiceInterface $discountService
*/
private $discountService;
public function __construct()
{
$this->items = [];
$this->discounts = [];
$this->discountService = app(DiscountServiceInterface::class);
$this->restoreFromSession();
}
/**
* Add a productable in the cart
*
* @param ProductableInterface $productable
* @param int $amount
* @return ShoppingCartItem
*/
public function addProductable(ProductableInterface $productable, int $amount = 1) : ?ShoppingCartItemInterface
{
/** @var Product $product */
if(!$productable) return null;
/** @var ShoppingCartItemInterface $shoppingCartItem */
$item = $this->getShoppingCartItemByProductableIdAndType($productable);
if($item) {
$item->setAmount($item->getAmount() + $amount);
} else {
$item = (app(ShoppingCartItemInterface::class))->setProductable($productable)->setAmount($amount);
$this->items[] = $item;
}
$this->updateDiscounts();
$this->saveSession();
return $item;
}
/**
* Returns an existing shoppingCartItem by checking if it has a productable with a
* certain id or false if there was no shoppingcart item with a
* productable with the specified productable id
* @param $productable
* @return ShoppingCartItemInterface|false
*/
private function getShoppingCartItemByProductableIdAndType(ProductableInterface $productable)
{
foreach($this->items as $item)
{
$itemProductable = $item->getProductable();
/** @var ShoppingCartItemInterface $item */
if($itemProductable->id == $productable->id && get_class($productable) === get_class($itemProductable)) return $item;
}
return false;
}
/**
* Set the amount of items for a product
*
* @param int $itemId
* @param int $amount
* @return $this
*/
public function setItemAmount(int $itemId, int $amount = 1): ShoppingCartServiceInterface
{
$item = $this->getItemById($itemId);
if(!$item) return $this;
$item->setAmount($amount);
$this->updateDiscounts();
$this->saveSession();
return $this;
}
/**
* Deletes the ShoppingCartItem that has a product with the specified id
*
* @param ProductableInterface $productable
* @return ShoppingCartServiceInterface
*/
public function deleteItem(ProductableInterface $productable) : ShoppingCartServiceInterface
{
foreach($this->items as $index => $item)
{
if($item->getProductable()->id == $productable->id)
{
array_splice($this->items, $index, 1);
break;
}
}
$this->updateDiscounts();
$this->saveSession();
return $this;
}
/**
* Clears the shopping cart of all items
*
* @return ShoppingCartServiceInterface
*/
public function clear(): ShoppingCartServiceInterface
{
$this->items = [];
$this->updateDiscounts();
$this->saveSession();
return $this;
}
/**
* Returns the sum of all base prices of products.
* If you specify the boolean argument as true it takes the CartDiscounts into account
* Remember the base price is in cents.
*
* @param bool $includeDiscounts
* @return float
*/
public function getTotal(bool $includeDiscounts = true): float
{
$price = 0;
if(count($this->items) > 0) {
foreach ($this->items as $index => $item) $price += $item->getTotal($includeDiscounts);
}
if($includeDiscounts)
{
foreach($this->discounts as $discount)
{
$action = $discount->getDiscountAction();
switch (get_class($action))
{
case ModifyPriceAction::class;
$price = $action->do($price);
}
}
}
// dd($this); //debugging helper
return $price;
}
/**
* Returns the amount of items in the cart.
* If you specify the $unique argument as true it returns the number of ShoppingCartItems.
* If it is false it will return the sum of all products in all Shopping cart items
* If you specify the $includeCartDiscounts argument as true it takes the Discount into account
* @param bool $unique
* @param bool $includeDiscounts
* @return int
*/
public function getItemsCount(bool $unique = false, bool $includeDiscounts = false): int
{
if($includeDiscounts === false) {
$itemCount = 0;
if($unique == true) {
$products = [];
foreach($this->items as $shoppingCartItem)
{
/** @var $shoppingCartItem ShoppingCartItem */
$productClass = get_class($shoppingCartItem->getProductable());
if(!in_array($productClass, $products))
{
$itemCount++;
$products[] = $productClass;
}
}
} else {
foreach($this->items as $item) $itemCount = $itemCount + $item->getAmount();
}
return $itemCount;
} else {
//TODO Implement me
return 0;
}
}
/**
* Get all shopping cart items
*
* @return array
*/
public function getItems(): array
{
return !empty($this->items) ? $this->items : [];
}
/**
* Applies the discount to itself
*
* @param Discount $discount
* @return DiscountableInterface
*/
public function applyDiscount(Discount $discount): DiscountableInterface
{
$this->discounts[] = $discount;
$this->saveSession();
return $this;
}
/**
* Returns all discounts applied
*
* @return Discount[]
*/
public function getDiscounts(): array
{
return $this->discounts;
}
/**
* Returns all discounts applied, from a certain type
*
* @param null $type
* @return Discount[]
*/
public function getDiscountsByType($type = null): array
{
if($type == null) return $this->discounts;
if(!DiscountTypes::isValidType($type)) throw new \InvalidArgumentException('The given type is not a valid DiscountType.');
return array_filter($this->discounts, function(Discount $discount) use ($type) {
return $discount->type == $type;
});
}
/**
* Get a shoppingCartItem by ID
*
* @param $itemId int
* @return ShoppingCartItemInterface
*/
private function getItemById(int $itemId): ?ShoppingCartItemInterface
{
$returnItem = null;
foreach($this->items as $index => $item) {
if ($item->getId() == $itemId) {
$returnItem = $item;
}
}
return $returnItem;
}
/**
* Validates a coupon code
*
* @param string $code
* @return bool
*/
public function validateCouponCode(string $code): bool
{
return $this->discountService->checkIfCouponCodeCanBeApplied($code, $this);
}
/**
* Removes all discounts
*/
public function clearDiscounts(): void
{
$this->discounts = [];
foreach($this->items as $shoppingCartItem) $shoppingCartItem->clearDiscounts();
}
/**
* Updates the discounts on the shopping cart and all shoppingCartItems after removing all discounts from them.
*/
public function updateDiscounts(): void
{
$this->clearDiscounts();
$this->discountService->updateDiscounts($this);
foreach($this->items as $shoppingCartItem) $this->discountService->updateDiscounts($shoppingCartItem);
}
/**
* Adds a coupon code. Notice that this does not mean that it is valid.
* Validation is done via the DiscountServiceInterface. Returns true if
* it was added. false if not
*
* @param string $code
* @return mixed
*/
public function addCouponCode(string $code): bool
{
$result = $this->traitAddCouponCode($code);
$this->updateDiscounts();
$this->saveSession();
return $result;
}
/**
* Removes a coupon code and returns true if it was removed and false if not.
*
* @param string $code
* @return bool
*/
public function removeCouponCode(string $code): bool
{
$result = $this->traitRemoveCouponCode($code);
$this->updateDiscounts();
$this->saveSession();
return $result;
}
/**
* Update the session variable with shopping cart info
*/
public function saveSession()
{
session()->put(self::$sessionVariable, [
'items' => $this->items,
'discounts' => $this->discounts,
'couponCodes' => $this->couponCodes
]);
}
/**
* Restores the shopping cart using the session.
*
* Warning! This will not work when this function is called via __constructors in
* laravel controllers. Since the session is not initialized yet.
*
* @return void
*/
private function restoreFromSession(): void
{
if(session()->has(self::$sessionVariable)) {
$sessionArray = session()->get(self::$sessionVariable);
if(isset($sessionArray['items'])) $this->items = $sessionArray['items'];
if(isset($sessionArray['discounts'])) $this->discounts = $sessionArray['discounts'];
if(isset($sessionArray['couponCodes'])) $this->couponCodes = $sessionArray['couponCodes'];
}
}
}