We’ve already investigated the broken cart bug in cases when administrators delete simple child products. But, as it turns out, there can be other reasons for this bug.
Magento 2 has a very convenient feature for store administrators: they can change product type as they wish. When creating a product, an administrator can set the product weight, resulting in the product having the type “simple.” They can also not set the weight, resulting in a “virtual” product. Moreover, when administrators remove the weight from an existing “simple” product and save it, they get a “virtual” product and vice versa. They can also take any simple product that isn’t part of a configurable one and make it configurable on the product editing page, using the configuration component.
We will elaborate on this last scenario since in the hands of inexperienced users it could create issues for store users. This article will be about how to use this feature properly.
Chapter 1: Background
Let’s say there’s a Magento 2 store with the following cast of characters:
- the developer who made a site based on functional specifications and provides support for it.
- the store administrator who creates products, process orders, etc.
- the customer who is buying something in the store.
This store sells clothing and hats since these are products that usually require configuration.
The administrator created a simple product and made it available for purchase: a cap with a white pattern on a black background (CAP1).
Chapter 2: Product Changes
The customer goes to the store and adds this product to their cart, but for some reason, they don’t make a purchase and the product stays in the cart. The customer doesn’t return to the site again for some time. Then, the same exact cap appeared in the store’s inventory, but with a black pattern on a white background.
The administrator decided to turn the existing product, CAP1, into a configurable product with a color attribute. This is easy to do on the product editing page. In this instance, the product becomes configurable after saving, and two simple child products are automatically created.
This couldn’t be done in Magento 1 since product type could only be set when creating a product. You had to create a new configurable product and link the child simple products to it. The new product would have a new URL, and that would start age-old SEO problems.
Chapter 3: Error
So, the administrator turned the simple product into a configurable one, and a drop-down list with color choices (or visual swatches) appeared on the product page on the front end. Now the customer decides to log in again and complete the purchase, but when they go to their cart, they get a 503 error.
The customer can’t proceed to checkout to complete the order, so they go to the Contact Us page and write a complaint to the manager. The manager then creates a bug report for the developer. The developer looks at the server logs and sees the following error:
[Tue Mar 05 19:20:54.026160 2019] [php7:error] [pid 8691] [client 127.0.0.1:58142] PHP Fatal error: Uncaught Error: Call to a member function getValue() on null in /var/www/magento23/vendor/magento/module-configurable-product/Model/Quote/Item/CartItemProcessor.php:86\nStack trace:\n#0 /var/www/magento23/generated/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor/Proxy.php(103): Magento\\ConfigurableProduct\\Model\\Quote\\Item\\CartItemProcessor->processOptions(Object(Magento\\Quote\\Model\\Quote\\Item))\n#1 /var/www/magento23/vendor/magento/module-quote/Model/Quote/Item/CartItemOptionsProcessor.php(84): Magento\\ConfigurableProduct\\Model\\Quote\\Item\\CartItemProcessor\\Proxy->processOptions(Object(Magento\\Quote\\Model\\Quote\\Item))\n#2 /var/www/magento23/vendor/magento/module-quote/Model/Quote/Item/Repository.php(74): Magento\\Quote\\Model\\Quote\\Item\\CartItemOptionsProcessor->addProductOptions('configurable', Object(Magento\\Quote\\Model\\Quote\\Item))\n#3 /var/www/magento23/vendor/magento/module-checkout/Model/DefaultConfigProvider.php(430): Magento\\Quote\\Model\\Quote\\Item\\Repository->getList('1')\n#4 /var/www/magen in /var/www/magento23/vendor/magento/module-configurable-product/Model/Quote/Item/CartItemProcessor.php on line 86, referer: http://magento23.loc/index.php/
This is the error for Magento version 2.3. Earlier versions will show a slightly different error, but the essence is the same. The error occurs because when automatically converting the product type, the product type in the cart changed, too.
The configurable product in the cart should have a child element, as well as configuration options—but it doesn’t since the product wasn’t configurable when it was added to the cart. The error appears in the processOptions method when attempting to receive the value of the ‘attributes’ option, which for the reason mentioned above does not exist. The method code is below.
public function processOptions(CartItemInterface $cartItem)
{
$attributesOption = $cartItem->getProduct()->getCustomOption('attributes');
$selectedConfigurableOptions = $this->serializer->unserialize($attributesOption->getValue());
if (is_array($selectedConfigurableOptions)) {
$configurableOptions = [];
foreach ($selectedConfigurableOptions as $optionId => $optionValue) {
/** @var \Magento\ConfigurableProduct\Api\Data\ConfigurableItemOptionValueInterface $option */
$option = $this->itemOptionValueFactory->create();
$option->setOptionId($optionId);
$option->setOptionValue($optionValue);
$configurableOptions[] = $option;
}
$productOption = $cartItem->getProductOption()
? $cartItem->getProductOption()
: $this->productOptionFactory->create();
/** @var \Magento\Quote\Api\Data\ProductOptionExtensionInterface $extensibleAttribute */
$extensibleAttribute = $productOption->getExtensionAttributes()
? $productOption->getExtensionAttributes()
: $this->extensionFactory->create();
$extensibleAttribute->setConfigurableItemOptions($configurableOptions);
$productOption->setExtensionAttributes($extensibleAttribute);
$cartItem->setProductOption($productOption);
}
return $cartItem;
}
Useful for you:
Chapter 4: Solution for Broken Cart Using Product Aftersave Event Observer
The developer thought about the situation and decided that the user’s cart shouldn’t be broken, regardless of what the store administrator did. The developer implemented the following solution: they added a product aftersave event observer to the broken cart module.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="catalog_product_save_after">
<observer name="fix-customer-cart" instance="Web4pro\BrokenCart\Model\Observer" shared="false" />
</event>
</config>
class Observer implements \Magento\Framework\Event\ObserverInterface
{
protected $cartCleaner;
public function __construct(\Web4pro\BrokenCart\Model\QuoteItemsCleaner $cartCleaner)
{
$this->cartCleaner = $cartCleaner;
}
public function execute(\Magento\Framework\Event\Observer $observer)
{
if($product = $observer->getEvent()->getProduct()){
if(($product->getTypeId()=='configurable')&&($product->getOrigData('type_id')!='configurable')){
$this->cartCleaner->execute($product);
}
}
}
}
If a configurable product’s type becomes configurable when it’s saved, the product should be removed from all users’ carts. Indeed, after its type was changed, the product in the example is no longer the same cap with a white pattern on a black background that the customer added to their cart.
eCommerce rules dictate that the product should be deleted from the user’s cart. Now the store administrator can change the product type at will, without it resulting in the problem described above.