1

I have upgraded magento from 2.3.7 to 2.4.7. And one of the graphql query gives below error

str_replace(): Argument #3 ($subject) must be of type array|string, float given at /vendor/magento/module-catalog-graph-ql/DataProvider/Product/LayeredNavigation/Builder/Price.php:84)

the issue seems fixed when I changed the mentioned line as below:

Original

foreach ($bucket->getValues() as $value) {
            $metrics = $value->getMetrics();
            $result['options'][] = $this->layerFormatter->buildItem(
                isset($metrics['value']) ? \str_replace('_', '-', $metrics['value']) : '',
                $metrics['value'],
                $metrics['count']
            );
        }

Changed To

foreach ($bucket->getValues() as $value) {
            $metrics = $value->getMetrics();
            $result['options'][] = $this->layerFormatter->buildItem(
                isset($metrics['value']) && (is_string($metrics['value']) || is_array($metrics['value'])) ? \str_replace('_', '-', $metrics['value']) : '',

                $metrics['value'],
                $metrics['count']
            );
        }

But As changing directly to vendor file is not a good practice, How can I correct this.

I am not directly calling this file so I dont see any issue anywhere else.

1 Answer 1

0

You can override this file, but before doing so, please check if this class is used elsewhere in app/code, as it might cause errors or other issues. If overriding is necessary for your work, you can proceed with the following steps:

vendor\module\etc\di.xml

<preference for="Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Builder\Price" type="Vendor\Module\Model\DataProvider\Product\LayeredNavigation\Builder\Price" />

Vendor\Module\Model\DataProvider\Product\LayeredNavigation\Builder\Price.php

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);

namespace Vendor\Module\Model\DataProvider\Product\LayeredNavigation\Builder;

use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\AttributeOptionProvider;
use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\LayerBuilderInterface;
use Magento\Framework\Api\Search\AggregationInterface;
use Magento\Framework\Api\Search\AggregationValueInterface;
use Magento\Framework\Api\Search\BucketInterface;
use Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation\Formatter\LayerFormatter;
use Zend_Db_Statement_Exception;

/**
 * @inheritdoc
 */
class Price implements LayerBuilderInterface
{
    /**
     * @var string
     */
    private const PRICE_BUCKET = 'price_bucket';

    /**
     * @var LayerFormatter
     */
    private $layerFormatter;

    /**
     * @var AttributeOptionProvider
     */
    private $attributeOptionProvider;

    /**
     * @var array
     */
    private static $bucketMap = [
        self::PRICE_BUCKET => [
            'request_name' => 'price',
            'label' => 'Price'
        ],
    ];

    /**
     * @param LayerFormatter $layerFormatter
     * @param AttributeOptionProvider $attributeOptionProvider
     */
    public function __construct(
        LayerFormatter $layerFormatter,
        AttributeOptionProvider $attributeOptionProvider
    ) {
        $this->layerFormatter = $layerFormatter;
        $this->attributeOptionProvider = $attributeOptionProvider;
    }

    /**
     * @inheritdoc
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function build(AggregationInterface $aggregation, ?int $storeId): array
    {
        $attributeOptions = $this->getAttributeOptions($aggregation, $storeId);
        $attributeCode = self::$bucketMap[self::PRICE_BUCKET]['request_name'];
        $attribute = $attributeOptions[$attributeCode] ?? [];
        $bucket = $aggregation->getBucket(self::PRICE_BUCKET);
        if ($this->isBucketEmpty($bucket)) {
            return [];
        }

        $result = $this->layerFormatter->buildLayer(
            $attribute['attribute_label'] ?? self::$bucketMap[self::PRICE_BUCKET]['label'],
            \count($bucket->getValues()),
            self::$bucketMap[self::PRICE_BUCKET]['request_name']
        );

        foreach ($bucket->getValues() as $value) {
            $metrics = $value->getMetrics();
            $result['options'][] = $this->layerFormatter->buildItem(
                isset($metrics['value']) && (is_string($metrics['value']) || is_array($metrics['value'])) ? \str_replace('_', '-', $metrics['value']) : '',
                $metrics['value'],
                $metrics['count']
            );
        }

        return [self::PRICE_BUCKET => $result];
    }

    /**
     * Check that bucket contains data
     *
     * @param BucketInterface|null $bucket
     * @return bool
     */
    private function isBucketEmpty(?BucketInterface $bucket): bool
    {
        return null === $bucket || !$bucket->getValues();
    }

    /**
     * Get list of attributes with options
     *
     * @param AggregationInterface $aggregation
     * @param int|null $storeId
     * @return array
     * @throws Zend_Db_Statement_Exception
     */
    private function getAttributeOptions(AggregationInterface $aggregation, ?int $storeId): array
    {
        $attributeOptionIds = [];
        $attributes = [];

        $bucket = $aggregation->getBucket(self::PRICE_BUCKET);

        if ($this->isBucketEmpty($bucket)) {
            return [];
        }

        $attributes[] = \preg_replace('~_bucket$~', '', $bucket->getName());
        $attributeOptionIds[] = \array_map(
            function (AggregationValueInterface $value) {
                return $value->getValue();
            },
            $bucket->getValues()
        );

        return $this->attributeOptionProvider->getOptions(
            \array_merge([], ...$attributeOptionIds),
            $storeId,
            $attributes
        );
    }
}

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.