From c8d9399b750a07a8e518fc3518f472cd368b7644 Mon Sep 17 00:00:00 2001 From: Jeff Coleman Date: Sat, 30 Nov 2019 20:49:43 -0800 Subject: [PATCH] fixed a huge issue where the discount code cookie wasn't being saved on non-cached requests (turned out I was setting the cookie in the wrong place) --- .../ControllerFrontSendResponseBefore.php | 96 +++++++++++++++++++ .../App/FrontControllerInterface.php | 54 ++++++++--- README.md | 10 -- etc/di.xml | 1 + etc/events.xml | 4 + 5 files changed, 142 insertions(+), 23 deletions(-) create mode 100644 Observer/Discountcodeurl/ControllerFrontSendResponseBefore.php diff --git a/Observer/Discountcodeurl/ControllerFrontSendResponseBefore.php b/Observer/Discountcodeurl/ControllerFrontSendResponseBefore.php new file mode 100644 index 0000000..0f747ca --- /dev/null +++ b/Observer/Discountcodeurl/ControllerFrontSendResponseBefore.php @@ -0,0 +1,96 @@ +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']); + } + } + } + } +} + diff --git a/Plugin/Framework/App/FrontControllerInterface.php b/Plugin/Framework/App/FrontControllerInterface.php index 76187aa..dd2eaca 100644 --- a/Plugin/Framework/App/FrontControllerInterface.php +++ b/Plugin/Framework/App/FrontControllerInterface.php @@ -38,9 +38,9 @@ class FrontControllerInterface { private $ruleModel; /** - * @var \Magento\Framework\Message\ManagerInterface + * @var \Magento\Framework\Registry $registry */ - private $messageManager; + private $registry; /************************************************************************/ @@ -48,12 +48,11 @@ class FrontControllerInterface { * 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, @@ -61,14 +60,14 @@ public function __construct( \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; } /************************************************************************/ @@ -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 { @@ -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 + ]); } } } diff --git a/README.md b/README.md index cfb2eab..b72641b 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/etc/di.xml b/etc/di.xml index a8257d4..e9eabc7 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -10,6 +10,7 @@ + + + + +