Question

I'm writing a module that will accept any Magento URL with /discount/COUPON_CODE appended to the end, set the code in a cookie for later use, and then forward to the URL minus the discount part.

So, for example, https://store.url/page/path/discount/COUPON_CODE would eventually load the page that would have been found at https://store.url/page/path.

The tricky part is that I can't do a 300 redirect (that would have been easy.) I want to actually forward to the appropriate controller/action internally so that the discount code URL remains intact. I'm having a really hard time figuring out how to do this.

Right now, I have a plugin for FrontControllerInterface with a beforeDispatch method that examines the URL, and if a discount code is found, sets it in a cookie. At the end of this method, I was hoping to strip /discount/COUPON_CODE from the URL and then continue dispatching using the modified URL, but I'm not sure this is the right approach (if it is, I can't figure out how.)

Does anyone have an idea of how I might accomplish this? I can see that there are ways I can dispatch a specific controller or action using a custom router, but I can't figure out how to match an arbitrary URL to the correct controller and action once I've modified it.

Was it helpful?

Solution

I figured out a solution that works for me. In the beforeDispatch() method of my FrontControllerInterface plugin, I have the following block of code:

$requestPath = $this->request->getPathInfo();

// If a coupon code was included in the URL, fix the URL
// after extracting it so that we can continue to route normally
if (preg_match("#/discount/([^/]+?)/*$#", $requestPath, $matches)) {

    $coupon = $matches[1];
    $realPath = str_replace($matches[0], '', $requestPath);

    if (!$realPath) {
        $realPath = '/';
    }

    $this->request->setPathInfo($realPath);
}

This does exactly what I need. Hope it can also help someone else in the future.

EDIT: The plugin I was writing can be found here: https://github.com/crankycyclops/DiscountCodeUrl It's a free and open source means to automatically apply coupon codes via the URL. Hope someone finds it useful :)

OTHER TIPS

For this your requirement, you need to use Custom routers instead of plugin on FrontControllerInterfaceetc.

Custom router has the ability to handle the custom URL as per as business logic.

Custom router internally hit the Magento Standard router Url www.example.com/<front-name>/<controller-name>/<action-name> .At Magento ,if you want to output/render from an Action then your URL must be hit <front-name>/<controller-name>/<action-name> directly or indirectly

A good example is the Magento Cms module' router Magento\Cms\Controller\Router.

        $page = $this->_pageFactory->create();
        $pageId = $page->checkIdentifier($identifier, $this->_storeManager->getStore()->getId());
        if (!$pageId) {
            return null;
        }
        $request->setModuleName('cms')->setControllerName('page')->setActionName('view')->setParam('page_id', $pageId);
        $request->setAlias(\Magento\Framework\Url::REWRITE_REQUEST_PATH_ALIAS, $identifier);
        return $this->actionFactory->create(\Magento\Framework\App\Action\Forward::class);

You need when a user is hit cms page identifier then it's internally hit magento www.example/cms/page/view with params page_id/$pageId

You need to build the same logic for this requirement.

Declare Custom router at di.xml

First create di.xml at app/code/{VendorName}/{ModuleName}/etc/frontend. And Defined Custom router here.

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <!--Add Custom route-->
    <type name="Magento\Framework\App\RouterList">
        <arguments>
            <argument name="routerList" xsi:type="array">
                <item name="crankycyclops" xsi:type="array">
                    <item name="class" xsi:type="string">VendorName\ModuleName\Controller\Router</item>
                    <item name="disable" xsi:type="boolean">false</item>
                    <item name="sortOrder" xsi:type="string">31</item>
                </item>
            </argument>
        </arguments>       
    </type>
 </config>

Custom router Class

After that create Custom router class VendorName\ModuleName\Controller\Router where you build the logic for /page/path/discount/COUPON_CODE routing.

Assume that Url is www.example.com/myurlone/myurltwo/discount/COUPON_CODE.

Here: www.example.com/myurlone/myurltwo/discount/COUPON_CODE hit

= www.example.com/routing/index/index/discount_code/COUPON_CODE

Code

<?php

declare(strict_types=1);

namespace VendorName\ModuleName\Controller;

use Magento\Framework\App\ActionFactory;
use Magento\Framework\App\Action\Forward;

class Router implements \Magento\Framework\App\RouterInterface {

    /**
     * @var ActionFactory
     */
    private $actionFactory;

    public function __construct(
        ActionFactory $actionFactory
    ) {

        $this->actionFactory = $actionFactory;
    }

    public function match(\Magento\Framework\App\RequestInterface $request) {


        $identifier = trim($request->getPathInfo(), '/');
        $condition = new \Magento\Framework\DataObject(['identifier' => $identifier, 'continue' => true]);
        $identifier = $condition->getIdentifier(); 

        if (strpos($identifier, 'myurlone/myurltwo/discount') === false) {
            return null;
        }

        /**
         * Split myurlone/myurltwo/discount/COUPON_CODE by /
         */
        $urlParts = explode('/', $identifier);

        /**
         * if $urlParts count is more then 4 or less then custom route return null means not match  
         */
        if(count($urlParts) > 4 && count($urlParts) <3 ){
            return null;
        }

        if($urlParts[0] !== 'myurlone' ||  $urlParts[1] !== 'myurltwo' 
                ||   $urlParts[2] !== 'discount'){
             return null;
        }

        $discountCode = $urlParts[3]; // $urlParts[3]   = COUPON_CODE

        /**
         * here assume that  COUPON_CODE is NOT match 
         *  Changes the logic as per your requirement
         */

        if($discountCode  === 'NOT MATCH'){
            return null;
        }

        /**
         * if match 
         */

        $request->setModuleName('routing');
        $request->setControllerName('index');
        $request->setActionName('index');
        $request->setParams([
            'discount_code' => $discountCode
        ]);

        return $this->actionFactory->create(Forward::class, ['request' => $request]);
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top