rogervila/cart

View on GitHub
src/Cart.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Cart;

use Cart\Contracts\SessionContractInterface;
use Cart\Helpers\Calculator;
use Cart\Transformers\CartTransformer;
use Cart\Transformers\ItemTransformer;
use Money\Currencies\ISOCurrencies;
use Money\Currency;
use Money\Money;

/**
 * Class Cart
 * @package Cart
 */
class Cart
{
    use HasId, ReturnsProperties, Calculator, CartTransformer, ItemTransformer;

    // Default ID key for the Cart
    const DEFAULT_ID_KEY = '_defaultCartIdKey';

    /**
     * @var SessionContractInterface
     */
    protected $session;

    /**
     * @var array
     */
    protected $items = [];

    /**
     * @var Currency
     */
    protected $currency;

    /**
     * Cart constructor.
     *
     * @param null $id
     */
    public function __construct($id = null)
    {
        // The default session system is included
        $this->session = new Session();

        // Set a default ID if no value is passed
        if (is_null($id)) {
            return $this->setId();
        }

        // Retrieve it or create a new cart
        return $this->createOrRetrieve($id);
    }

    // ID

    /**
     * Overwrite trait method
     *
     * @param null $id
     *
     * @return $this
     */
    protected function setId($id = null)
    {
        $this->id = $this->handleId($id);

        // A new session row will be added with the new ID.
        $this->session->put($this->id, $this);

        // IMPORTANT: if the ID is changed, the old row will not be deleted automatically.
        return $this;
    }

    // Session

    /**
     * @param null $value
     *
     * @return mixed
     */
    public function session($value = null)
    {
        if ( ! is_null($value)) {
            return $this->setSession($value);
        }

        return $this->session;
    }


    /**
     * Change the Session system
     *
     * @param SessionContractInterface $contract
     *
     * @return $this
     */
    protected function setSession(SessionContractInterface $contract)
    {
        $this->session = $contract;
        $this->session->put($this->id, $this);

        return $this;
    }

    // ITEMS

    /**
     * Add an Item or an array of Items
     *
     * @param $arrayOrItem
     *
     * @return $this|Cart
     * @throws \Exception
     */
    public function add($arrayOrItem)
    {
        if ($arrayOrItem instanceof Item) {
            return $this->setItem($arrayOrItem);
        }

        if ( ! is_array($arrayOrItem)) {
            throw new \Exception('Value passed to Cart->add() is not an array nor an Item. Value passed: ' . json_encode($arrayOrItem));
        }

        foreach ($arrayOrItem as $item) {
            if ($item instanceof Item) {
                $this->setItem($item);
            }
        }

        $this->session->put($this->id, $this);

        return $this;
    }

    /**
     * @param Item $item
     *
     * @return $this
     * @throws \Exception
     */
    protected function setItem(Item $item)
    {
        // Check that there is no other item with the same ID
        if ($this->has($item)) {
            return $this;
        }

        // The item MUST have an ID
        if (is_null($item->id()) || $item->id() === '') {
            throw new \Exception("An item without ID can't be added to the cart. The item: " . json_encode($item));
        }

        // At least 1 quantity
        $item = $this->atLeastOneQuantity($item);

        // Price with or without currency
        $item = $this->parsePrice($item);

        // Add it to the Cart
        array_push($this->items, $item);

        // Append the full object to the session
        $this->session->put($this->id, $this);

        return $this;
    }

    /**
     * If no value is passed returns the current cart items. Else, acts like an alias of add()
     *
     * @param null $itemOrItems
     *
     * @return array|Cart
     */
    public function items($itemOrItems = null)
    {
        if (is_null($itemOrItems)) {
            return $this->items;
        }

        return $this->add($itemOrItems);
    }

    /**
     * @param $id
     *
     * @return array|bool
     */
    public function item($id)
    {
        return $this->getCartItemById($id);
    }

    /**
     * @param Item $item
     *
     * @return $this
     */
    public function update(Item $item)
    {
        // The item MUST have an ID
        $currentItem = $this->getCartItemById($item->id());

        if ( ! $currentItem instanceof Item) {
            return $this;
        }

        // returns property names as an array
        $itemProperties = $item->properties();

        // Update every $currentItem property with the $item ones
        foreach ($itemProperties as $property) {
            $value = $item->{$property}();

            if ( ! is_null($value) && $value !== '') {
                $currentItem->{$property}($value);
            }
        }

        // Append the full object to the session
        $this->session->put($this->id, $this);

        return $this;
    }

    /**
     * If getItemById returns a boolean, it has not found the Item
     *
     * @param Item $item
     *
     * @return bool
     */
    public function has(Item $item)
    {
        return ! is_bool($this->getCartItemById($item->id()));
    }


    // CURRENCY

    /**
     * @param null $value
     *
     * @return Currency
     */
    public function currency($value = null)
    {
        if ( ! is_null($value)) {
            $this->setCurrency($value);
        }

        return $this->currency;
    }

    /**
     * @param $currency
     *
     * @return $this
     */
    protected function setCurrency($currency)
    {
        $currencies = new ISOCurrencies();

        if ($currencies->contains(new Currency($currency))) {
            $this->currency = new Currency($currency);
        }

        $this->session->put($this->id, $this);

        return $this;
    }

    // RESULTS

    /**
     * @return float|Money
     */
    public function subtotal()
    {
        if ($this->currency instanceof Currency) {
            return $this->calculateSubtotalWithCurrency();
        }

        return $this->calculateSubtotalWithoutCurrency();
    }
}