0

I have 5 categories with single products each. I also have 5 stores.

I want to redirect the user directly to the product page when he clicks on the category link in the main menu for each store.

Can this be done in magento 2.1 CE?

2 Answers 2

0

you can write an observer for the event controller_action_predispatch_catalog_category_view that checks if the current category has a single product.

Create this file in one of your extensions.

[Namespace]/[Module]/etc/frontend/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="controller_action_predispatch_catalog_category_view">
        <observer name="[namespace]-[module]-category-view" instance="[Namespace]\[Module]\Observer\CategoryView" />
    </event>
</config>

then create the observer class:
[Namespace]/[Module]/Observer/CategoryView.php

<?php
namespace [Namespace]\[Module]\Observer;

class CategoryView implements \Magento\Framework\Event\ObserverInterface
{
    /**
     * @var \Magento\Catalog\Model\Layer\Resolver
     */
    protected $layerResolver;
    /**
     * @var \Magento\Framework\App\RequestInterface
     */
    protected $request;

    /**
     * @var \Magento\Catalog\Api\CategoryRepositoryInterface
     */
    protected $categoryRepository;

    /**
     * @var \Magento\Framework\App\ResponseInterface
     */
    protected $response;

    /**
     * @param \Magento\Catalog\Model\Layer\Resolver $layerResolver
     * @param \Magento\Framework\App\RequestInterface $request
     * @param \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository
     * @param \Magento\Framework\App\ResponseInterface $reponse
     */
    public function __construct(
        \Magento\Catalog\Model\Layer\Resolver $layerResolver,
        \Magento\Framework\App\RequestInterface $request,
        \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository,
        \Magento\Framework\App\ResponseInterface $reponse
    ) {
        $this->layerResolver = $layerResolver;
        $this->request = $request;
        $this->categoryRepository = $categoryRepository;
        $this->response = $reponse;
    }
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $categoryId = $this->request->getParam('id', false);
        if (!$categoryId) {
            return $this;
        }
        $layer = $this->layerResolver->get();
        $category = $this->categoryRepository->get($categoryId);
        $layer->setCurrentCategory($category);
        $collection = $layer->getProductCollection();
        if ($collection->count() == 1) {
            /** @var \Magento\Catalog\Model\Product $product */
            $product = $collection->getFirstItem();
            $this->response->setRedirect($product->getProductUrl());
            $this->response->sendResponse();
            exit;
        }
        return $this;
    }

}
0

Create a module (it's actually a plugin) in your app/code folder, let's call the module SingleProductCategoryLink. In the folder app/code/VendorName/SingleProductCategoryLink you will create 6 files.

  • registration.php
  • etc/module.xml
  • etc/di.xml
  • etc/config.xml (optional)
  • etc/adminhtml/system.xml
  • Plugin/Controller/ListProduct.php

The first two should be obvious so I will not elaborate on them. The config.xml file will allow you to set this setting in the backend per store-front.

<!-- etc/config.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <elgentos>
            <single_product_category_link>
                <enabled>1</enabled>
            </single_product_category_link>
        </elgentos>
    </default>
</config>
<!-- etc/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Block\Product\ListProduct">
        <plugin name="firstproduct-category-mod" type="Vendor\SingleProductCategoryLink\Block\Catalog\Product\ListProduct" sortOrder="1"/>
    </type>
</config>

This one is optional but it allows you to enable the module selectively per store-front

<!-- etc/adminhtml/system.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="elgentos" translate="label" sortOrder="102">
            <label>Vendor</label>
        </tab>
        <section id="single_product_category_link" sortOrder="102" showInDefault="1" showInWebsite="1" showInStore="1">
            <tab>Vendor</tab>
            <label>Single Product Category Link</label>
            <resource>Vendor_SingleProductCategoryLink::config</resource>

            <group id="general" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>General</label>

                <field id="enabled" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Enabled</label>
                    <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model>
                </field>
            </group>
        </section>
    </system>
</config>

And this is the meat of the plugin:

<?php
// Plugin/Controller/ListProduct.php

declare(strict_types=1);

namespace Vendor\SingleProductCategoryLink\Plugin\Controller\ListProduct;

use Magento\Catalog\Block\Product\ListProduct as MagentoListProduct;
use Magento\Catalog\Model\Product;
use Magento\Eav\Model\Entity\Collection\AbstractCollection;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Response\Http;
use Magento\Store\Model\ScopeInterface;

class ListProduct
{
    public function __construct(
        protected readonly Http $response,
        protected readonly ScopeConfigInterface $scopeConfig
    ) {
    }

    public function afterGetLoadedProductCollection(
        MagentoListProduct $subject, 
        AbstractCollection $resultCollection
    ): AbstractCollection
    {
        // If you included the optional config.xml file
        $enabled = $this->scopeConfig->getValue('single_product_category_link/general/enabled', ScopeInterface::SCOPE_STORE);

        if ($enabled && $resultCollection->getSize() === 1) {
            /** @var Product $product */
            $product = $resultCollection->getFirstItem();
            $this->response->setRedirect($product->getProductUrl());
        }

        return $resultCollection;
    }
}

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.