0

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();?>
?>
1

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

1
  • Thanks, works well! – JGeer May 31 '20 at 13:16
1

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();
}
?>
3
  • Thanks Herve! I tried it, but it seems that it results also in a page error on search, the same as my code. Any ideas? – JGeer May 29 '20 at 20:43
  • Of course.. tried this.. just updated it – Herve Tribouilloy May 30 '20 at 5:12
  • Thanks! This work, but it will skip all products, so there is no child template loaded in the search results. When we place the same code in the list.phtml it works fine. So it seems to break at some point on the search results. – JGeer May 30 '20 at 8:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.