0

So I'm trying to add some related products and up-sell products from admin, but as soon as I add one the page gets broken.

In the page I see a full html template with some scripts and this error in the end:

Error: Call to a member function getAlternativeImages() on null in /var/www/html/app/design/frontend/Vendor/theme/Magento_Catalog/templates/product/list/item.phtml:58

So my item.phtml is this:

<?php
/**
 * Hyvä Themes - https://hyva.io
 * Copyright © Hyvä Themes 2020-present. All rights reserved.
 * This product is licensed per Magento install
 * See https://hyva.io/license
 */

declare(strict_types=1);

use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\HeroiconsOutline;
use Hyva\Theme\ViewModel\HeroiconsSolid;
use Hyva\Theme\ViewModel\ProductCompare;
use Hyva\Theme\ViewModel\ProductListItem;
use Hyva\Theme\ViewModel\ProductPage;
use Hyva\Theme\ViewModel\Wishlist;
use Magento\Catalog\Block\Product\AbstractProduct;
use Magento\Catalog\Helper\Output as CatalogOutputHelper;
use Magento\Framework\Escaper;

// phpcs:disable Magento2.Templates.ThisInTemplate
// phpcs:disable Generic.Files.LineLength.TooLong

/** @var AbstractProduct $block */
/** @var Escaper $escaper */
/** @var ViewModelRegistry $viewModels */

/** @var ProductPage $productViewModel */
$productViewModel = $viewModels->require(ProductPage::class);

/** @var ProductListItem $productListItemViewModel */
$productListItemViewModel = $viewModels->require(ProductListItem::class);

/** @var CatalogOutputHelper $catalogOutputHelper */
$catalogOutputHelper = $this->helper(CatalogOutputHelper::class);

/** @var ProductCompare $compareViewModel */
$compareViewModel = $viewModels->require(ProductCompare::class);
/** @var Wishlist $wishlistViewModel */
$wishlistViewModel = $viewModels->require(Wishlist::class);

/** @var HeroiconsOutline $heroiconsOutline */
$heroiconsOutline = $viewModels->require(HeroiconsOutline::class);
/** @var HeroiconsSolid $heroiconsSolid */
$heroiconsSolid = $viewModels->require(HeroiconsSolid::class);

/** @var Magento\Catalog\Model\Product $product */
$product = $block->getData('product');
$imageDisplayArea = $block->getData('image_display_area');
$templateType = $block->getData('template_type');
$viewMode = $block->getData('view_mode');
$showDescription = $block->getData('show_description');
$showAddToWishlist = $wishlistViewModel->isEnabled();
$showAddToCompare = $compareViewModel->showInProductList();
$viewIsGrid = $viewMode === 'grid';
$_backImageHelper = $block->getData('image_alternative_product_list');
if($_backImageHelper->getAlternativeImages($product)) {
    $productAlternativeImages = $_backImageHelper->getAlternativeImages($product);
} else {
    $productAlternativeImages = [];
}

if (!$product) {
    return '';
}
$productId = $product->getId();
$uniqueId = '_' . uniqid();

?>

<?php if ($product->isSaleable()): ?>
<form method="post"
    action="<?= $escaper->escapeUrl($productViewModel->getAddToCartUrl($product)) ?>"
    class="item product product-item product_addtocart_form flex  flex-col mb-10 lg:mb-24 relative <?= $viewIsGrid ? '' : '' ?>"
      x-data="{ active: 0 }"
    <?php if ($product->getOptions()): ?>
    enctype="multipart/form-data"
    <?php endif; ?>
>
    <?= /** @noEscape */ $block->getBlockHtml('formkey') ?>
    <input type="hidden" name="product" value="<?= (int)$productId ?>"/>
<?php else: ?>
<div class="item product product-item flex flex-col  relative <?= $viewIsGrid ? '' : '' ?>">
<?php endif; ?>
    <?php
    $productImage = $block->getImage($product, $imageDisplayArea);
    ?>
    <?php // Product Image ?>
    <div class="flex items-center gap-1 z-40">
        <span class="cursor-pointer h-1.5 w-1/3"
              @click.prevent="active = 0"
              :class="{'bg-secondary' : active === 0, 'bg-container-darker' : active !== 0}">
        </span>
        <?php foreach($productAlternativeImages as $key => $alternativeImg): ?>
            <span :class="{'bg-secondary' : active === <?= $key+1 ?>, 'bg-container-darker' : active !== <?= $key+1 ?>}"
                  class="cursor-pointer bg-primary h-1.5 w-1/3"
                  @click.prevent="active = <?= $key+1 ?>"></span>
        <?php endforeach; ?>
    </div>

    <button class="transform rotate-180 w-10 h-10 absolute right-3 top-3 z-40"
            @click.prevent="active >= 0 && active < <?= count($productAlternativeImages) ?> ? active = active+1 : active = 0" >
        <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M16 8.94434L2 8.94433" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
            <path d="M9 15.8887L2 8.94433L9 2" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
        </svg>
    </button>


    <a href="<?= $escaper->escapeUrl($product->getProductUrl()) ?>"

        class="product pt-full relative photo product-item-photo block mb-3 <?= $viewIsGrid ? '' : 'md:mb-0' ?>"
        tabindex="-1">

        <img class="hover:shadow-sm object-contain"
             x-show="active === 0"
             loading="lazy"
             @update-gallery-<?= (int)$productId ?>.window="$el.src = event.detail"
             alt="<?= $escaper->escapeHtmlAttr($productImage->getLabel()) ?>"
             title="<?= $escaper->escapeHtmlAttr($productImage->getLabel()) ?>"
             src="<?= $escaper->escapeUrl($productImage->getImageUrl()) ?>"
             width="100%"
             height="<?= $escaper->escapeHtmlAttr($productImage->getHeight()) ?>"
        />

        <?php foreach($productAlternativeImages as $key => $alternativeImg): ?>
            <img class="hover:shadow-sm object-contain aspect-square"
                 x-show = "active === <?= $key+1 ?> "
                 src="<?= $alternativeImg->getData('image_url') ?>"
                 width="<?= $escaper->escapeHtmlAttr($productImage->getWidth()) ?>"
                 height="<?= $escaper->escapeHtmlAttr($productImage->getHeight()) ?>"
                 alt="<?= $escaper->escapeHtmlAttr($productImage->getLabel()) ?>"
            />
        <?php endforeach; ?>
    </a>
    <div class="product-info flex flex-col flex-grow">
        <?php $productNameStripped = $block->stripTags($product->getName(), null, true); ?>
        <div class="mt-2 items-center justify-center text-primary text-lg lg:text-3xl  <?= $viewIsGrid ? '' : 'md:text-left' ?>">
            <a class="product-item-link"
               href="<?= $escaper->escapeUrl($product->getProductUrl()) ?>">
                <?= /* @noEscape */
                $catalogOutputHelper->productAttribute($product, $product->getName(), 'name') ?>
            </a>
        </div>

        <div class="py-1 mx-auto hidden w-6/12 <?= $viewIsGrid ? '' : 'md:mx-0 md:w-auto' ?>">
            <?= $block->getReviewsSummaryHtml($product, 'short') ?>
        </div>

        <?php if ($product->isAvailable()):?>
            <?= $block->getProductDetailsHtml($product) ?>
        <?php endif; ?>

        <script>
            function initPriceBox_<?= /** @noEscape */ $uniqueId ?>()
            {
                return {
                    updatePrice(priceData) {
                        const regularPriceLabel = this.$el.querySelector('.normal-price .price-label');
                        const regularPriceElement = this.$el.querySelector('.normal-price .price-wrapper .price');
                        if (priceData.finalPrice.amount < priceData.oldPrice.amount) {
                            regularPriceLabel.classList.add('hidden');
                        } else {
                            regularPriceLabel.classList.remove('hidden');
                        }
                        regularPriceElement.innerText = hyva.formatPrice(priceData.finalPrice.amount);
                    }
                }
            }
        </script>
        <div class="lg:pt-1 text-xs lg:text-2xl"
             x-data="initPriceBox_<?= /** @noEscape */ $uniqueId ?>()"
             @update-prices-<?= (int)$productId ?>.window="updatePrice(event.detail);"
        >
            <?= /* @noEscape */ $productListItemViewModel->getProductPriceHtml($product) ?>
        </div>

        <div class=" mt-auto pt-3 flex flex-wrap justify-center items-center">
            <?php if ($product->isSaleable()): ?>
            <button class="hidden w-auto btn btn-primary justify-center text-sm mr-auto
                        <?= $viewIsGrid ? 'mr-auto' : 'mr-auto md:mr-0' ?>"
                    aria-label="<?= $escaper->escapeHtmlAttr(__('Add to Cart')) ?>"
            >
                <?= $heroiconsOutline->shoppingCartHtml("h-6 w-6 border-current inline", 25, 25) ?>
                <span class="ml-2 inline <?= $viewIsGrid ? 'md:ml-0 md:hidden lg:ml-2 lg:inline' : '' ?>">
                    <?= $escaper->escapeHtml(__('Add to Cart')) ?>
                </span>
            </button>
            <?php endif; ?>
            <?php if ($showAddToWishlist): ?>
            <script>
                function initWishlist() {
                    return {
                        addToWishlist(productId) {
                            var formKey = document.querySelector('input[name=form_key]').value;
                            var postUrl = BASE_URL+"wishlist/index/add/";
                            fetch(postUrl, {
                                "headers": {
                                    "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
                                },
                                "body": "form_key="+ formKey + "&product="+productId+"&uenc="+btoa(window.location.href),
                                "method": "POST",
                                "mode": "cors",
                                "credentials": "include"
                            }).then(function (response) {
                                if (response.redirected) {
                                    window.location.href = response.url;
                                } else if (response.ok) {
                                    return response.json();
                                } else {
                                    typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
                                        [{
                                            type: "warning",
                                            text: "<?= $escaper->escapeHtml(__('Could not add item to wishlist.')) ?>"
                                        }], 5000
                                    );
                                }
                            }).then(function (response) {
                                if(!response) { return }
                                typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
                                    [{
                                        type: (response.success) ? "success" : "error",
                                        text: (response.success)
                                            ? "<?= $escaper->escapeHtml(__("%1 has been added to your Wish List.", __("Product"))) ?>"
                                            : response.error_message
                                    }], 5000
                                );
                                var reloadCustomerDataEvent = new CustomEvent("reload-customer-section-data");
                                window.dispatchEvent(reloadCustomerDataEvent);
                            }).catch(function (error) {
                                typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
                                    [{
                                        type: "error",
                                        text: error
                                    }], 5000
                                );
                            });
                        }
                    }
                }
            </script>
            <button x-data="initWishlist()"
                    @click.prevent="addToWishlist(<?= (int)$productId ?>)"
                    aria-label="<?= $escaper->escapeHtmlAttr(__('Add to Wish List')) ?>"
                    type="button"
                    class="absolute left-3 top-3 z-20 w-9 h-9 p-0 border-0 inline-flex flex-shrink-0 items-center justify-center text-grayLight hover:text-red-600">
                <svg class="w-5 h-5 lg:w-7 lg:h-7" width="29" height="26" viewBox="0 0 29 26" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                    <path d="M21.4955 0C15.8084 0 14.4976 5.56033 14.4976 5.56033C14.4976 5.56033 13.185 0 7.49795 0C1.81086 0 -1.34769 6.06631 0.554216 10.7863C2.45612 15.5064 6.59019 23.0075 14.501 26C22.4117 23.0075 26.5543 15.5046 28.4494 10.7863C30.3445 6.06811 27.1826 0 21.4955 0Z"
                          fill=""/>
                </svg>

            </button>
            <?php endif; ?>
            <?php if ($showAddToCompare): ?>
            <script>
            function initCompare() {
                return {
                    addToCompare(productId) {
                        const formKey = document.querySelector('input[name=form_key]').value;
                        const postUrl = BASE_URL + 'catalog/product_compare/add/';

                        fetch(postUrl, {
                            "headers": {
                                "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
                            },
                            "body": "form_key="+ formKey + "&product="+productId+"&uenc="+btoa(window.location.href),
                            "method": "POST",
                            "mode": "cors",
                            "credentials": "include"
                        }).then(function (response) {
                            if (response.redirected) {
                                window.location.href = response.url;
                            }
                        }).catch(function (error) {
                            typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
                                [{
                                    type: "error",
                                    text: error
                                }], 5000
                            );
                        });
                    }
                };
            }
            </script>
            <button x-data="initCompare()"
                    @click.prevent="addToCompare(<?= (int)$productId ?>)"
                    aria-label="<?= $escaper->escapeHtmlAttr(__('Add to Compare')) ?>"
                    type="button"
                    class="rounded-full w-9 h-9 bg-gray-200 p-0 border-0 inline-flex flex-shrink-0 items-center justify-center text-gray-500 hover:text-yellow-500 ml-2">
                <?= $heroiconsOutline->scaleHtml("w-5 h-5", 25, 25) ?>
            </button>
            <?php endif; ?>
        </div>
    </div>
<?php if ($product->isSaleable()): ?>
</form>
<?php else: ?>
</div>
<?php endif; ?>

and that function getAlternativeImages(), in item.phtml is used here:

if($_backImageHelper->getAlternativeImages($product)) {
    $productAlternativeImages = $_backImageHelper->getAlternativeImages($product);
} else {
    $productAlternativeImages = [];
}

I'm not understanding why this is happening, it seems that $product is null, but still I can see the product page correctly except for when adding related products or up-sell ones.

If you guys could help it would be awesome, save my day for sure.

6
  • $_backImageHelper = $block->getData('image_alternative_product_list')... seem to be return null Commented May 24, 2022 at 11:40
  • Yes it seems to be
    – D.D.
    Commented May 24, 2022 at 12:37
  • You first need to figure out where 'image_alternative_product_list' get set. A quick workaround would be to change if($_backImageHelper->getAlternativeImages($product)) to if($_backImageHelper && $_backImageHelper->getAlternativeImages($product)) Commented May 24, 2022 at 12:56
  • You may want to consider asking this question in the Hyvä slack channel Commented May 24, 2022 at 12:58
  • Adding this further workaround seemed to work! Now I can finally see related products! Thanks man!
    – D.D.
    Commented May 24, 2022 at 13:32

1 Answer 1

0

You need to figure out where 'image_alternative_product_list' get set.

A quick workaround would be to change

if($_backImageHelper->getAlternativeImages($product)) {
    ....

to

if($_backImageHelper && $_backImageHelper->getAlternativeImages($product)) { 
    ....

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.