Skip to content

Commit

Permalink
fixed a huge issue where the discount code cookie wasn't being saved …
Browse files Browse the repository at this point in the history
…on non-cached requests (turned out I was setting the cookie in the wrong place)
  • Loading branch information
crankycyclops committed Dec 1, 2019
1 parent 6ab3c8c commit c8d9399
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 23 deletions.
96 changes: 96 additions & 0 deletions Observer/Discountcodeurl/ControllerFrontSendResponseBefore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

/**
* @category Crankycyclops
* @package Crankycyclops_DiscountCodeUrl
* @author James Colannino
* @copyright Copyright (c) 2019 James Colannino
* @license https://www.gnu.org/licenses/gpl-3.0.en.html GPL v3
*/

namespace Crankycyclops\DiscountCodeUrl\Observer\Discountcodeurl;

class ControllerFrontSendResponseBefore implements \Magento\Framework\Event\ObserverInterface {

/**
* @var \Crankycyclops\DiscountCodeUrl\Helper\Config
*/
private $config;

/**
* @var \Crankycyclops\DiscountCodeUrl\Helper\Cookie
*/
private $cookieHelper;

/**
* @var \Magento\Framework\Registry $registry
*/
private $registry;

/**
* @var \Magento\Framework\Message\ManagerInterface
*/
private $messageManager;

/************************************************************************/

/**
* Constructor
*
* @param \Crankycyclops\DiscountCodeUrl\Helper\Config $config
* @param \Crankycyclops\DiscountCodeUrl\Helper\Cookie $cookieHelper
* @param \Magento\Framework\Registry $registry
* @param \Magento\Framework\Message\ManagerInterface $messageManager
*/
public function __construct(
\Crankycyclops\DiscountCodeUrl\Helper\Config $config,
\Crankycyclops\DiscountCodeUrl\Helper\Cookie $cookieHelper,
\Magento\Framework\Registry $registry,
\Magento\Framework\Message\ManagerInterface $messageManager
) {
$this->config = $config;
$this->cookieHelper = $cookieHelper;
$this->registry = $registry;
$this->messageManager = $messageManager;
}

/************************************************************************/

/**
* If a valid coupon code was set in the URL, we save a cookie with that
* value here so we can remember it when it's time to checkout. Originally,
* I was trying to set this value in
* Plugin\FrontControllerInterface::beforeDispatch(), but after having weird
* issues and tracing through the code in Magento core, I discovered in
* \Magento\Framework\App\Http::launch() that I should listen for the
* controller_front_send_response_before event and then set cookies there,
* because the cookie headers can't be set until after the request has been
* dispatched and the response has been fully rendered. I spent many hours
* pulling my hair out figuring this out...
*
* @param \Magento\Framework\Event\Observer $observer
*
* @return void
*/
public function execute(\Magento\Framework\Event\Observer $observer): void {

if ($this->config->isEnabled()) {

$coupon = $this->registry->registry('crankycyclops_discounturl_coupon');
$message = $this->registry->registry('crankycyclops_discounturl_message');

if ($coupon) {
$this->cookieHelper->setCookie($coupon);
}

if ($message) {
if ($message['error']) {
$this->messageManager->addError($message['message']);
} else {
$this->messageManager->addSuccess($message['message']);
}
}
}
}
}

54 changes: 41 additions & 13 deletions Plugin/Framework/App/FrontControllerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,37 +38,36 @@ class FrontControllerInterface {
private $ruleModel;

/**
* @var \Magento\Framework\Message\ManagerInterface
* @var \Magento\Framework\Registry $registry
*/
private $messageManager;
private $registry;

/************************************************************************/

/**
* Constructor
*
* @param \Magento\Framework\App\RequestInterface $request
* @param \Magento\Framework\UrlInterface $url
* @param \Crankycyclops\DiscountCodeUrl\Helper\Config $config
* @param \Crankycyclops\DiscountCodeUrl\Helper\Cookie $cookieHelper
* @param \Magento\SalesRule\Model\Coupon $couponModel
* @param \Magento\SalesRule\Model\Rule $ruleModel
* @param \Magento\Framework\Message\ManagerInterface $messageManager
* @param \Magento\Framework\Registry $registry
*/
public function __construct(
\Magento\Framework\App\RequestInterface $request,
\Crankycyclops\DiscountCodeUrl\Helper\Config $config,
\Crankycyclops\DiscountCodeUrl\Helper\Cookie $cookieHelper,
\Magento\SalesRule\Model\Coupon $couponModel,
\Magento\SalesRule\Model\Rule $ruleModel,
\Magento\Framework\Message\ManagerInterface $messageManager
\Magento\Framework\Registry $registry
) {
$this->request = $request;
$this->config = $config;
$this->cookieHelper = $cookieHelper;
$this->couponModel = $couponModel;
$this->ruleModel = $ruleModel;
$this->messageManager = $messageManager;
$this->registry = $registry;
}

/************************************************************************/
Expand Down Expand Up @@ -144,17 +143,26 @@ public function beforeDispatch(\Magento\Framework\App\FrontControllerInterface $

// Discount code is expired
if ($expirationDay && strtotime($expirationDay) < $today) {
$this->messageManager->addError(__($expiredMessage));
$this->registry->register('crankycyclops_discounturl_message', [
'message' => __($expiredMessage),
'error' => true
]);
}

// Discount hasn't started yet
else if ($startDay && strtotime($startDay) > $today) {
$this->messageManager->addError(__($invalidMessage));
$this->registry->register('crankycyclops_discounturl_message', [
'message' => __($invalidMessage),
'error' => true
]);
}

// Coupon has already been fully consumed
else if ($maxUses && $numUses >= $maxUses) {
$this->messageManager->addError(__($consumedMessage));
$this->registry->register('crankycyclops_discounturl_message', [
'message' => __($consumedMessage),
'error' => true
]);
}

else {
Expand All @@ -172,18 +180,38 @@ public function beforeDispatch(\Magento\Framework\App\FrontControllerInterface $
$successMessage .= " per customer)";
}

$this->cookieHelper->setCookie($coupon);
$this->messageManager->addSuccess(__($successMessage));
// As documented in
// \Magento\Framework\App\Http::launch()
// around line 150, I can't actually set a
// cookie until after the request is
// dispatched and the result is rendered.
// Thus, I save this coupon code in the
// registry and actually set the cookie in an
// observer that listens for
// controller_front_send_response_before. You
// don't know how many hours I pulled my hair
// out figuring this out...
$this->registry->register('crankycyclops_discounturl_coupon', $coupon);
$this->registry->register('crankycyclops_discounturl_message', [
'message' => __($successMessage),
'error' => false
]);
}
}

else {
$this->messageManager->addError(__($invalidMessage));
$this->registry->register('crankycyclops_discounturl_message', [
'message' => __($invalidMessage),
'error' => true
]);
}
}

else {
$this->messageManager->addError(__($invalidMessage));
$this->registry->register('crankycyclops_discounturl_message', [
'message' => __($invalidMessage),
'error' => true
]);
}
}
}
Expand Down
10 changes: 0 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,6 @@ It's also a good idea to clear your cache.

Config options can be found in Stores -> Configuration -> Customers -> Promotions -> Discount URL Settings

## Known Issues

There's a really annoying bug that I'm trying to track down. I believe it's something happening in Magento core, but I can't prove that yet. Basically what happens is, the first time a discount code URL is used, the cookie header isn't sent, and so the cookie doesn't get set and the discount code doesn't get remembered. On the second request, however, and every request after, the cookie header is sent as it should be.

This is a server side issue, not something on the client side. If, for example, you request http://store.url/path/to/page/discount/TESTCODE in one browser, the cookie header won't be sent. Then, if you request that exact same URL in another browser, the cookie will be set, even though it's the first time the page has been requested in that other browser.

It's a really weird issue, and right now, I'm kind of at a loss. For now, to work around this issue, you can "prime" the module by requesting coupon code URLs you know you're going to share at least once. Doing so will ensure that the next time someone browses to that URL, the module works as expected. If you clear the cache, you'll have to "prime" the URL again.

I'm working on it...

## Authors

James Colannino - [crankycyclops](https://github.com/crankycyclops)
Expand Down
1 change: 1 addition & 0 deletions etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

<!-- Detects the discount code in the URL and set a cookie to remember it -->
<type name="Magento\Framework\App\FrontControllerInterface">
<plugin
name="crankycyclops_discountcodeurl_frontcontrollerinterface"
Expand Down
4 changes: 4 additions & 0 deletions etc/events.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">

<event name="controller_front_send_response_before">
<observer name="crankycyclops_discountcodeurl_controller_front_send_response_before" instance="Crankycyclops\DiscountCodeUrl\Observer\Discountcodeurl\ControllerFrontSendResponseBefore" />
</event>

<event name="checkout_cart_save_after">
<observer name="crankycyclops_discountcodeurl_checkout_cart_save_after" instance="Crankycyclops\DiscountCodeUrl\Observer\Discountcodeurl\CheckoutCartSaveAfter" />
</event>
Expand Down

0 comments on commit c8d9399

Please sign in to comment.