1

I've created a module that modifies list.phtml to eventually place some data under the price in product category listing. I'm using Magento 2.4.1.

Module can be downloaded here: https://www.dropbox.com/s/g24ut8cwp11esle/payment-plan.zip?dl=0

The module loads, but when it does I am getting the following error when trying to view category pages:

1 exception(s):
Exception #0 (ReflectionException): Class Magento\Catalog\ViewModel\Product\OptionsData does not exist

Exception #0 (ReflectionException): Class Magento\Catalog\ViewModel\Product\OptionsData does not exist
<pre>#1 Magento\Framework\Code\Reader\ClassReader->getConstructor() called at [vendor/magento/framework/ObjectManager/Definition/Runtime.php:54]
#2 Magento\Framework\ObjectManager\Definition\Runtime->getParameters() called at [vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php:48]
#3 Magento\Framework\ObjectManager\Factory\Dynamic\Developer->create() called at [vendor/magento/framework/ObjectManager/ObjectManager.php:70]
#4 Magento\Framework\ObjectManager\ObjectManager->get() called at [vendor/magento/framework/View/Layout/Argument/Interpreter/DataObject.php:61]
#5 Magento\Framework\View\Layout\Argument\Interpreter\DataObject->evaluate() called at [vendor/magento/framework/Data/Argument/Interpreter/Composite.php:61]
#6 Magento\Framework\Data\Argument\Interpreter\Composite->evaluate() called at [vendor/magento/framework/View/Layout/Argument/Interpreter/Decorator/Updater.php:47]
#7 Magento\Framework\View\Layout\Argument\Interpreter\Decorator\Updater->evaluate() called at [vendor/magento/framework/View/Layout/Generator/Block.php:320]
#8 Magento\Framework\View\Layout\Generator\Block->evaluateArguments() called at [vendor/magento/framework/View/Layout/Generator/Block.php:229]
#9 Magento\Framework\View\Layout\Generator\Block->generateBlock() called at [vendor/magento/framework/View/Layout/Generator/Block.php:134]
#10 Magento\Framework\View\Layout\Generator\Block->process() called at [vendor/magento/framework/View/Layout/GeneratorPool.php:93]
#11 Magento\Framework\View\Layout\GeneratorPool->process() called at [vendor/magento/framework/View/Layout.php:365]
#12 Magento\Framework\View\Layout->generateElements() called at [vendor/magento/framework/Interception/Interceptor.php:58]
#13 Magento\Framework\View\Layout\Interceptor->___callParent() called at [vendor/magento/framework/Interception/Interceptor.php:138]
#14 Magento\Framework\View\Layout\Interceptor->Magento\Framework\Interception\{closure}() called at [vendor/magento/framework/Interception/Interceptor.php:153]
#15 Magento\Framework\View\Layout\Interceptor->___callPlugins() called at [generated/code/Magento/Framework/View/Layout/Interceptor.php:68]
#16 Magento\Framework\View\Layout\Interceptor->generateElements() called at [vendor/magento/framework/View/Layout/Builder.php:129]
#17 Magento\Framework\View\Layout\Builder->generateLayoutBlocks() called at [vendor/magento/framework/View/Page/Builder.php:65]
#18 Magento\Framework\View\Page\Builder->generateLayoutBlocks() called at [vendor/magento/framework/View/Layout/Builder.php:65]
#19 Magento\Framework\View\Layout\Builder->build() called at [vendor/magento/framework/View/Page/Config.php:224]
#20 Magento\Framework\View\Page\Config->build() called at [vendor/magento/framework/View/Page/Config.php:587]
#21 Magento\Framework\View\Page\Config->getElementAttribute() called at [vendor/magento/framework/View/Page/Config.php:545]
#22 Magento\Framework\View\Page\Config->addBodyClass() called at [vendor/magento/module-catalog/Controller/Category/View.php:249]
#23 Magento\Catalog\Controller\Category\View->execute() called at [vendor/magento/framework/Interception/Interceptor.php:58]
#24 Magento\Catalog\Controller\Category\View\Interceptor->___callParent() called at [vendor/magento/framework/Interception/Interceptor.php:138]
#25 Magento\Catalog\Controller\Category\View\Interceptor->Magento\Framework\Interception\{closure}() called at [vendor/magento/framework/App/Action/Plugin/ActionFlagNoDispatchPlugin.php:51]
#26 Magento\Framework\App\Action\Plugin\ActionFlagNoDispatchPlugin->aroundExecute() called at [vendor/magento/framework/Interception/Interceptor.php:135]
#27 Magento\Catalog\Controller\Category\View\Interceptor->Magento\Framework\Interception\{closure}() called at [vendor/magento/framework/Interception/Interceptor.php:153]
#28 Magento\Catalog\Controller\Category\View\Interceptor->___callPlugins() called at [generated/code/Magento/Catalog/Controller/Category/View/Interceptor.php:23]
#29 Magento\Catalog\Controller\Category\View\Interceptor->execute() called at [vendor/magento/framework/App/Action/Action.php:111]
#30 Magento\Framework\App\Action\Action->dispatch() called at [vendor/magento/framework/Interception/Interceptor.php:58]
#31 Magento\Catalog\Controller\Category\View\Interceptor->___callParent() called at [vendor/magento/framework/Interception/Interceptor.php:138]
#32 Magento\Catalog\Controller\Category\View\Interceptor->Magento\Framework\Interception\{closure}() called at [vendor/magento/framework/Interception/Interceptor.php:153]
#33 Magento\Catalog\Controller\Category\View\Interceptor->___callPlugins() called at [generated/code/Magento/Catalog/Controller/Category/View/Interceptor.php:32]
#34 Magento\Catalog\Controller\Category\View\Interceptor->dispatch() called at [vendor/magento/framework/App/FrontController.php:186]
#35 Magento\Framework\App\FrontController->processRequest() called at [vendor/magento/framework/App/FrontController.php:118]
#36 Magento\Framework\App\FrontController->dispatch() called at [vendor/magento/framework/Interception/Interceptor.php:58]
#37 Magento\Framework\App\FrontController\Interceptor->___callParent() called at [vendor/magento/framework/Interception/Interceptor.php:138]
#38 Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}() called at [vendor/magento/module-store/App/FrontController/Plugin/RequestPreprocessor.php:99]
#39 Magento\Store\App\FrontController\Plugin\RequestPreprocessor->aroundDispatch() called at [vendor/magento/framework/Interception/Interceptor.php:135]
#40 Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}() called at [vendor/magento/module-page-cache/Model/App/FrontController/BuiltinPlugin.php:75]
#41 Magento\PageCache\Model\App\FrontController\BuiltinPlugin->aroundDispatch() called at [vendor/magento/framework/Interception/Interceptor.php:135]
#42 Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}() called at [vendor/magento/framework/Interception/Interceptor.php:153]
#43 Magento\Framework\App\FrontController\Interceptor->___callPlugins() called at [generated/code/Magento/Framework/App/FrontController/Interceptor.php:23]
#44 Magento\Framework\App\FrontController\Interceptor->dispatch() called at [vendor/magento/framework/App/Http.php:116]
#45 Magento\Framework\App\Http->launch() called at [generated/code/Magento/Framework/App/Http/Interceptor.php:23]
#46 Magento\Framework\App\Http\Interceptor->launch() called at [vendor/magento/framework/App/Bootstrap.php:263]
#47 Magento\Framework\App\Bootstrap->run() called at [pub/index.php:29]
</pre>

app/code/jr2/payment-plan/etc/module.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="JR2_PaymentPlan"/>
</config>

app/code/jr2/payment-plan/view/frontend/layout/catalog_category_view.xml (this data is copied as is from Magento/Catalog/view/frontend/layout/catalog_category_view.xml:

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="columns.top">
            <container name="category.view.container" htmlTag="div" htmlClass="category-view" after="-">
                <block class="Magento\Catalog\Block\Category\View" name="category.image" template="Magento_Catalog::category/image.phtml">
                    <arguments>
                        <argument name="image" xsi:type="object">Magento\Catalog\ViewModel\Category\Image</argument>
                        <argument name="output" xsi:type="object">Magento\Catalog\ViewModel\Category\Output</argument>
                    </arguments>
                </block>
                <block class="Magento\Catalog\Block\Category\View" name="category.description" template="Magento_Catalog::category/description.phtml"/>
                <block class="Magento\Catalog\Block\Category\View" name="category.cms" template="Magento_Catalog::category/cms.phtml"/>
            </container>
        </referenceContainer>
        <referenceContainer name="content">
            <block class="Magento\Catalog\Block\Category\View" name="category.products" template="Magento_Catalog::category/products.phtml">
                <block class="Magento\Catalog\Block\Product\ListProduct" name="category.products.list" as="product_list" template="Magento_Catalog::product/list.phtml">
                    <container name="category.product.list.additional" as="additional" />
                    <block class="Magento\Framework\View\Element\RendererList" name="category.product.type.details.renderers" as="details.renderers">
                        <block class="Magento\Framework\View\Element\Template" name="category.product.type.details.renderers.default" as="default"/>
                    </block>
                    <block class="Magento\Catalog\Block\Product\ProductList\Item\Container" name="category.product.addto" as="addto">
                        <block class="Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare"
                               name="category.product.addto.compare" as="compare"
                               template="Magento_Catalog::product/list/addto/compare.phtml"/>
                    </block>
                    <block class="Magento\Catalog\Block\Product\ProductList\Toolbar" name="product_list_toolbar" template="Magento_Catalog::product/list/toolbar.phtml">
                        <block class="Magento\Theme\Block\Html\Pager" name="product_list_toolbar_pager"/>
                    </block>
                    <action method="setToolbarBlockName">
                        <argument name="name" xsi:type="string">product_list_toolbar</argument>
                    </action>
                </block>
            </block>
            <block class="Magento\Cookie\Block\RequireCookie" name="require-cookie" template="Magento_Cookie::require_cookie.phtml">
                <arguments>
                    <argument name="triggers" xsi:type="array">
                        <item name="compareProductLink" xsi:type="string">.action.tocompare</item>
                    </argument>
                </arguments>
            </block>
        </referenceContainer>
        <referenceBlock name="page.main.title">
            <arguments>
                <argument name="id" xsi:type="string">page-title-heading</argument>
                <argument name="add_base_attribute_aria" xsi:type="string">page-title-heading toolbar-amount</argument>
            </arguments>
            <block class="Magento\Catalog\Block\Category\Rss\Link" name="rss.link" template="Magento_Catalog::category/rss.phtml"/>
        </referenceBlock>
        <referenceBlock name="category.products.list">
            <arguments>
                <argument name="viewModel" xsi:type="object">Magento\Catalog\ViewModel\Product\OptionsData</argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

And finally app/code/jr2/payment-plan/view/templates/product/list.phtml (again, this is copied as is from Magento/Catalog/view/frontend/templates/product/list.phtml:

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
use Magento\Framework\App\Action\Action;

?>
<?php
/**
 * Product list template
 *
 * @var $block \Magento\Catalog\Block\Product\ListProduct
 * @var \Magento\Framework\Escaper $escaper
 * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
 */
?>
<?php
$_productCollection = $block->getLoadedProductCollection();
/** @var \Magento\Catalog\Helper\Output $_helper */
$_helper = $block->getData('outputHelper');
?>
<?php if (!$_productCollection->count()): ?>
    <div class="message info empty">
        <div><?= $escaper->escapeHtml(__('We can\'t find products matching the selection.')) ?></div>
    </div>
<?php else: ?>
    <?= $block->getToolbarHtml() ?>
    <?= $block->getAdditionalHtml() ?>
    <?php
    if ($block->getMode() === 'grid') {
        $viewMode = 'grid';
        $imageDisplayArea = 'category_page_grid';
        $showDescription = false;
        $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::SHORT_VIEW;
    } else {
        $viewMode = 'list';
        $imageDisplayArea = 'category_page_list';
        $showDescription = true;
        $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::FULL_VIEW;
    }
    /**
     * Position for actions regarding image size changing in vde if needed
     */
    $pos = $block->getPositioned();
    ?>
    <div class="products wrapper <?= /* @noEscape */ $viewMode ?> products-<?= /* @noEscape */ $viewMode ?>">
        <ol class="products list items product-items">
            <?php /** @var $_product \Magento\Catalog\Model\Product */ ?>
            <?php foreach ($_productCollection as $_product): ?>
                <li class="item product product-item">
                    <div class="product-item-info"
                         id="product-item-info_<?= /* @noEscape */ $_product->getId() ?>"
                         data-container="product-<?= /* @noEscape */ $viewMode ?>">
                        <?php
                        $productImage = $block->getImage($_product, $imageDisplayArea);
                        if ($pos != null) {
                            $position = 'left:' . $productImage->getWidth() . 'px;'
                                . 'top:' . $productImage->getHeight() . 'px;';
                        }
                        ?>
                        <?php // Product Image ?>
                        <a href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>"
                           class="product photo product-item-photo"
                           tabindex="-1">
                            <?= $productImage->toHtml() ?>
                        </a>
                        <div class="product details product-item-details">
                            <?php $_productNameStripped = $block->stripTags($_product->getName(), null, true); ?>
                            <strong class="product name product-item-name">
                                <a class="product-item-link"
                                   href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>">
                                    <?=/* @noEscape */ $_helper->productAttribute($_product, $_product->getName(), 'name')?>
                                </a>
                            </strong>
                            <?= $block->getReviewsSummaryHtml($_product, $templateType) ?>
                            <?= /* @noEscape */ $block->getProductPrice($_product) ?>

                            <?= $block->getProductDetailsHtml($_product) ?>

                            <div class="product-item-inner">
                                <div class="product actions product-item-actions">
                                    <div class="actions-primary">
                                        <?php if ($_product->isSaleable()):?>
                                            <?php $postParams = $block->getAddToCartPostParams($_product); ?>
                                            <form data-role="tocart-form"
                                                  data-product-sku="<?= $escaper->escapeHtml($_product->getSku()) ?>"
                                                  action="<?= $escaper->escapeUrl($postParams['action']) ?>"
                                                  method="post">
                                                <?php $optionsData = $block->getData('viewModel')->getOptionsData($_product); ?>
                                                <?php foreach ($optionsData as $optionItem): ?>
                                                    <input type="hidden"
                                                           name="<?= $escaper->escapeHtml($optionItem['name']) ?>"
                                                           value="<?= $escaper->escapeHtml($optionItem['value']) ?>">
                                                <?php endforeach; ?>
                                                <input type="hidden"
                                                       name="product"
                                                       value="<?= /* @noEscape */ $postParams['data']['product'] ?>">
                                                <input type="hidden"
                                                       name="<?= /* @noEscape */ Action::PARAM_NAME_URL_ENCODED ?>"
                                                       value="<?=
                                                       /* @noEscape */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED]
                                                       ?>">
                                                <?= $block->getBlockHtml('formkey') ?>
                                                <button type="submit"
                                                        title="<?= $escaper->escapeHtmlAttr(__('Add to Cart')) ?>"
                                                        class="action tocart primary"
                                                        disabled>
                                                    <span><?= $escaper->escapeHtml(__('Add to Cart')) ?></span>
                                                </button>
                                            </form>
                                        <?php else:?>
                                            <?php if ($_product->isAvailable()):?>
                                                <div class="stock available">
                                                    <span><?= $escaper->escapeHtml(__('In stock')) ?></span></div>
                                            <?php else:?>
                                                <div class="stock unavailable">
                                                    <span><?= $escaper->escapeHtml(__('Out of stock')) ?></span></div>
                                            <?php endif; ?>
                                        <?php endif; ?>
                                    </div>
                                    <?= strpos($pos, $viewMode . '-primary') ?
                                        /* @noEscape */ $secureRenderer->renderStyleAsTag(
                                            $position,
                                            'product-item-info_' . $_product->getId() . ' div.actions-primary'
                                        ) : '' ?>
                                    <div data-role="add-to-links" class="actions-secondary">
                                        <?php if ($addToBlock = $block->getChildBlock('addto')): ?>
                                            <?= $addToBlock->setProduct($_product)->getChildHtml() ?>
                                        <?php endif; ?>
                                    </div>
                                    <?= strpos($pos, $viewMode . '-secondary') ?
                                        /* @noEscape */ $secureRenderer->renderStyleAsTag(
                                            $position,
                                            'product-item-info_' . $_product->getId() . ' div.actions-secondary'
                                        ) : '' ?>
                                </div>
                                <?php if ($showDescription): ?>
                                    <div class="product description product-item-description">
                                        <?= /* @noEscape */ $_helper->productAttribute(
                                            $_product,
                                            $_product->getShortDescription(),
                                            'short_description'
                                        ) ?>
                                        <a href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>"
                                           title="<?= /* @noEscape */ $_productNameStripped ?>"
                                           class="action more"><?= $escaper->escapeHtml(__('Learn More')) ?></a>
                                    </div>
                                <?php endif; ?>
                            </div>
                        </div>
                    </div>
                    <?= strpos($pos, $viewMode . '-actions') ?
                        /* @noEscape */ $secureRenderer->renderStyleAsTag(
                            $position,
                            'product-item-info_' . $_product->getId() . ' div.product-item-actions'
                        ) : '' ?>
                </li>
            <?php endforeach; ?>
        </ol>
    </div>
    <?= $block->getChildBlock('toolbar')->setIsBottom(true)->toHtml() ?>
    <script type="text/x-magento-init">
    {
        "[data-role=tocart-form], .form.map.checkout": {
            "catalogAddToCart": {
                "product_sku": "<?= $escaper->escapeJs($_product->getSku()) ?>"
            }
        }
    }
    </script>
<?php endif; ?>

I've tried:

  • Overriding list.html in a them without overriding catalog_category_view.xml and just overriding the list.phtml. This causes the same problem.

Can anybody help me figure out what is causing this?

Edit: Following the suggestion in the comment with a link to another question I replace the contents of catalog_category_view.xml with the following:

<?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" template="JR2_PaymentPlan::product/list.phtml"/>
    </body>
</page>

This is what I see on the frontend:

enter image description here

3
  • @RahulBarot already tried this. I still get the same error as above. May 26, 2021 at 12:20
  • If you notice in the question I've put the path to where the original list.html is. It's a core in Magento module: Magento/Catalog/view/frontend/templates/product/list.phtml. I've also edited the question to show what I see when I add <referenceBlock name="category.products.list" template="JR2_PaymentPlan::product/list.phtml"/>. May 26, 2021 at 12:42
  • @RahulBarot as I've already said in the question I have attempted to do the same in my theme and I get exactly the same error. Also, the custom module is needed because I need to implement more features for this module once I get this working. I've put the module on dropbox, link in question. May 26, 2021 at 12:49

1 Answer 1

1

this should work

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="category.products.list" template="Vendor_Modulename::product/list.phtml" />
    </body>
</page>

please revert all other code for override list.phtml

I dont know why you added below code. I can't able to find in core "catalog_category_view.xml" file. please remove this code and also clear cache after made changes in xml file

 <referenceBlock name="category.products.list">
            <arguments>
                <argument name="viewModel" xsi:type="object">Magento\Catalog\ViewModel\Product\OptionsData</argument>
            </arguments>
        </referenceBlock>
1
  • This does solve this error and I had already done this, but the second error in my image is related. Because in both cases it's the product options model that can't be found. The effected line in the error is `<?php $optionsData = $block->getData('viewModel')->getOptionsData($_product); ?>. I will accept as it solves the error I questioned. May 26, 2021 at 13:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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