In this blog post, I would like to share a pretty interesting and not obvious solution on how to join some additional details to cart item block on checkout cart page.
Let’s say we need to add the “Brand” custom product attribute and show its value within a specific block on the product view page and checkout cart page. It’s very simple in the scope of the product page, but how to deal with number cart items on checkout cart page. There are a couple of issues which you may face with. We will cover them further.
Product View Page
If the default product information tab containing the list of visible product attributes is not enough, and you need to use some custom block with its own structure, in some specific place – you can simply retrieve the product from the registry and get the needed attribute value:
/** @var \Magento\Catalog\Model\Product $product */ $product = $this->registry->registry('product'); $brand = $product->getData('product_brand');
Where the $registry
property is the instance of Magento\Framework\Registry
class.
Magento Checkout Cart Page
Issue #1. There are multiple cart items which can be rendered on the checkout cart page. That’s why we need to find another way to get the product for each of our custom blocks separately.
Issue #2. The custom product attributes are not being added to selection while loading a product assigned to each cart item. We need to add the product_brand
attribute to selection.
First of all, we will create the layout update in order to insert the custom block into the cart item one:
<?xml version="1.0"?> <!-- File: app/code/Atwix/CatalogAttribute/view/frontend/layout/checkout_cart_index.xml --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="additional.product.info"> <block class="Atwix\CatalogAttribute\Block\CartItemBrandBlock" name="atwix.cart.item.brand" template="Atwix_CatalogAttribute::product_brand.phtml" before="-"> </block> </referenceBlock> </body> </page>
As the parent we will use the block with additional.product.info
name. The additional product information block is very helpful, because it allows to access the cart item of the Magento\Checkout\Block\Cart\Additional\Info::getItem
method. The additional product information block itself can be retrieved as a parent of our custom atwix.cart.item.brand
block.
The template provides a basic HTML structure and runs the Atwix\CatalogAttribute\Block\CartItemBrandBlock::getProductBrand
method:
<?php /** @var Atwix\CatalogAttribute\Block\CartItemBrandBlock $block */ /* File: app/code/Atwix/CatalogAttribute/view/frontend/templates/product_brand.phtml */ ?> <div class="product-brand"> <p><?php echo $block->getProductBrand(); ?></p> </div>
The main logic of CartItemBrandBlock class is the following:
<?php /* File: app/code/Atwix/CatalogAttribute/Block/CartItemBrandBlock.php */ namespace Atwix\CatalogAttribute\Block; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\Data\ProductInterfaceFactory; use Magento\Catalog\Model\Product; use Magento\Checkout\Block\Cart\Additional\Info as AdditionalBlockInfo; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Element\Template as ViewTemplate; use Magento\Framework\View\Element\Template\Context; /** * Class CartItemBrandBlock */ class CartItemBrandBlock extends ViewTemplate { /** * Product * * @var ProductInterface|null */ protected $product = null; /** * Product Factory * * @var ProductInterfaceFactory */ protected $productFactory; /** * CartItemBrandBlock constructor * * @param Context $context * @param ProductInterfaceFactory $productFactory */ public function __construct( Context $context, ProductInterfaceFactory $productFactory ) { parent::__construct($context); $this->productFactory = $productFactory; } /** * Get Product Brand Text * * @return string */ public function getProductBrand(): string { $product = $this->getProduct(); /** @var Product $product */ $productBrand = (string) $product->getData('product_brand'); return $productBrand; } /** * Get product from quote item * * @return ProductInterface */ public function getProduct(): ProductInterface { if ($this->product instanceof ProductInterface) { return $this->product; } try { $layout = $this->getLayout(); } catch (LocalizedException $e) { $this->product = $this->productFactory->create(); return $this->product; } /** @var AdditionalBlockInfo $block */ $block = $layout->getBlock('additional.product.info'); if ($block instanceof AdditionalBlockInfo) { $item = $block->getItem(); $this->product = $item->getProduct(); } return $this->product; } }
Basically, there are two steps to be done:
- Retrieve the corresponding quote item from the
additional.product.info
parent block. Then get the related product instance from quote item. - Get the
product_brand
attribute from the received product.
The finishing touch on our “composition” is adding the custom product_brand
attribute to selection for product entities collection. Otherwise, the whole previous implementation will not work.
<?xml version="1.0"?> <!-- File: app/code/Atwix/CatalogAttribute/etc/catalog_attributes.xml --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/catalog_attributes.xsd"> <group name="quote_item"> <attribute name="product_brand"/> </group> </config>
Curious about why we use use the catalog_attributes.xml
file, Find more details in my previous blog post on how to access the custom catalog attributes in Magento 2.
Let’s check the results to see how it works.
Thanks for reading. Hope you found it helpful and interesting.