Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Item.php
Go to the documentation of this file.
1 <?php
7 
10 
98 class Item extends \Magento\Quote\Model\Quote\Item\AbstractItem implements \Magento\Quote\Api\Data\CartItemInterface
99 {
105  protected $_eventPrefix = 'sales_quote_item';
106 
114  protected $_eventObject = 'item';
115 
121  protected $_quote;
122 
128  protected $_options = [];
129 
135  protected $_optionsByCode = [];
136 
142  protected $_notRepresentOptions = ['info_buyRequest'];
143 
149 
155  protected $_errorInfos;
156 
160  protected $_localeFormat;
161 
166 
170  protected $quoteItemCompare;
171 
176  protected $stockRegistry;
177 
183  private $serializer;
184 
204  public function __construct(
205  \Magento\Framework\Model\Context $context,
206  \Magento\Framework\Registry $registry,
207  ExtensionAttributesFactory $extensionFactory,
210  \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
211  \Magento\Sales\Model\Status\ListFactory $statusListFactory,
212  \Magento\Framework\Locale\FormatInterface $localeFormat,
213  \Magento\Quote\Model\Quote\Item\OptionFactory $itemOptionFactory,
215  \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
216  \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
217  \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
218  array $data = [],
219  \Magento\Framework\Serialize\Serializer\Json $serializer = null
220  ) {
221  $this->_errorInfos = $statusListFactory->create();
222  $this->_localeFormat = $localeFormat;
223  $this->_itemOptionFactory = $itemOptionFactory;
224  $this->quoteItemCompare = $quoteItemCompare;
225  $this->stockRegistry = $stockRegistry;
226  $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
227  ->get(\Magento\Framework\Serialize\Serializer\Json::class);
228  parent::__construct(
229  $context,
230  $registry,
231  $extensionFactory,
235  $resource,
236  $resourceCollection,
237  $data
238  );
239  }
240 
246  protected function _construct()
247  {
248  $this->_init(\Magento\Quote\Model\ResourceModel\Quote\Item::class);
249  }
250 
256  public function beforeSave()
257  {
258  parent::beforeSave();
259  $this->setIsVirtual($this->getProduct()->getIsVirtual());
260  if ($this->getQuote()) {
261  $this->setQuoteId($this->getQuote()->getId());
262  }
263  return $this;
264  }
265 
271  public function getAddress()
272  {
273  if ($this->getQuote()->getItemsQty() == $this->getQuote()->getVirtualItemsQty()) {
274  $address = $this->getQuote()->getBillingAddress();
275  } else {
276  $address = $this->getQuote()->getShippingAddress();
277  }
278 
279  return $address;
280  }
281 
288  public function setQuote(\Magento\Quote\Model\Quote $quote)
289  {
290  $this->_quote = $quote;
291  $this->setQuoteId($quote->getId());
292  $this->setStoreId($quote->getStoreId());
293  return $this;
294  }
295 
303  public function getQuote()
304  {
305  return $this->_quote;
306  }
307 
314  protected function _prepareQty($qty)
315  {
316  $qty = $this->_localeFormat->getNumber($qty);
317  $qty = $qty > 0 ? $qty : 1;
318  return $qty;
319  }
320 
327  public function addQty($qty)
328  {
333  if (!$this->getParentItem() || !$this->getId()) {
334  $qty = $this->_prepareQty($qty);
335  $this->setQtyToAdd($qty);
336  $this->setQty($this->getQty() + $qty);
337  }
338  return $this;
339  }
340 
347  public function setQty($qty)
348  {
349  $qty = $this->_prepareQty($qty);
350  $oldQty = $this->_getData(self::KEY_QTY);
351  $this->setData(self::KEY_QTY, $qty);
352 
353  $this->_eventManager->dispatch('sales_quote_item_qty_set_after', ['item' => $this]);
354 
355  if ($this->getQuote() && $this->getQuote()->getIgnoreOldQty()) {
356  return $this;
357  }
358 
359  if ($this->getUseOldQty()) {
360  $this->setData(self::KEY_QTY, $oldQty);
361  }
362 
363  return $this;
364  }
365 
375  public function getQtyOptions()
376  {
377  $qtyOptions = $this->getData('qty_options');
378  if ($qtyOptions === null) {
379  $productIds = [];
380  $qtyOptions = [];
381  foreach ($this->getOptions() as $option) {
383  if (is_object($option->getProduct())
384  && $option->getProduct()->getId() != $this->getProduct()->getId()
385  ) {
386  $productIds[$option->getProduct()->getId()] = $option->getProduct()->getId();
387  }
388  }
389 
390  foreach ($productIds as $productId) {
391  $option = $this->getOptionByCode('product_qty_' . $productId);
392  if ($option) {
393  $qtyOptions[$productId] = $option;
394  }
395  }
396 
397  $this->setData('qty_options', $qtyOptions);
398  }
399 
400  return $qtyOptions;
401  }
402 
411  public function setQtyOptions($qtyOptions)
412  {
413  return $this->setData('qty_options', $qtyOptions);
414  }
415 
422  public function setProduct($product)
423  {
424  if ($this->getQuote()) {
425  $product->setStoreId($this->getQuote()->getStoreId());
426  $product->setCustomerGroupId($this->getQuote()->getCustomerGroupId());
427  }
428  $this->setData('product', $product)
429  ->setProductId($product->getId())
430  ->setProductType($product->getTypeId())
431  ->setSku($this->getProduct()->getSku())
432  ->setName($product->getName())
433  ->setWeight($this->getProduct()->getWeight())
434  ->setTaxClassId($product->getTaxClassId())
435  ->setBaseCost($product->getCost());
436 
437  $stockItem = $product->getExtensionAttributes()->getStockItem();
438  $this->setIsQtyDecimal($stockItem ? $stockItem->getIsQtyDecimal() : false);
439 
440  $this->_eventManager->dispatch(
441  'sales_quote_item_set_product',
442  ['product' => $product, 'quote_item' => $this]
443  );
444 
445  return $this;
446  }
447 
454  public function representProduct($product)
455  {
456  $itemProduct = $this->getProduct();
457  if (!$product || $itemProduct->getId() != $product->getId()) {
458  return false;
459  }
460 
465  $stickWithinParent = $product->getStickWithinParent();
466  if ($stickWithinParent) {
467  if ($this->getParentItem() !== $stickWithinParent) {
468  return false;
469  }
470  }
471 
472  // Check options
473  $itemOptions = $this->getOptionsByCode();
474  $productOptions = $product->getCustomOptions();
475 
476  if (!$this->compareOptions($itemOptions, $productOptions)) {
477  return false;
478  }
479  if (!$this->compareOptions($productOptions, $itemOptions)) {
480  return false;
481  }
482  return true;
483  }
484 
494  public function compareOptions($options1, $options2)
495  {
496  foreach ($options1 as $option) {
497  $code = $option->getCode();
498  if (in_array($code, $this->_notRepresentOptions)) {
499  continue;
500  }
501  if (!isset($options2[$code]) || $options2[$code]->getValue() != $option->getValue()) {
502  return false;
503  }
504  }
505  return true;
506  }
507 
514  public function compare($item)
515  {
516  return $this->quoteItemCompare->compare($this, $item);
517  }
518 
524  public function getProductType()
525  {
526  $option = $this->getOptionByCode(self::KEY_PRODUCT_TYPE);
527  if ($option) {
528  return $option->getValue();
529  }
530  $product = $this->getProduct();
531  if ($product) {
532  return $product->getTypeId();
533  }
534  // $product should always exist or there will be an error in getProduct()
535  return $this->_getData(self::KEY_PRODUCT_TYPE);
536  }
537 
545  public function getRealProductType()
546  {
547  return $this->_getData(self::KEY_PRODUCT_TYPE);
548  }
549 
556  public function toArray(array $arrAttributes = [])
557  {
558  $data = parent::toArray($arrAttributes);
559 
560  $product = $this->getProduct();
561  if ($product) {
562  $data['product'] = $product->toArray();
563  }
564  return $data;
565  }
566 
573  public function setOptions($options)
574  {
575  if (is_array($options)) {
576  foreach ($options as $option) {
577  $this->addOption($option);
578  }
579  }
580  return $this;
581  }
582 
590  public function getOptions()
591  {
592  return $this->_options;
593  }
594 
602  public function getOptionsByCode()
603  {
604  return $this->_optionsByCode;
605  }
606 
614  public function addOption($option)
615  {
616  if (is_array($option)) {
617  $option = $this->_itemOptionFactory->create()->setData($option)->setItem($this);
618  } elseif ($option instanceof \Magento\Framework\DataObject &&
619  !$option instanceof \Magento\Quote\Model\Quote\Item\Option
620  ) {
621  $option = $this->_itemOptionFactory->create()->setData(
622  $option->getData()
623  )->setProduct(
624  $option->getProduct()
625  )->setItem(
626  $this
627  );
628  } elseif ($option instanceof \Magento\Quote\Model\Quote\Item\Option) {
629  $option->setItem($this);
630  } else {
631  throw new \Magento\Framework\Exception\LocalizedException(__('We found an invalid item option format.'));
632  }
633 
634  $exOption = $this->getOptionByCode($option->getCode());
635  if ($exOption) {
636  $exOption->addData($option->getData());
637  } else {
638  $this->_addOptionCode($option);
639  $this->_options[] = $option;
640  }
641  return $this;
642  }
643 
653  public function updateQtyOption(\Magento\Framework\DataObject $option, $value)
654  {
655  $optionProduct = $option->getProduct();
656  $options = $this->getQtyOptions();
657 
658  if (isset($options[$optionProduct->getId()])) {
659  $options[$optionProduct->getId()]->setValue($value);
660  }
661 
662  $this->getProduct()->getTypeInstance()->updateQtyOption(
663  $this->getOptions(),
664  $option,
665  $value,
666  $this->getProduct()
667  );
668 
669  return $this;
670  }
671 
678  public function removeOption($code)
679  {
680  $option = $this->getOptionByCode($code);
681  if ($option) {
682  $option->isDeleted(true);
683  }
684  return $this;
685  }
686 
694  protected function _addOptionCode($option)
695  {
696  if (!isset($this->_optionsByCode[$option->getCode()])) {
697  $this->_optionsByCode[$option->getCode()] = $option;
698  } else {
699  throw new \Magento\Framework\Exception\LocalizedException(
700  __('An item option with code %1 already exists.', $option->getCode())
701  );
702  }
703  return $this;
704  }
705 
712  public function getOptionByCode($code)
713  {
714  if (isset($this->_optionsByCode[$code]) && !$this->_optionsByCode[$code]->isDeleted()) {
715  return $this->_optionsByCode[$code];
716  }
717  return null;
718  }
719 
726  protected function _hasModelChanged()
727  {
728  if (!$this->hasDataChanges()) {
729  return false;
730  }
731 
732  return $this->_getResource()->hasDataChanged($this);
733  }
734 
740  public function saveItemOptions()
741  {
742  foreach ($this->_options as $index => $option) {
743  if ($option->isDeleted()) {
744  $option->delete();
745  unset($this->_options[$index]);
746  unset($this->_optionsByCode[$option->getCode()]);
747  } else {
748  if (!$option->getItem() || !$option->getItem()->getId()) {
749  $option->setItem($this);
750  }
751  $option->save();
752  }
753  }
754 
755  $this->_flagOptionsSaved = true;
756  // Report to watchers that options were saved
757 
758  return $this;
759  }
760 
769  public function setIsOptionsSaved($flag)
770  {
771  $this->_flagOptionsSaved = $flag;
772  }
773 
781  public function isOptionsSaved()
782  {
784  }
785 
791  public function afterSave()
792  {
793  $this->saveItemOptions();
794  return parent::afterSave();
795  }
796 
802  public function __clone()
803  {
804  parent::__clone();
805  $options = $this->getOptions();
806  $this->_quote = null;
807  $this->_options = [];
808  $this->_optionsByCode = [];
809  foreach ($options as $option) {
810  $this->addOption(clone $option);
811  }
812  return $this;
813  }
814 
821  public function getBuyRequest()
822  {
823  $option = $this->getOptionByCode('info_buyRequest');
824  $data = $option ? $this->serializer->unserialize($option->getValue()) : [];
825  $buyRequest = new \Magento\Framework\DataObject($data);
826 
827  // Overwrite standard buy request qty, because item qty could have changed since adding to quote
828  $buyRequest->setOriginalQty($buyRequest->getQty())->setQty($this->getQty() * 1);
829 
830  return $buyRequest;
831  }
832 
839  protected function _setHasError($flag)
840  {
841  return $this->setData('has_error', $flag);
842  }
843 
854  public function setHasError($flag)
855  {
856  if ($flag) {
857  $this->addErrorInfo();
858  } else {
859  $this->_clearErrorInfo();
860  }
861  return $this;
862  }
863 
870  protected function _clearErrorInfo()
871  {
872  $this->_errorInfos->clear();
873  $this->_setHasError(false);
874  return $this;
875  }
876 
887  public function addErrorInfo($origin = null, $code = null, $message = null, $additionalData = null)
888  {
889  $this->_errorInfos->addItem($origin, $code, $message, $additionalData);
890  if ($message !== null) {
891  $this->setMessage($message);
892  }
893  $this->_setHasError(true);
894 
895  return $this;
896  }
897 
903  public function getErrorInfos()
904  {
905  return $this->_errorInfos->getItems();
906  }
907 
917  {
918  $removedItems = $this->_errorInfos->removeItemsByParams($params);
919  foreach ($removedItems as $item) {
920  if ($item['message'] !== null) {
921  $this->removeMessageByText($item['message']);
922  }
923  }
924 
925  if (!$this->_errorInfos->getItems()) {
926  $this->_setHasError(false);
927  }
928 
929  return $this;
930  }
931 
937  public function getItemId()
938  {
939  return $this->getData(self::KEY_ITEM_ID);
940  }
941 
945  public function setItemId($itemID)
946  {
947  return $this->setData(self::KEY_ITEM_ID, $itemID);
948  }
949 
953  public function getSku()
954  {
955  return $this->getData(self::KEY_SKU);
956  }
957 
961  public function setSku($sku)
962  {
963  return $this->setData(self::KEY_SKU, $sku);
964  }
965 
969  public function getQty()
970  {
971  return $this->getData(self::KEY_QTY);
972  }
973 
977  public function getName()
978  {
979  return $this->getData(self::KEY_NAME);
980  }
981 
985  public function setName($name)
986  {
987  return $this->setData(self::KEY_NAME, $name);
988  }
989 
993  public function getPrice()
994  {
995  return $this->getData(self::KEY_PRICE);
996  }
997 
1001  public function setPrice($price)
1002  {
1003  return $this->setData(self::KEY_PRICE, $price);
1004  }
1005 
1009  public function setProductType($productType)
1010  {
1011  return $this->setData(self::KEY_PRODUCT_TYPE, $productType);
1012  }
1013 
1017  public function getQuoteId()
1018  {
1019  return $this->getData(self::KEY_QUOTE_ID);
1020  }
1021 
1025  public function setQuoteId($quoteId)
1026  {
1027  return $this->setData(self::KEY_QUOTE_ID, $quoteId);
1028  }
1029 
1035  public function getProductOption()
1036  {
1037  return $this->getData(self::KEY_PRODUCT_OPTION);
1038  }
1039 
1047  {
1048  return $this->setData(self::KEY_PRODUCT_OPTION, $productOption);
1049  }
1050 
1051  //@codeCoverageIgnoreEnd
1052 
1058  public function getExtensionAttributes()
1059  {
1060  return $this->_getExtensionAttributes();
1061  }
1062 
1069  public function setExtensionAttributes(\Magento\Quote\Api\Data\CartItemExtensionInterface $extensionAttributes)
1070  {
1071  return $this->_setExtensionAttributes($extensionAttributes);
1072  }
1073 }
addErrorInfo($origin=null, $code=null, $message=null, $additionalData=null)
Definition: Item.php:887
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
toArray(array $arrAttributes=[])
Definition: Item.php:556
setExtensionAttributes(\Magento\Quote\Api\Data\CartItemExtensionInterface $extensionAttributes)
Definition: Item.php:1069
_setExtensionAttributes(\Magento\Framework\Api\ExtensionAttributesInterface $extensionAttributes)
$quote
__()
Definition: __.php:13
$resource
Definition: bulk.php:12
$price
$message
setProductType($productType)
Definition: Item.php:1009
$address
Definition: customer.php:38
setQuote(\Magento\Quote\Model\Quote $quote)
Definition: Item.php:288
$value
Definition: gender.phtml:16
removeErrorInfosByParams($params)
Definition: Item.php:916
updateQtyOption(\Magento\Framework\DataObject $option, $value)
Definition: Item.php:653
__construct(\Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, ExtensionAttributesFactory $extensionFactory, AttributeValueFactory $customAttributeFactory, \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, \Magento\Sales\Model\Status\ListFactory $statusListFactory, \Magento\Framework\Locale\FormatInterface $localeFormat, \Magento\Quote\Model\Quote\Item\OptionFactory $itemOptionFactory, \Magento\Quote\Model\Quote\Item\Compare $quoteItemCompare, \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, \Magento\Framework\Model\ResourceModel\AbstractResource $resource=null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection=null, array $data=[], \Magento\Framework\Serialize\Serializer\Json $serializer=null)
Definition: Item.php:204
compareOptions($options1, $options2)
Definition: Item.php:494
foreach($product->getExtensionAttributes() ->getBundleProductOptions() as $option) $buyRequest
setQtyOptions($qtyOptions)
Definition: Item.php:411
$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE]
Definition: website.php:18
setProductOption(\Magento\Quote\Api\Data\ProductOptionInterface $productOption)
Definition: Item.php:1046
$index
Definition: list.phtml:44
$code
Definition: info.phtml:12
if(!isset($_GET['name'])) $name
Definition: log.php:14