1

I have created an Extension to show a button named Shop By Collection on product page based on the condition that shop_by_collection dropdown attribute option is assigned to product. This part is working fien and I can get a button with link and attribue code option id in the url. After clicking the button I am redirected to a new custom collection page where I am showing all relevent products this is working fine but toolbar and layered navigation is not showing.

Here is my complete code, plz guide me what is the issue.

Block/ProductList.php

<?php /** * Mica ShopByCollection Product List Block */ namespace Mica\ShopByCollection\Block\Collection; use Magento\Catalog\Block\Product\ListProduct; use Magento\Catalog\Block\Product\Context; use Magento\Catalog\Model\Layer\Resolver; use Magento\Framework\Data\Helper\PostHelper; use Magento\Framework\Url\Helper\Data; use Magento\Framework\App\RequestInterface; use Magento\Framework\Registry; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\Catalog\Api\CategoryRepositoryInterface; class ProductList extends ListProduct { /** * @var RequestInterface */ protected $request; /** * @var Registry */ protected $registry; /** * @var CollectionFactory */ protected $productCollectionFactory; /** * @param Context $context * @param PostHelper $postDataHelper * @param Resolver $layerResolver * @param CategoryRepositoryInterface $categoryRepository * @param RequestInterface $request * @param Registry $registry * @param CollectionFactory $productCollectionFactory * @param Data $urlHelper * @param array $data */ public function __construct( Context $context, PostHelper $postDataHelper, Resolver $layerResolver, CategoryRepositoryInterface $categoryRepository, RequestInterface $request, Registry $registry, CollectionFactory $productCollectionFactory, Data $urlHelper, array $data = [] ) { $this->request = $request; $this->registry = $registry; $this->productCollectionFactory = $productCollectionFactory; parent::__construct( $context, $postDataHelper, $layerResolver, $categoryRepository, $urlHelper, $data ); } /** * Get loaded product collection * * @return \Magento\Catalog\Model\ResourceModel\Product\Collection */ protected function _getProductCollection() { if ($this->_productCollection === null) { // Try to get collection from layer first $layer = $this->getLayer(); if ($layer) { $this->_productCollection = $layer->getProductCollection(); } // If layer collection is empty, create our own if ($this->_productCollection === null || $this->_productCollection->count() === 0) { $this->_productCollection = $this->productCollectionFactory->create(); $this->_productCollection->addAttributeToSelect('*'); $this->_productCollection->addMinimalPrice(); $this->_productCollection->addFinalPrice(); $this->_productCollection->addTaxPercents(); $this->_productCollection->addUrlRewrite(); $this->_productCollection->addStoreFilter(); } // Apply shop_by_collection filter $attributeValue = $this->request->getParam('attribute_value'); if ($attributeValue) { // Add the filter directly to the collection $this->_productCollection->addAttributeToFilter('shop_by_collection', $attributeValue); } // Filter out configurable child products // This joins the collection with the super link table and excludes any // products that are children of configurable products $this->_productCollection->getSelect()->joinLeft( ['super_link' => $this->_productCollection->getTable('catalog_product_super_link')], 'e.entity_id = super_link.product_id', [] )->where('super_link.product_id IS NULL'); } return $this->_productCollection; } } 

Block/CollectionButton.php

<?php /** * Mica ShopByCollection Button Block */ namespace Mica\ShopByCollection\Block\Product; use Magento\Catalog\Model\Product; use Magento\Framework\Registry; use Magento\Framework\View\Element\Template; use Magento\Framework\View\Element\Template\Context; class CollectionButton extends Template { /** * @var Registry */ private $registry; /** * @param Context $context * @param Registry $registry * @param array $data */ public function __construct( Context $context, Registry $registry, array $data = [] ) { $this->registry = $registry; parent::__construct($context, $data); } /** * Get current product * * @return Product|null */ public function getProduct() { return $this->registry->registry('current_product'); } /** * Check if product has collection attribute * * @return bool */ public function hasCollection() { $product = $this->getProduct(); if ($product && $product->getData('shop_by_collection')) { return true; } return false; } /** * Get collection attribute value * * @return string */ public function getCollectionValue() { $product = $this->getProduct(); if ($product) { $attributeValue = $product->getAttributeText('shop_by_collection'); if ($attributeValue) { return $attributeValue; } return $product->getData('shop_by_collection'); } return ''; } /** * Get collection URL * * @return string */ public function getCollectionUrl() { $product = $this->getProduct(); if ($product) { // Log the attribute value for debugging $this->_logger->debug('Collection attribute value: ' . $product->getData('shop_by_collection')); return $this->getUrl('collection/collection/view', [ 'attribute_value' => $product->getData('shop_by_collection') ]); } return '#'; } /** * Get collection attribute options for debugging * * @return array */ public function getAttributeOptions() { $options = []; $product = $this->getProduct(); if ($product) { $attribute = $product->getResource()->getAttribute('shop_by_collection'); if ($attribute) { foreach ($attribute->getOptions() as $option) { $options[$option->getValue()] = $option->getLabel(); } } } return $options; } } 

Controller/Collection/View.php

<?php /** * Mica ShopByCollection Collection View Controller */ namespace Mica\ShopByCollection\Controller\Collection; use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\RequestInterface; use Magento\Framework\View\Result\PageFactory; use Magento\Framework\Registry; use Magento\Catalog\Model\Layer\Resolver; class View extends Action { /** * @var PageFactory */ protected $resultPageFactory; /** * @var Registry */ protected $registry; /** * @var Resolver */ protected $layerResolver; /** * @param Context $context * @param PageFactory $resultPageFactory * @param Registry $registry * @param Resolver $layerResolver */ public function __construct( Context $context, PageFactory $resultPageFactory, Registry $registry, Resolver $layerResolver ) { $this->resultPageFactory = $resultPageFactory; $this->registry = $registry; $this->layerResolver = $layerResolver; parent::__construct($context); } /** * Collection view action * * @return \Magento\Framework\Controller\ResultInterface */ public function execute() { // FIXED: Use a valid layer type - 'search' is always available $this->layerResolver->create('search'); $resultPage = $this->resultPageFactory->create(); $attributeValue = $this->getRequest()->getParam('attribute_value'); // Register attribute value for use in blocks if ($attributeValue) { $this->registry->register('current_collection_attribute_value', $attributeValue); $attributeText = $this->getAttributeText($attributeValue); $resultPage->getConfig()->getTitle()->set(__('Products in %1 Collection', $attributeText)); } else { $resultPage->getConfig()->getTitle()->set(__('Collection')); } return $resultPage; } /** * Get attribute text value * * @param string $attributeValue * @return string */ private function getAttributeText($attributeValue) { // Get attribute text from option value $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $attributeRepository = $objectManager->get(\Magento\Eav\Api\AttributeRepositoryInterface::class); try { $attribute = $attributeRepository->get('catalog_product', 'shop_by_collection'); foreach ($attribute->getOptions() as $option) { if ($option->getValue() == $attributeValue) { return $option->getLabel(); } } } catch (\Exception $e) { return $attributeValue; } return $attributeValue; } } 

etc/frontend/routes.xml

<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="standard"> <route id="mica_collection" frontName="collection"> <module name="Mica_ShopByCollection" /> </route> </router> </config> 

etc/di/xml

<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Catalog\Model\Layer\Search\FilterableAttributeList"> <plugin name="mica_collection_filter_search" type="Mica\ShopByCollection\Plugin\Layer\CollectionFilterList" /> </type> <type name="Magento\Catalog\Model\Layer\Category\FilterableAttributeList"> <plugin name="mica_collection_filter_category" type="Mica\ShopByCollection\Plugin\Layer\CollectionFilterList" /> </type> </config> 

Model/CollectionProducts.php

<?php /** * Mica ShopByCollection Collection Products Model */ namespace Mica\ShopByCollection\Model; use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\Framework\App\RequestInterface; class CollectionProducts { /** * @var CollectionFactory */ protected $collectionFactory; /** * @var RequestInterface */ protected $request; /** * @param CollectionFactory $collectionFactory * @param RequestInterface $request */ public function __construct( CollectionFactory $collectionFactory, RequestInterface $request ) { $this->collectionFactory = $collectionFactory; $this->request = $request; } /** * Get product collection filtered by attribute value * * @return Collection */ public function getFilteredCollection() { $collection = $this->collectionFactory->create(); $collection->addAttributeToSelect('*'); $attributeValue = $this->request->getParam('attribute_value'); if ($attributeValue) { $collection->addAttributeToFilter('shop_by_collection', $attributeValue); } return $collection; } } 

view/frontend/layout/mica_collection_collection_view.xml

<?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"> <update handle="catalog_category_view_type_layered"/> <body> <referenceContainer name="content"> <!-- Product listing --> <block class="Mica\ShopByCollection\Block\Collection\ProductList" name="collection.products" template="Magento_Catalog::product/list.phtml" cacheable="false"> <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="default" as="default"/> </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> </referenceContainer> <referenceContainer name="sidebar.main"> <block class="Magento\LayeredNavigation\Block\Navigation\Category" name="catalog.leftnav" template="Magento_LayeredNavigation::layer/view.phtml"> <block class="Magento\LayeredNavigation\Block\Navigation\State" name="catalog.navigation.state" as="state" /> <block class="Magento\LayeredNavigation\Block\Navigation\FilterRenderer" name="catalog.navigation.renderer" as="renderer" template="Magento_LayeredNavigation::layer/filter.phtml"/> </block> </referenceContainer> <referenceContainer name="head.additional"> <block class="Magento\Framework\View\Element\Template" name="mica.shopbycollection.custom.css" template="Mica_ShopByCollection::custom-styles.phtml"/> </referenceContainer> </body> </page> 

view/frontend/layout/catalog_product_view.xml

<?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> <referenceContainer name="product.info.price"> <block class="Mica\ShopByCollection\Block\Product\CollectionButton" name="product.info.collection.button" template="Mica_ShopByCollection::product/collection_button.phtml" after="-" /> </referenceContainer> </body> </page> 

view/frontend/templates/collection_button.phtml

<?php /** * @var $block \Mica\ShopByCollection\Block\Product\CollectionButton */ ?> <?php if ($block->hasCollection()): ?> <div class="collection-button-container"> <a href="<?= $block->escapeUrl($block->getCollectionUrl()) ?>" class="action primary collection-button" title="<?= $block->escapeHtml(__('Shop by %1 Collection', $block->getCollectionValue())) ?>"> <span><?= $block->escapeHtml(__('Shop by %1 Collection', $block->getCollectionValue())) ?></span> </a> </div> 

1 Answer 1

0

Oh boy, welcome to my worst Magento nightmare ^^

For some reasons; getting a custom collection (aka something that is not fully included inside a single category) is actually quite easy...but adapting the toolbar and layered navigation is really a nightmare. I've been trying and trying to do it without any success.

I made this one to get help a few times ago : Update layer navigation filter with the proper custom collection

Plot twist...i've never succeeded to do what i wanted to.

I've been ending by taking the issue hover a whole new different angle.

TO get my products from custom collection i've been using a virtual category (not accessible from menu). From that virtual category, I've been updating the front search system in order to get my 'all virtual products category' filtered.

Here is an example of what my controller is doing

 public function execute() { $searchedRefOe = strtoupper(trim($this->request->getParam('refOe'))); if($searchedRefOe == null || $searchedRefOe == ''){ $searchedRefOe = strtoupper(trim($this->request->getParam('refOeHeader'))); } $searchedRefOe = strtoupper(str_replace(' ', '', $searchedRefOe)); $url = $this->frontSearch->getAllProductsUrl([ 'tws_piece_reference_oe' => $this->sanitizeRefOe($searchedRefOe), ]); return $this->resultRedirectFactory->create() ->setUrl($url); } 

The idea is that it's sending a custom parameter value to the frontsearch which is responsible to give me the proper results. And as the results come from a real 'virtual' category...no more nightmare with toolbar and layered navigation.

 public function getAllProductsCategoryPath($store = null) { // Hardcoded for now, maybe make it configurable, either category id or request path return 'search-refoe.html'; } public function getAllProductsUrl(array $params = [], $store = null) { $routeParams = [ '_direct' => $this->getAllProductsCategoryPath($store), ]; if ($params) { $routeParams['_query'] = $params; } return $this->url->getUrl('', $routeParams); } 

Hope this will help you from a year of nightmare !

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.