diff --git a/changelog/_unreleased/2024-06-21-add-new-context-request-attr-on-customer-login.md b/changelog/_unreleased/2024-06-21-add-new-context-request-attr-on-customer-login.md
new file mode 100644
index 00000000000..572714aef18
--- /dev/null
+++ b/changelog/_unreleased/2024-06-21-add-new-context-request-attr-on-customer-login.md
@@ -0,0 +1,8 @@
+---
+title: Add new context request attribute on customer login
+issue: NEXT-36874
+---
+# Core
+* Added listener on `CustomerLoginEvent` to `\Shopware\Core\Framework\Adapter\Cache\Http\CacheResponseSubscriber` in order to save the new Context to the request, which is later used in the subscriber to generate the cache state.
+* Removed manually adding the context to the request in the `\Shopware\Storefront\Controller\AuthController` and replaced it with the listener, that should handle all cases (e.g. login, double-opt-in, etc).
+
diff --git a/src/Core/Framework/Adapter/Cache/Http/CacheResponseSubscriber.php b/src/Core/Framework/Adapter/Cache/Http/CacheResponseSubscriber.php
index 2b3ac55d703..d138fbe60f6 100644
--- a/src/Core/Framework/Adapter/Cache/Http/CacheResponseSubscriber.php
+++ b/src/Core/Framework/Adapter/Cache/Http/CacheResponseSubscriber.php
@@ -4,6 +4,7 @@
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
+use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent;
use Shopware\Core\Framework\Adapter\Cache\CacheStateSubscriber;
use Shopware\Core\Framework\Event\BeforeSendResponseEvent;
use Shopware\Core\Framework\Log\Package;
@@ -14,6 +15,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
@@ -47,6 +49,7 @@ public function __construct(
private readonly int $defaultTtl,
private readonly bool $httpCacheEnabled,
private readonly MaintenanceModeResolver $maintenanceResolver,
+ private readonly RequestStack $requestStack,
private readonly bool $reverseProxyEnabled,
private readonly ?string $staleWhileRevalidate,
private readonly ?string $staleIfError
@@ -65,6 +68,7 @@ public static function getSubscribedEvents(): array
['setResponseCacheHeader', 1500],
],
BeforeSendResponseEvent::class => 'updateCacheControlForBrowser',
+ CustomerLoginEvent::class => 'onCustomerLogin',
];
}
@@ -217,6 +221,16 @@ private function hasInvalidationState(array $cacheStates, array $states): bool
return false;
}
+ public function onCustomerLogin(CustomerLoginEvent $event): void
+ {
+ $request = $this->requestStack->getCurrentRequest();
+ if (!$request) {
+ return;
+ }
+
+ $request->attributes->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT, $event->getSalesChannelContext());
+ }
+
private function buildCacheHash(SalesChannelContext $context): string
{
return md5(json_encode([
diff --git a/src/Core/Framework/DependencyInjection/cache.xml b/src/Core/Framework/DependencyInjection/cache.xml
index bdbd335d117..66a14c505d9 100644
--- a/src/Core/Framework/DependencyInjection/cache.xml
+++ b/src/Core/Framework/DependencyInjection/cache.xml
@@ -160,6 +160,7 @@
%shopware.http.cache.default_ttl%
%shopware.http.cache.enabled%
+
%shopware.http_cache.reverse_proxy.enabled%
%shopware.http_cache.stale_while_revalidate%
%shopware.http_cache.stale_if_error%
diff --git a/src/Storefront/Controller/AuthController.php b/src/Storefront/Controller/AuthController.php
index c4aed685a66..75a685bf7dd 100644
--- a/src/Storefront/Controller/AuthController.php
+++ b/src/Storefront/Controller/AuthController.php
@@ -20,9 +20,6 @@
use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
-use Shopware\Core\PlatformRequest;
-use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceInterface;
-use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceParameters;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Checkout\Cart\SalesChannel\StorefrontCartFacade;
use Shopware\Storefront\Framework\Routing\RequestTransformer;
@@ -54,8 +51,7 @@ public function __construct(
private readonly AbstractLoginRoute $loginRoute,
private readonly AbstractLogoutRoute $logoutRoute,
private readonly StorefrontCartFacade $cartFacade,
- private readonly AccountRecoverPasswordPageLoader $recoverPasswordPageLoader,
- private readonly SalesChannelContextServiceInterface $salesChannelContextService
+ private readonly AccountRecoverPasswordPageLoader $recoverPasswordPageLoader
) {
}
@@ -161,20 +157,6 @@ public function login(Request $request, RequestDataBag $data, SalesChannelContex
$token = $this->loginRoute->login($data, $context)->getToken();
$cartBeforeNewContext = $this->cartFacade->get($token, $context);
- $newContext = $this->salesChannelContextService->get(
- new SalesChannelContextServiceParameters(
- $context->getSalesChannelId(),
- $token,
- $context->getLanguageId(),
- $context->getCurrencyId(),
- $context->getDomainId(),
- $context->getContext()
- )
- );
-
- // Update the sales channel context for CacheResponseSubscriber
- $request->attributes->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT, $newContext);
-
if (!empty($token)) {
$this->addCartErrors($cartBeforeNewContext);
diff --git a/src/Storefront/DependencyInjection/controller.xml b/src/Storefront/DependencyInjection/controller.xml
index 687d906814e..82696c2f3f8 100644
--- a/src/Storefront/DependencyInjection/controller.xml
+++ b/src/Storefront/DependencyInjection/controller.xml
@@ -86,7 +86,6 @@
-
diff --git a/tests/integration/Storefront/Controller/AuthControllerTest.php b/tests/integration/Storefront/Controller/AuthControllerTest.php
index c986d20543c..c24ba5396ba 100644
--- a/tests/integration/Storefront/Controller/AuthControllerTest.php
+++ b/tests/integration/Storefront/Controller/AuthControllerTest.php
@@ -704,8 +704,7 @@ private function getAuthController(?AbstractSendPasswordRecoveryMailRoute $sendP
$this->getContainer()->get(LoginRoute::class),
$this->createMock(AbstractLogoutRoute::class),
$this->getContainer()->get(StorefrontCartFacade::class),
- $this->getContainer()->get(AccountRecoverPasswordPageLoader::class),
- $this->getContainer()->get(SalesChannelContextService::class)
+ $this->getContainer()->get(AccountRecoverPasswordPageLoader::class)
);
$controller->setContainer($this->getContainer());
$controller->setTwig($this->getContainer()->get('twig'));
diff --git a/tests/integration/Storefront/Controller/ControllerRateLimiterTest.php b/tests/integration/Storefront/Controller/ControllerRateLimiterTest.php
index 27a68cb952b..8e77a1cad97 100644
--- a/tests/integration/Storefront/Controller/ControllerRateLimiterTest.php
+++ b/tests/integration/Storefront/Controller/ControllerRateLimiterTest.php
@@ -32,7 +32,6 @@
use Shopware\Core\SalesChannelRequest;
use Shopware\Core\System\SalesChannel\Context\AbstractSalesChannelContextFactory;
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextFactory;
-use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Checkout\Cart\SalesChannel\StorefrontCartFacade;
use Shopware\Storefront\Controller\AuthController;
@@ -122,8 +121,7 @@ public function testGenerateAccountRecoveryRateLimit(): void
$this->getContainer()->get(LoginRoute::class),
$this->getContainer()->get(LogoutRoute::class),
$this->getContainer()->get(StorefrontCartFacade::class),
- $this->getContainer()->get(AccountRecoverPasswordPageLoader::class),
- $this->getContainer()->get(SalesChannelContextService::class)
+ $this->getContainer()->get(AccountRecoverPasswordPageLoader::class)
);
$controller->setContainer($this->getContainer());
@@ -154,8 +152,7 @@ public function testAuthControllerGuestLoginShowsRateLimit(): void
$this->createMock(LoginRoute::class),
$this->createMock(AbstractLogoutRoute::class),
$this->getContainer()->get(StorefrontCartFacade::class),
- $this->getContainer()->get(AccountRecoverPasswordPageLoader::class),
- $this->getContainer()->get(SalesChannelContextService::class)
+ $this->getContainer()->get(AccountRecoverPasswordPageLoader::class)
);
$controller->setContainer($this->getContainer());
$controller->setTwig($this->getContainer()->get('twig'));
@@ -192,8 +189,7 @@ public function testAuthControllerLoginShowsRateLimit(): void
$loginRoute,
$this->createMock(AbstractLogoutRoute::class),
$this->getContainer()->get(StorefrontCartFacade::class),
- $this->getContainer()->get(AccountRecoverPasswordPageLoader::class),
- $this->getContainer()->get(SalesChannelContextService::class)
+ $this->getContainer()->get(AccountRecoverPasswordPageLoader::class)
);
$controller->setContainer($this->getContainer());
diff --git a/tests/integration/Storefront/Controller/RegisterControllerTest.php b/tests/integration/Storefront/Controller/RegisterControllerTest.php
index 2b42e133ea5..ddcf8dadc8e 100644
--- a/tests/integration/Storefront/Controller/RegisterControllerTest.php
+++ b/tests/integration/Storefront/Controller/RegisterControllerTest.php
@@ -25,6 +25,7 @@
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\Framework\Validation\DataBag\QueryDataBag;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
+use Shopware\Core\PlatformRequest;
use Shopware\Core\SalesChannelRequest;
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextFactory;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
@@ -55,10 +56,7 @@ class RegisterControllerTest extends TestCase
use MailTemplateTestBehaviour;
use StorefrontControllerTestBehaviour;
- /**
- * @var SalesChannelContext
- */
- private $salesChannelContext;
+ private SalesChannelContext $salesChannelContext;
protected function setUp(): void
{
@@ -115,6 +113,7 @@ public function testGuestRegisterWithRequirePasswordConfirmation(): void
static::assertEquals(200, $response->getStatusCode());
static::assertCount(1, $customers);
+ static::assertTrue($request->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT));
}
public function testGuestRegister(): void
@@ -130,6 +129,7 @@ public function testGuestRegister(): void
static::assertEquals(200, $response->getStatusCode());
static::assertCount(1, $customers);
+ static::assertTrue($request->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT));
}
public function testRegisterWithDoubleOptIn(): void
@@ -165,6 +165,8 @@ public function testRegisterWithDoubleOptIn(): void
$response = $registerController->register($request, $data, $this->salesChannelContext);
+ static::assertFalse($request->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT));
+
static::assertEquals(302, $response->getStatusCode());
static::assertInstanceOf(RedirectResponse::class, $response);
static::assertEquals('/account/register', $response->getTargetUrl());
@@ -217,6 +219,8 @@ public function testRegisterWithDoubleOptInDomainChanged(): void
$response = $registerController->register($request, $data, $this->salesChannelContext);
+ static::assertFalse($request->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT));
+
static::assertEquals(302, $response->getStatusCode());
static::assertInstanceOf(RedirectResponse::class, $response);
static::assertEquals('/account/register', $response->getTargetUrl());
@@ -275,6 +279,8 @@ public function testConfirmRegisterWithRedirectTo(): void
$registerController->register($request, $data, $this->salesChannelContext);
+ static::assertFalse($request->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT));
+
static::assertInstanceOf(CustomerDoubleOptInRegistrationEvent::class, $event);
$customer = $customerRepository->search(new Criteria([$event->getCustomer()->getId()]), $this->salesChannelContext->getContext())->getEntities();
@@ -285,6 +291,8 @@ public function testConfirmRegisterWithRedirectTo(): void
$response = $registerController->confirmRegistration($this->salesChannelContext, $queryData);
+ static::assertTrue($request->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT));
+
static::assertEquals(302, $response->getStatusCode());
static::assertInstanceOf(RedirectResponse::class, $response);
static::assertEquals('/checkout/confirm', $response->getTargetUrl());
diff --git a/tests/unit/Core/Framework/Adapter/Cache/Http/CacheResponseSubscriberTest.php b/tests/unit/Core/Framework/Adapter/Cache/Http/CacheResponseSubscriberTest.php
index 76a0aeb5cb0..35ee294384b 100644
--- a/tests/unit/Core/Framework/Adapter/Cache/Http/CacheResponseSubscriberTest.php
+++ b/tests/unit/Core/Framework/Adapter/Cache/Http/CacheResponseSubscriberTest.php
@@ -9,6 +9,7 @@
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Shopware\Core\Checkout\Customer\CustomerEntity;
+use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Adapter\Cache\Http\CacheResponseSubscriber;
use Shopware\Core\Framework\Event\BeforeSendResponseEvent;
@@ -52,6 +53,7 @@ public function testHasEvents(): void
['setResponseCacheHeader', 1500],
],
BeforeSendResponseEvent::class => 'updateCacheControlForBrowser',
+ CustomerLoginEvent::class => 'onCustomerLogin',
];
static::assertSame($expected, CacheResponseSubscriber::getSubscribedEvents());
@@ -64,6 +66,7 @@ public function testNoHeadersAreSetIfCacheIsDisabled(): void
100,
false,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -98,6 +101,7 @@ public function testNoAutoCacheControlHeader(): void
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -127,6 +131,7 @@ public function testNoAutoCacheControlHeaderCacheDisabled(): void
100,
false,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -156,6 +161,7 @@ public function testNoAutoCacheControlHeaderNoHttpCacheRoute(): void
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -189,6 +195,7 @@ public function testGenerateCashHashWithItemsInCart(?CustomerEntity $customer, C
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -270,6 +277,7 @@ public function testMaintenanceRequest(bool $active, array $whitelist, bool $sho
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ $requestStack,
false,
null,
null
@@ -308,6 +316,32 @@ public function testMaintenanceRequest(bool $active, array $whitelist, bool $sho
$subscriber->setResponseCache($event);
}
+ public function testOnCustomerLogin(): void
+ {
+ $requestStack = new RequestStack();
+
+ $subscriber = new CacheResponseSubscriber(
+ $this->createMock(CartService::class),
+ 100,
+ true,
+ new MaintenanceModeResolver(new EventDispatcher()),
+ $requestStack,
+ false,
+ null,
+ null
+ );
+
+ $salesChannelContext = $this->createMock(SalesChannelContext::class);
+
+ $request = new Request();
+ $requestStack->push($request);
+
+ $event = new CustomerLoginEvent($salesChannelContext, new CustomerEntity(), 'token');
+ $subscriber->onCustomerLogin($event);
+
+ static::assertSame($salesChannelContext, $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT));
+ }
+
/**
* @return array>
*/
@@ -352,6 +386,7 @@ public function testResponseHeaders(bool $reverseProxyEnabled, ?string $beforeHe
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
$reverseProxyEnabled,
null,
null
@@ -422,6 +457,7 @@ public function testAddHttpCacheToCoreRoutes(): void
1,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -442,6 +478,7 @@ public function testCurrencyChange(?string $currencyId): void
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -485,6 +522,7 @@ public function testStatesGetDeletedOnEmptyState(): void
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -517,6 +555,7 @@ public function testNotCacheablePages(Request $request): void
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -561,6 +600,7 @@ public function testNoCachingWhenInvalidateStateMatches(): void
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
@@ -597,6 +637,7 @@ public function testMakeGetsCached(): void
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
'5',
'6'
@@ -681,6 +722,7 @@ public function testSetResponseCacheOnLogin(
100,
true,
new MaintenanceModeResolver(new EventDispatcher()),
+ new RequestStack(),
false,
null,
null
diff --git a/tests/unit/Storefront/Controller/AuthControllerTest.php b/tests/unit/Storefront/Controller/AuthControllerTest.php
index f597b2b4faa..2ad67686c07 100644
--- a/tests/unit/Storefront/Controller/AuthControllerTest.php
+++ b/tests/unit/Storefront/Controller/AuthControllerTest.php
@@ -5,16 +5,11 @@
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
-use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLoginRoute;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLogoutRoute;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractResetPasswordRoute;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractSendPasswordRecoveryMailRoute;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
-use Shopware\Core\PlatformRequest;
-use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceInterface;
-use Shopware\Core\System\SalesChannel\ContextTokenResponse;
-use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\Test\Generator;
use Shopware\Storefront\Checkout\Cart\SalesChannel\StorefrontCartFacade;
use Shopware\Storefront\Controller\AuthController;
@@ -41,8 +36,6 @@ class AuthControllerTest extends TestCase
private MockObject&AbstractLoginRoute $loginRoute;
- private MockObject&SalesChannelContextServiceInterface $salesChannelContextService;
-
protected function setUp(): void
{
$this->accountLoginPageLoader = $this->createMock(AccountLoginPageLoader::class);
@@ -52,7 +45,6 @@ protected function setUp(): void
$logoutRoute = $this->createMock(AbstractLogoutRoute::class);
$cartFacade = $this->createMock(StorefrontCartFacade::class);
$recoverPasswordRoute = $this->createMock(AccountRecoverPasswordPageLoader::class);
- $this->salesChannelContextService = $this->createMock(SalesChannelContextServiceInterface::class);
$this->controller = new AuthControllerTestClass(
$this->accountLoginPageLoader,
@@ -62,7 +54,6 @@ protected function setUp(): void
$logoutRoute,
$cartFacade,
$recoverPasswordRoute,
- $this->salesChannelContextService,
);
$containerBuilder = new ContainerBuilder();
@@ -94,37 +85,6 @@ public function testAccountRegister(): void
static::assertInstanceOf(AccountLoginPageLoadedHook::class, $this->controller->calledHook);
}
- public function testLoginNewContextIsAdded(): void
- {
- $this->loginRoute
- ->method('login')
- ->willReturn(new ContextTokenResponse('context_token_response'));
-
- $newSalesChannelContext = Generator::createSalesChannelContext();
- $this->salesChannelContextService
- ->expects(static::once())
- ->method('get')
- ->willReturn($newSalesChannelContext);
-
- $oldSalesChannelContext = Generator::createSalesChannelContext();
- $oldSalesChannelContext->assign(['customer' => null]);
-
- $request = new Request();
- $request->attributes->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT, $oldSalesChannelContext);
-
- $response = $this->controller->login($request, new RequestDataBag(), $oldSalesChannelContext);
-
- /** @var SalesChannelContext $newSalesChannelContext */
- $newSalesChannelContext = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
- static::assertNotSame(
- $oldSalesChannelContext,
- $newSalesChannelContext,
- 'Sales Channel context should have been changed after login to update the states in cache'
- );
- static::assertInstanceOf(CustomerEntity::class, $newSalesChannelContext->getCustomer());
- static::assertSame(Response::HTTP_OK, $response->getStatusCode());
- }
-
public function testGuestLoginPageWithoutRedirectParametersRedirects(): void
{
$context = Generator::createSalesChannelContext();