Magento 2 Cross-sell Products – How to Display on Product Page

magento2product-viewrelated-products

I have been trying to get crosssell products to show on the product pages. however adding the block does not work as it relies on the product being in the cart. I have tried to overcome this by following the following post: Magento 2 – How To Display Cross Sell Products On Product Details Page (PDP)?

The module described here no longer seems to work however as folowing error occurs:

"PHP Fatal error: Uncaught Error: Call to a member function toHtml() on null "

Removing the toHtml function allows my page to load and a list of products is almost rendered however some data for products does not seem to be available.

This is the code i'm currently using for my block:

<?php
namespace Vendor\Module\Block;

class Crosssellproduct extends \Magento\Catalog\Block\Product\AbstractProduct implements \Magento\Framework\DataObject\IdentityInterface {

protected $_registry;
protected $_itemCollection;

public function __construct(
    \Magento\Catalog\Block\Product\Context $context,
    \Magento\Framework\Registry $registry, 
    array $data = []
) {
    $this->_registry = $registry;
    parent::__construct(
        $context,
        $data
    );
}

public function getCurrentProduct() {
    return $this->_registry->registry('current_product');
}

public function getItems() {
    $items = $this->getData('items');
    if ($items === null) {
        $product = $this->getCurrentProduct();
        $items = $product->getCrossSellProducts();
        $this->setData('items', $items);
    }
    return $items;
}

public function getItemCount() {
    return count($this->getItems());
}

public function getIdentities()
{
    $identities = [];
    foreach ($this->getItems() as $item) {
        $identities = array_merge($identities, $item->getIdentities());
    }
    return $identities;
}
}

UPDATE

Using some code from related products i have it a little closer to working with working links and add to cart buttons. I've updated code in question to above. However images show the placeholder image, names are not showing and prices appear as £0.00. So below sections of code do not appear to work as expected:

echo $block->escapeHtml($_item->getName())
echo $block->getProductPrice($_item);
echo $block->getImage($_item, $image)->toHtml();

However these do work:

echo $block->getProductUrl($_item)
$block->getAddToCartUrl($_item)

Does anyone know why these would be failing slightly and what is required to allow these to work?

Best Answer

Ok i've sussed it for if anyone needs to do this in future:

<?php
namespace Vendor\Module\Block;

class Crosssellproduct extends \Magento\Catalog\Block\Product\AbstractProduct implements \Magento\Framework\DataObject\IdentityInterface {
protected $_itemCollection;
public function __construct(
    \Magento\Catalog\Block\Product\Context $context,
    array $data = []
) {
    $this->_registry = $context->getRegistry();
    parent::__construct(
        $context,
        $data
    );
}

protected function _prepareData()
{
    $product = $this->_registry->registry('current_product');
    $this->_itemCollection = $product->getCrossSellProductCollection()->addAttributeToSelect(
        $this->_catalogConfig->getProductAttributes()
    )->setPositionOrder()->addStoreFilter();
    $this->_itemCollection->load();
    foreach ($this->_itemCollection as $product) {
        $product->setDoNotUseCategoryId(true);
    }
    return $this;
}

protected function _beforeToHtml()
{
    $this->_prepareData();
    return parent::_beforeToHtml();
}

public function getItems()
{
    return $this->_itemCollection;
}

public function getItemCount() {
    return count($this->getItems());
}
public function getIdentities()
{
    $identities = [];
    foreach ($this->getItems() as $item) {
        $identities = array_merge($identities, $item->getIdentities());
    }
    return $identities;
}
}

And then this block within your module can then be referenced like so:

         <block class="Vendor\Module\Crosssellproduct" name="product.crosssell" template="Magento_Catalog::product/list/items.phtml" after="-">
            <arguments>
                <argument name="type" xsi:type="string">crosssell</argument>
            </arguments>
            <block class="Magento\Catalog\Block\Product\ProductList\Item\Container" name="crosssell.product.addto" as="addto">
                <block class="Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare"
                       name="crosssell.product.addto.compare" as="compare"
                       template="Magento_Catalog::product/list/addto/compare.phtml"/>
            </block>
        </block> 

I would like to modify it so that it includes standard crosssell plus the crosssell products for the products being viewed however this answers the question.