Question

We use the following method to extend the product list collection inside a child template. This works fine for the product catalog list page, but gives a error (skips the load from the element) on the search page. The setProduct element does not work on search.

How can we solve this to make this also work on the search page?

Code:

<?php
namespace Vendor\CatalogProduct\Block\Product;

class Data extends \Magento\Framework\View\Element\Template
{
    private $product;

    public function setProduct($product)
    {
        $this->product = $product;
    }

    public function getProduct()
    {
      return $this->product;  
    }

}

Inside list.phtml;

<ul class="product-highlights-specs">
   <?php $block->getChildBlock("product-specs")->setProduct($_product);
   echo $block->getChildHtml('product-specs', false);?>
</ul>

Inside product-specs template;

<?php
$_product = $block->getProduct();
$attributeset = $_product->getAttributeSetId();

echo $_product->getName();?>
?>
Was it helpful?

Solution

Whilst the first answer works, not only it is rather messing up the template but more importantly, it is not reusable a lot. In your case, you want to reuse this product spec block in the search.

I have come up with a solution more robust but it comes with a cost that you have to do more changes in your system.

you have to create another layout file called view/frontend/layout/catalogsearch_result_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>              
        <referenceBlock name="search_result_list">
            <block class="Mbs\ProductList\Block\ProductSpec" name="product-specs"
                   template="Mbs_ProductList::product-specs.phtml" />
            <arguments>
                <argument name="viewModel" xsi:type="object">Mbs\ProductList\ViewModel\ProductListing</argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

the view Model can be any ViewModel, here I used a ViewModel that is already used in my product listing block.

the other category layout file was (for reminder)

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="category.products.list">
            <block class="Mbs\ProductList\Block\ProductSpec" name="product-specs"
                   template="Mbs_ProductList::product-specs.phtml" />
            <arguments>
                <argument name="viewModel" xsi:type="object">Mbs\ProductList\ViewModel\ProductListing</argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

the ViewModel may be as below:

<?php

namespace Mbs\ProductList\ViewModel;

use Magento\Framework\View\Element\Block\ArgumentInterface;
use Magento\Framework\View\Layout;

class ProductListing implements ArgumentInterface
{
    /**
     * @var Layout
     */
    private $layout;

    public function __construct(
        Layout $layout
    ) {
        $this->layout = $layout;
    }

    /**
     * @param \Magento\Catalog\Model\Product $_product
     * @return string
     */
    public function getProductSpecHtml(\Magento\Catalog\Model\Product $_product)
    {
        $html = '';

        $parentName = $this->getParentNameForProductListing();

        if ($parentName) {
            $blockSpecs = $this->layout->getChildBlock($parentName, 'product-specs');
            if ($blockSpecs) {
                $blockSpecs->setProduct($_product);
                $html = $blockSpecs->toHtml();
            }
        }

        return $html;
    }

    /**
     * @return string
     */
    private function getParentNameForProductListing(): string
    {
        $parent = '';

        if ($this->layout->getBlock('category.products.list')) {
            $parent = 'category.products.list';
        } elseif ($this->layout->getBlock('search_result_list')) {
            $parent = 'search_result_list';
        }

        return $parent;
    }
}

finally, place these lines at the top the listing template

/** @var \Mbs\ProductList\ViewModel\ProductListing $viewModel */
$viewModel = $block->getData('viewModel');

and this line wherever you need the spec block to render

<?php echo $viewModel->getProductSpecHtml($_product); ?>

Although this answer is a lot more robust, it is still not working everywhere. If you use the advanced search, you'd need to alter the viewModel slightly and add another layout file that handles the product listing block with its name on the advanced search

OTHER TIPS

Changing the list.phtml template, The below does work for me:

<?php
  $blockSpecs = $block->getChildBlock('product-specs');
 if ($blockSpecs) {
  $blockSpecs->setProduct($_product);
  echo $blockSpecs->toHtml();
}
?>
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top