1

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.

2 Answers 2

0

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 :)

-1

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]);
    }

}
5
  • How will this work for any arbitrary URL? I want to transform my.store/any_possible_url/discount/CODE to a request for my.store/any_possible_url. any_possible_url could have any number of parts and could contain any number of different strings. You're only matching one specific URL. Commented Nov 25, 2019 at 21:00
  • Figured out how to do what I needed using my plugin. See my answer above. Commented Nov 25, 2019 at 22:18
  • @Crankycyclops, your solution is ok but I don't think it appropriate
    – Amit Bera
    Commented Nov 26, 2019 at 7:25
  • Fair enough. If you can come up with a better solution that covers the criteria mentioned in the original question, I'll consider changing my approach. Commented Nov 27, 2019 at 0:49
  • @AmitBera can you help me on this magento.stackexchange.com/questions/296960/… Commented Nov 27, 2019 at 3:07

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.