0

I have looked everywhere for any reviews extension that supported grouped products and could not find any. So trying to hash it out myself.

I need the reviews block on a grouped product to also show reviews for the simple products that are on the page.

The collections classes Magento\Review\Model\ResourceModel\Review\Product\Collection and Magento\Review\Model\ResourceModel\Review\Collection build a query via method AddEntityFilter using exact match.

It seems that "all" that is needed here is to create a new block that calls these factory methods and passes a list of IDs - and then "override" the query in AddEntityFilter from entity_pk_value=? to entity_pk_value in (?).

That would remain backwards compatible with single value so there is no risk of conflict with anything else relying on these classes. So it could be a plugin or preference override.

I am currently getting a death walk of errors with everything I try - it seems plugins are very hard around query objects because half the methods are protected.

I can't tell if my death walk of errors are because I dont understand "around" plugins well enough and am making rookie mistakes, or because I am trying something that cannot be done by plugin

Here's what didn't work so far for me

Module Alpine\FixGrouped

Alpine\FixGrouped\view\templates\review\product\view\list.phtml is identical to the core version, except for calling a different block class

layout Alpine\FixGrouped\view\frontend\layout\review_product_list.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="catalog_product_view_type_grouped"/>
    <body>
        <referenceContainer name="product.info.details">
                <block class="Alpine\FixGrouped\Block\Review\Product\View\ListView" name="product.info.product_additional_data" as="product_additional_data" template="Alpine_FixGrouped::review/product/view/list.phtml" ifconfig="catalog/review/active"/>
        </referenceContainer>
    </body>
</page>

beginning of template: (all identical except @var line)

   /** @var Alpine\FixGrouped\Block\Review\Product\View\GroupedListView $block */

    $_items = $block->getReviewsCollection()->getItems();
    ...

Next the custom block - adds a "getGroupIds" method and uses that in the building of the collection instead of single getProductId

namespace Alpine\FixGrouped\Block\Review\Product\View;

class GroupedListView extends \Magento\Review\Block\Product\View
{

    /**
     * Get grouped product ids
     *
     * @return string|int|null
     */
    public function getGroupIds()
    {
        $product = $this->_coreRegistry->registry('product');

        // get children IDs
        if ($product) {

            $children = $product->getTypeInstance()->getChildrenIds($product->getId());
            $idlist = [$product->getId()];
            $idlist[] = $children[0];
            return implode(',',$idlist);
        }

        return null;
    }

    /**
     * Get collection of reviews
     *
     * @return ReviewCollection
     */
    public function getReviewsCollection()
    {
        if (null === $this->_reviewsCollection) {
            $this->_reviewsCollection = $this->_reviewsColFactory->create()->addStoreFilter(
                $this->_storeManager->getStore()->getId()
            )->addStatusFilter(
                \Magento\Review\Model\Review::STATUS_APPROVED
            )->addEntityFilter(
                'product',
                $this->getGroupIds()
            )->setDateOrder();
        }
        return $this->_reviewsCollection;
    }

Obviously now the addEntityFilter method in the review collection needs to be adapted to work with a list.

So I tried was plugin but I admit I am out of my depth here, never done an "around" replacement plugin before.

in frontend\di.xml

<type name="Magento\Review\Model\ResourceModel\Review\Collection">
    <plugin sortOrder="1" name="FixGroupedPluginModelReviewCollection" type="Alpine\FixGrouped\Plugin\Model\Review\ResourceModel\Review\Collection" disabled="false" />
</type>

and the code

namespace Alpine\FixGrouped\Plugin\Model\Review\ResourceModel\Review

/**
 * Review collection resource model
 *
 * @api
 * @since 100.0.2
 */
class Collection extends Magento\Review\Model\ResourceModel\Review\Collection
{

    /**
     * Add entity filter
     *
     * @param int|string $entity
     * @param int|string $pkValue
     * @return $this
     */
    public function aroundAddEntityFilter(\Magento\Review\Model\ResourceModel\Review\Collection $target, \Closure $ignore, $entity, $pkValue)
    {
        $reviewEntityTable = $target->getReviewEntityTable();
        if (is_numeric($entity)) {
            $target->addFilter('entity', $target->getConnection()->quoteInto('main_table.entity_id=?', $entity), 'string');
        } elseif (is_string($entity)) {
            $target->_select->join(
                $reviewEntityTable,
                'main_table.entity_id=' . $reviewEntityTable . '.entity_id',
                ['entity_code']
            );

            $target->addFilter(
                'entity',
                $target->getConnection()->quoteInto($reviewEntityTable . '.entity_code=?', $entity),
                'string'
            );
        }

        $target->addFilter(
            'entity_pk_value',
            $target->getConnection()->quoteInto('main_table.entity_pk_value in (?)', $pkValue),
            'string'
        );

        return $target;
    }

The result at the moment is that no error appears anywhere but also no reviews appear. Different approaches have caused outright error - including warning of protected methods - so at least some of the "plugin" code is executed

2
  • Do I need to give up on plugin and just extend both collection classes and use "preference"?
    – iphigenie
    May 6, 2020 at 11:39
  • still haven't quite made it work - the getreviewscollection element needs to be extended and neither outright extension neither plugin has quite worked :(
    – iphigenie
    Jun 3, 2020 at 15:43

2 Answers 2

2

try again with the plugin approach and replace $target->_select->join( with $target->getSelect()->join(

and $target->getReviewEntityTable(); with $target->getTable('review_entity');
This should get rid of the protected method calls.

5
  • I'm not seeing any warnings or errors, thanks! but it is not showing any reviews either. I am probably too far out of my depth with plugins - will try to output some debug info to see what I am missing
    – iphigenie
    May 7, 2020 at 18:56
  • You can get the actual sql query with echo $target->getSelect().maybe you get a hint of what is wrong
    – Marius
    May 7, 2020 at 19:02
  • I think your changes got my "around" routines to work, they at least are called. It is my override of getReviewsCollection that isnt being called, but maybe I took a shortcut too many. So it just shows the parent reviews. Which is progress!
    – iphigenie
    May 7, 2020 at 20:10
  • I can confirm that your "around" fixes did fix that part, though I am not yet getting reviews as now need to tackle the block with getReviewsCollection and probably the SummaryFactory class. Why they built it in for configurable and options but left it out for Grouped....
    – iphigenie
    May 20, 2020 at 15:38
  • in case you are easily amused @marius I have so far had to plugin an override query in the following ResourceModels: Rating/Option/Collection, Rating/Option/Vote/Collection, Review/Collection, Review/Product/Collection, Review/Summary/Collection - at least one more to go in Rating I think
    – iphigenie
    Jun 6, 2020 at 11:43
0

After a month of end of day head banging I had no luck trying to override the getReviewsCollection blocks where criteria are hard coded, neither as preference nor as plugin (things like $target->getReviewsColFactory->create() still wouldn't work no matter how many diagonal approaches I tried).

So I ended up adding logic to the plugin in the Collection classes @marius helped me fix . It's not where it should be but this does add the children reviews to the parents page - further plugins needed for the summary and the ratings, though.

Putting this out here should someone else be a grouped-product-fixing masochist

namespace Alpine\FixGrouped\Plugin\Model\Review\ResourceModel\Review;
// was namespace Magento\Review\Model\ResourceModel\Review;
use Psr\Log\LoggerInterface;
use Magento\GroupedProduct\Model\ResourceModel\Product\Link;

class Collection 
{
    /**
     * Add entity filter
     *
     * @param int|string $entity
     * @param int|string $pkValue
     * @return $this
     */
    public function aroundAddEntityFilter(\Magento\Review\Model\ResourceModel\Review\Collection $target, \Closure $ignore, $entity, $pkValue)
    {

        $reviewEntityTable = $target->getTable('review_entity');
        if (is_numeric($entity)) {
            $target->addFilter('entity', $target->getConnection()->quoteInto('main_table.entity_id=?', $entity), 'string');
        } elseif (is_string($entity)) {
            $target->getSelect()->join(
                $reviewEntityTable,
                'main_table.entity_id=' . $reviewEntityTable . '.entity_id',
                ['entity_code']
            );
            $target->addFilter(
                'entity',
                $target->getConnection()->quoteInto($reviewEntityTable . '.entity_code=?', $entity),
                'string'
            );
        }

        $idlist = $pkValue;
        if ( (is_string($entity) && $entity=='product') || ( is_numeric($entity) && $entity==1) ) {
            // should i test  is_numeric($pkValue)?
            $children = $this->getGroupedChildren($pkValue);
            if (!empty($children[3])) 
            {
                foreach(array_keys($children[3]) as $key) {$idlist .= ','.$key;}
            }
        }

        $target->addFilter(
            'entity_pk_value',
            'main_table.entity_pk_value in ('.$idlist.')',
            'string'
        );
        return $target;
    }

    /**
     * Retrieve Required children ids
     * Return grouped array, ex array(
     *   group => array(ids)
     * )
     *
     * @param int $parentId
     * @return array
     */
    public function getGroupedChildren($parentId) 
    {
        $groupedworker = \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\GroupedProduct\Model\ResourceModel\Product\Link::class);
        return $groupedworker->getChildrenIds(
            $parentId,
            \Magento\GroupedProduct\Model\ResourceModel\Product\Link::LINK_TYPE_GROUPED
        );
    }
}

PS: Here we are at the "review collections lookup" for a product so I just fetch the IDs (already fetched since we are on the product page) and put them in the condition entity_pk_value in ('.$idlist.')

For the product collections I suspect a subquery condition will be needed, as in "entity_pk_value=$entityId or entity_pk_value in (SELECT linked_product_id FROM ".$linktable." WHERE product_id=$entityId and link_type_id=3)"

PPS: I wasted a lot of time tracing things through Review and Framework classes before I realised that the original class' use of quoteInto in the line $target->getConnection()->quoteInto('main_table.entity_pk_value in (?)', $pkValue) was breaking things by quoting my whole variable...

1

Your Answer

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

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