I'm trying to extend a Block in Magento, the Magento\Catalog\Block\Product\ListProduct
block and Inject a dependency on it, but I've hit a wall and just get this error.
Fatal error: Uncaught TypeError: Argument 2 passed to Holy\Demo\Block\Product\ListProduct::__construct() must implement interface Magento\CatalogInventory\Api\StockStateInterface, instance of Magento\Framework\Data\Helper\PostHelper given, called in /var/www/html/generated/code/Holy/Demo/Block/Product/ListProduct/Interceptor.php on line 14 and defined in /var/www/html/app/code/Holy/Demo/Block/Product/ListProduct.php:14 Stack trace: #0 /var/www/html/generated/code/Holy/Demo/Block/Product/ListProduct/Interceptor.php(14): Holy\Demo\Block\Product\ListProduct->__construct(Object(Magento\Catalog\Block\Product\Context), Object(Magento\Framework\Data\Helper\PostHelper), Object(Magento\Catalog\Model\Layer\Resolver), Object(Magento\Catalog\Model\CategoryRepository\Interceptor), Object(Magento\Framework\Url\Helper\Data), Array) #1 /var/www/html/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php(111): Holy\Demo\Block\Product\ListProduct\Inte in /var/www/html/app/code/Holy/Demo/Block/Product/ListProduct.php on line 14
This is the di.xml of my module:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Block\Product\ListProduct" type="Holy\Demo\Block\Product\ListProduct" />
</config>
And this is my Block class:
<?php
namespace Holy\Demo\Block\Product;
/**
* Product list
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ListProduct extends \Magento\Catalog\Block\Product\ListProduct
{
/**
* @param \Magento\CatalogInventory\Api\StockStateInterface $stockState
*/
public function __construct(
\Magento\Catalog\Block\Product\Context $context,
// \Magento\Backend\Block\Template\Context $context,
\Magento\CatalogInventory\Api\StockStateInterface $stockState,
array $data = []
)
{
$this->stockState = $stockState;
parent::__construct($context, $data);
}
public function getStockQty($product) {
// TODO: Don't use objectManager, Inject the \Magento\CatalogInventory\Api\StockStateInterface on the constructor of the block
// $_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
// $_stockState = $_objectManager->get('\Magento\CatalogInventory\Api\StockStateInterface');
// return $_stockState->getStockQty($product->getId(), $product->getStore()->getWebsiteId());
return $this->stockState->getStockQty($product->getId(), $product->getStore()->getWebsiteId());
}
}
UPDATE
I was able to get it working by adding all of the parent class arguments from it's constructor to my constructor, but this does not seem like a good approach to me as if in a Magento update a new dependency is added to the parent class, won't that break my module?
public function __construct(
\Magento\Catalog\Block\Product\Context $context,
\Magento\Framework\Data\Helper\PostHelper $postDataHelper,
\Magento\Catalog\Model\Layer\Resolver $layerResolver,
\Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository,
\Magento\Framework\Url\Helper\Data $urlHelper,
\Magento\CatalogInventory\Api\StockStateInterface $stockState,
array $data = []
)
{
$this->stockState = $stockState;
parent::__construct($context, $postDataHelper , $layerResolver, $categoryRepository, $urlHelper, $data);
}
Best Answer
Your child arguments constructor should inherit from parent class:
Remember to remove
var/generation
and clear cache also.[EDIT]
For this answer: Magento 2 StoreManagerInterface already exists in context object in compilation. We don't need to inject
\Magento\Store\Model\StoreManagerInterface
in our child class, but we inject\Magento\Framework\View\Element\Context
in the construct. This class has\Magento\Store\Model\StoreManagerInterface
in its constructor already.