vendor/isotope/isotope-core/system/modules/isotope/library/Isotope/Model/ProductCollection/Cart.php line 92

Open in your IDE?
  1. <?php
  2. /*
  3.  * Isotope eCommerce for Contao Open Source CMS
  4.  *
  5.  * Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup
  6.  *
  7.  * @link       https://isotopeecommerce.org
  8.  * @license    https://opensource.org/licenses/lgpl-3.0.html
  9.  */
  10. namespace Isotope\Model\ProductCollection;
  11. use Contao\Controller;
  12. use Contao\FrontendUser;
  13. use Contao\Input;
  14. use Contao\PageModel;
  15. use Contao\System;
  16. use Isotope\Interfaces\IsotopeOrderableCollection;
  17. use Isotope\Isotope;
  18. use Isotope\Message;
  19. use Isotope\Model\Address;
  20. use Isotope\Model\Config;
  21. use Isotope\Model\ProductCollection;
  22. /**
  23.  * Class Cart provides methods to handle Isotope cart.
  24.  */
  25. class Cart extends ProductCollection implements IsotopeOrderableCollection
  26. {
  27.     /**
  28.      * Cookie hash value
  29.      * @var string
  30.      */
  31.     protected $strHash '';
  32.     /**
  33.      * Name of the temporary cart cookie
  34.      * @var string
  35.      */
  36.     protected static $strCookie 'ISOTOPE_TEMP_CART';
  37.     /**
  38.      * Draft of Order for this cart
  39.      * @var Order
  40.      */
  41.     protected $objDraftOrder;
  42.     /**
  43.      * A cart does not have a payment method,
  44.      * but the order might require payment for surcharges (e.g. shipping)
  45.      */
  46.     public function requiresPayment()
  47.     {
  48.         $draftOrder $this->getDraftOrder();
  49.         if (null !== $draftOrder) {
  50.             return $draftOrder->requiresPayment();
  51.         }
  52.         return parent::requiresPayment();
  53.     }
  54.     /**
  55.      * Get billing address or create if none exists
  56.      *
  57.      * @return Address
  58.      */
  59.     public function getBillingAddress()
  60.     {
  61.         if (!empty($this->arrCache['billingAddress'])) {
  62.             return $this->arrCache['billingAddress'];
  63.         }
  64.         $objAddress parent::getBillingAddress();
  65.         // Try to load the default member address
  66.         if (null === $objAddress && FE_USER_LOGGED_IN === true) {
  67.             $objAddress Address::findDefaultBillingForMember(FrontendUser::getInstance()->id);
  68.         }
  69.         // Try to load the default collection address
  70.         if (null === $objAddress) {
  71.             $objAddress Address::findDefaultBillingForProductCollection($this->id);
  72.         }
  73.         // Last option: create a new address, including member data if available
  74.         if (null === $objAddress) {
  75.             $objAddress Address::createForProductCollection(
  76.                 $this,
  77.                 Isotope::getConfig()->getBillingFields(),
  78.                 true
  79.             );
  80.         }
  81.         $this->arrCache['billingAddress'] = $objAddress;
  82.         return $objAddress;
  83.     }
  84.     /**
  85.      * Get shipping address or create if none exists
  86.      *
  87.      * @return Address
  88.      */
  89.     public function getShippingAddress()
  90.     {
  91.         if (!empty($this->arrCache['shippingAddress'])) {
  92.             return $this->arrCache['shippingAddress'];
  93.         }
  94.         $objAddress parent::getShippingAddress();
  95.         // Try to load the default member address
  96.         if (null === $objAddress && FE_USER_LOGGED_IN === true) {
  97.             $objAddress Address::findDefaultShippingForMember(FrontendUser::getInstance()->id);
  98.         }
  99.         // Try to load the default collection address
  100.         if (null === $objAddress) {
  101.             $objAddress Address::findDefaultShippingForProductCollection($this->id);
  102.         }
  103.         // Last option: create a new address, including member data if available
  104.         if (null === $objAddress) {
  105.             $objAddress Address::createForProductCollection(
  106.                 $this,
  107.                 Isotope::getConfig()->getShippingFields(),
  108.                 false,
  109.                 true
  110.             );
  111.         }
  112.         $this->arrCache['shippingAddress'] = $objAddress;
  113.         return $objAddress;
  114.     }
  115.     /**
  116.      * Merge guest cart if necessary
  117.      *
  118.      * @throws \BadMethodCallException if the product collection is locked.
  119.      */
  120.     public function mergeGuestCart()
  121.     {
  122.         $this->ensureNotLocked();
  123.         $strHash = (string) Input::cookie(static::$strCookie);
  124.         // Temporary cart available, move to this cart. Must be after creating a new cart!
  125.         if (FE_USER_LOGGED_IN === true && '' !== $strHash && $this->member 0) {
  126.             $blnMerge $this->countItems() > 0;
  127.             $objTemp = static::findOneBy(array('uniqid=?''store_id=?'), array($strHash$this->store_id));
  128.             if (null !== $objTemp) {
  129.                 $arrIds $this->copyItemsFrom($objTemp);
  130.                 if ($blnMerge && \count($arrIds) > 0) {
  131.                     Message::addConfirmation($GLOBALS['TL_LANG']['MSC']['cartMerged']);
  132.                 }
  133.                 $objTemp->delete();
  134.             }
  135.             // Delete cookie
  136.             System::setCookie(static::$strCookie''time() - 3600);
  137.             Controller::reload();
  138.         }
  139.     }
  140.     /**
  141.      * Get and update order draft for current cart or create one if it does not yet exist
  142.      *
  143.      * @return Order
  144.      */
  145.     public function getDraftOrder()
  146.     {
  147.         if ($this->objDraftOrder === null) {
  148.             $t Order::getTable();
  149.             $objOrder Order::findOneBy(
  150.                 array(
  151.                     "$t.source_collection_id=?",
  152.                     "$t.locked IS NULL"
  153.                 ),
  154.                 array($this->id)
  155.             );
  156.             if (null === $objOrder) {
  157.                 $objOrder Order::createFromCollection($this);
  158.             }
  159.             try {
  160.                 $objOrder->config_id = (int) $this->config_id;
  161.                 $objOrder->store_id  = (int) $this->store_id;
  162.                 $objOrder->member    = (int) $this->member;
  163.                 $objOrder->setShippingMethod($this->getShippingMethod());
  164.                 $objOrder->setPaymentMethod($this->getPaymentMethod());
  165.                 $objOrder->setBillingAddress($this->getBillingAddress());
  166.                 if ($this->shipping_address_id) {
  167.                     $objOrder->setShippingAddress($this->getShippingAddress());
  168.                 } else {
  169.                     $objOrder->setShippingAddress($this->getBillingAddress());
  170.                 }
  171.                 $objOrder->purge();
  172.                 $arrItemIds $objOrder->copyItemsFrom($this);
  173.                 $objOrder->updateDatabase();
  174.                 // HOOK: order status has been updated
  175.                 if (isset($GLOBALS['ISO_HOOKS']['updateDraftOrder'])
  176.                     && \is_array($GLOBALS['ISO_HOOKS']['updateDraftOrder'])
  177.                 ) {
  178.                     foreach ($GLOBALS['ISO_HOOKS']['updateDraftOrder'] as $callback) {
  179.                         System::importStatic($callback[0])->{$callback[1]}($objOrder$this$arrItemIds);
  180.                     }
  181.                 }
  182.             } catch (\Exception $e) {
  183.                 $objOrder null;
  184.             }
  185.             $this->objDraftOrder $objOrder;
  186.         }
  187.         return $this->objDraftOrder;
  188.     }
  189.     /**
  190.      * Check if minimum order amount is reached
  191.      *
  192.      * @return bool
  193.      */
  194.     public function hasErrors()
  195.     {
  196.         if (Isotope::getConfig()->cartMinSubtotal && Isotope::getConfig()->cartMinSubtotal $this->getSubtotal()) {
  197.             return true;
  198.         }
  199.         return parent::hasErrors();
  200.     }
  201.     /**
  202.      * Get error messages for the cart
  203.      *
  204.      * @return array
  205.      */
  206.     public function getErrors()
  207.     {
  208.         $arrErrors parent::getErrors();
  209.         if (Isotope::getConfig()->cartMinSubtotal && Isotope::getConfig()->cartMinSubtotal $this->getSubtotal()) {
  210.             $arrErrors[] = sprintf(
  211.                 $GLOBALS['TL_LANG']['ERR']['cartMinSubtotal'],
  212.                 Isotope::formatPriceWithCurrency(Isotope::getConfig()->cartMinSubtotal)
  213.             );
  214.         }
  215.         return $arrErrors;
  216.     }
  217.     /**
  218.      * {@inheritdoc}
  219.      */
  220.     public function save()
  221.     {
  222.         parent::save();
  223.         // Create/renew the guest cart cookie
  224.         if (!$this->member && !headers_sent()) {
  225.             System::setCookie(
  226.                 static::$strCookie,
  227.                 $this->uniqid,
  228.                 $this->tstamp $GLOBALS['TL_CONFIG']['iso_cartTimeout']
  229.             );
  230.         }
  231.         return $this;
  232.     }
  233.     /**
  234.      * Get a collection-specific error message for items with errors
  235.      *
  236.      * @return string
  237.      */
  238.     protected function getMessageIfErrorsInItems()
  239.     {
  240.         return $GLOBALS['TL_LANG']['ERR']['cartErrorInItems'];
  241.     }
  242.     /**
  243.      * Clear all cache properties
  244.      */
  245.     protected function clearCache()
  246.     {
  247.         parent::clearCache();
  248.         $this->objDraftOrder null;
  249.     }
  250.     /**
  251.      * Load the current cart
  252.      *
  253.      * @return Cart
  254.      */
  255.     public static function findForCurrentStore()
  256.     {
  257.         /** @var PageModel $objPage */
  258.         global $objPage;
  259.         if ('FE' !== TL_MODE || null === $objPage || === (int) $objPage->rootId) {
  260.             return null;
  261.         }
  262.         /** @var PageModel|\stdClass $rootPage */
  263.         $rootPage PageModel::findByPk($objPage->rootId);
  264.         $time       time();
  265.         $objCart    null;
  266.         $cookieHash null;
  267.         $storeId    = (int) $rootPage->iso_store_id;
  268.         if (true === FE_USER_LOGGED_IN) {
  269.             $objCart = static::findOneBy(
  270.                 array('tl_iso_product_collection.member=?''store_id=?'),
  271.                 array(FrontendUser::getInstance()->id$storeId)
  272.             );
  273.         } else {
  274.             $cookieHash = (string) Input::cookie(static::$strCookie);
  275.             if ('' !== $cookieHash) {
  276.                 $objCart = static::findOneBy(array('uniqid=?''store_id=?'), array($cookieHash$storeId));
  277.             }
  278.             if (null === $objCart) {
  279.                 $cookieHash self::generateCookieId();
  280.             }
  281.         }
  282.         // Create new cart
  283.         if ($objCart === null) {
  284.             $objConfig Config::findByRootPageOrFallback($objPage->rootId);
  285.             $objCart   = new static();
  286.             // Can't call the individual rows here, it would trigger markModified and a save()
  287.             $objCart->setRow(array_merge($objCart->row(), array(
  288.                 'tstamp'    => $time,
  289.                 'member'    => FE_USER_LOGGED_IN === true FrontendUser::getInstance()->id 0,
  290.                 'uniqid'    => $cookieHash,
  291.                 'config_id' => $objConfig->id,
  292.                 'store_id'  => $storeId,
  293.             )));
  294.             return $objCart;
  295.         }
  296.         $objCart->tstamp $time;
  297.         // Renew the guest cart cookie
  298.         if (!$objCart->member) {
  299.             // Create a new cookie ID if it's an old sha1() hash
  300.             if (40 == strlen($objCart->uniqid)) {
  301.                 $objCart->uniqid self::generateCookieId();
  302.             }
  303.             if (!headers_sent()) {
  304.                 System::setCookie(
  305.                     static::$strCookie,
  306.                     $objCart->uniqid,
  307.                     $time $GLOBALS['TL_CONFIG']['iso_cartTimeout']
  308.                 );
  309.             }
  310.         }
  311.         return $objCart;
  312.     }
  313.     private static function generateCookieId()
  314.     {
  315.         if (!function_exists('random_bytes')) {
  316.             return uniqid(''true);
  317.         }
  318.         try {
  319.             return bin2hex(random_bytes(32));
  320.         } catch (\Exception $e) {
  321.             return uniqid(''true);
  322.         }
  323.     }
  324. }