[New routing] Refactoring account change password

This commit is contained in:
Loïc Frémont 2026-02-26 09:15:11 +01:00
parent 7a0c37fdbc
commit 274892c611
9 changed files with 197 additions and 0 deletions

View file

@ -53,6 +53,7 @@ sylius_core:
shop_cart_summary: false
shop_account: false
shop_account_address_book: false
shop_account_change_password: false
shop_account_order: false
shop_checkout: false
shop_order_thank_you: false

View file

@ -19,8 +19,11 @@ use Sylius\Bundle\ApiBundle\Attribute\ShopUserIdAware;
class ChangeShopUserPassword
{
public function __construct(
#[\SensitiveParameter]
public readonly string $newPassword,
#[\SensitiveParameter]
public readonly string $confirmNewPassword,
#[\SensitiveParameter]
public readonly string $currentPassword,
public readonly mixed $shopUserId,
) {

View file

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Command\Shop\Account;
/**
* @experimental
*/
class ChangeShopUserPassword
{
public function __construct(
#[\SensitiveParameter]
public readonly string $newPassword,
public readonly mixed $shopUserId,
) {
}
}

View file

@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\CommandHandler\Shop;
use Sylius\Bundle\CoreBundle\Command\Shop\Account\ChangeShopUserPassword;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Sylius\Component\User\Security\PasswordUpdaterInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Webmozart\Assert\Assert;
#[AsMessageHandler]
final readonly class ChangeShopUserPasswordHandler
{
public function __construct(
private PasswordUpdaterInterface $passwordUpdater,
private UserRepositoryInterface $userRepository,
) {
}
public function __invoke(ChangeShopUserPassword $command): void
{
/** @var ShopUserInterface|null $user */
$user = $this->userRepository->find($command->shopUserId);
Assert::notNull($user);
$user->setPlainPassword($command->newPassword);
$this->passwordUpdater->updatePassword($user);
}
}

View file

@ -19,10 +19,20 @@ use Sylius\Bundle\CoreBundle\CommandHandler\Admin\Account\SendResetPasswordEmail
use Sylius\Bundle\CoreBundle\CommandHandler\ResendOrderConfirmationEmailHandler;
use Sylius\Bundle\CoreBundle\CommandHandler\ResendShipmentConfirmationEmailHandler;
use Sylius\Bundle\CoreBundle\CommandHandler\Shop\Account\ResetPasswordHandler as ShopResetPasswordHandler;
use Sylius\Bundle\CoreBundle\CommandHandler\Shop\ChangeShopUserPasswordHandler;
return static function (ContainerConfigurator $container) {
$services = $container->services();
$services
->set('sylius.command_handler.shop.account.change_password', ChangeShopUserPasswordHandler::class)
->args([
service('sylius.security.password_updater'),
service('sylius.repository.shop_user'),
])
->tag('messenger.message_handler', ['bus' => 'sylius.command_bus'])
;
$services
->set('sylius.command_handler.admin.account.request_reset_password_email', RequestResetPasswordEmailHandler::class)
->args([

View file

@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ShopBundle\Controller;
use Sylius\Bundle\CoreBundle\Command\Shop\Account\ChangeShopUserPassword;
use Sylius\Bundle\CoreBundle\Provider\FlashBagProvider;
use Sylius\Bundle\ShopBundle\Form\Type\UserChangePasswordType;
use Sylius\Bundle\UserBundle\Form\Model\ChangePassword;
use Sylius\Component\User\Model\UserInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Twig\Environment;
use Webmozart\Assert\Assert;
/**
* @experimental
*/
final readonly class ChangePasswordAction
{
public function __construct(
private Environment $twig,
private TokenStorageInterface $tokenStorage,
private FormFactoryInterface $formFactory,
private MessageBusInterface $messageBus,
private RouterInterface $router,
private RequestStack $requestStack,
) {
}
public function __invoke(Request $request, ?string $formType = null, ?string $template = null, ?string $redirect = null): Response
{
$template ??= '@SyliusShop/account/change_password.html.twig';
$formType ??= UserChangePasswordType::class;
$redirect ??= 'sylius_shop_account_dashboard';
$user = $this->getUser();
$changePassword = new ChangePassword();
$form = $this->formFactory->create($formType, $changePassword);
if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->handleRequest($request)->isSubmitted() && $form->isValid()) {
$this->messageBus->dispatch(new ChangeShopUserPassword($changePassword->getNewPassword(), $user->getId()));
$this->addSuccessNotification();
return new RedirectResponse($this->router->generate($redirect));
}
return new Response($this->twig->render($template, [
'form' => $form->createView(),
]));
}
private function getUser(): UserInterface
{
$user = $this->tokenStorage->getToken()?->getUser();
if (null === $user) {
throw new AccessDeniedException('You have to be registered user to access this section.');
}
Assert::isInstanceOf($user, UserInterface::class);
return $user;
}
private function addSuccessNotification(): void
{
FlashBagProvider::getFlashBag($this->requestStack)->add('success', 'sylius.user.change_password');
}
}

View file

@ -55,9 +55,16 @@ sylius_shop_account_profile_update:
sylius_shop_account_change_password:
path: /change-password
methods: [GET, POST]
condition: "context.isSyliusRoutingBcLayerEnabled('shop_account_change_password')"
defaults:
_controller: sylius.controller.shop_user::changePasswordAction
_sylius:
form: Sylius\Bundle\ShopBundle\Form\Type\UserChangePasswordType
template: "@SyliusShop/account/change_password.html.twig"
redirect: sylius_shop_account_dashboard
_sylius_shop_account_change_password:
path: /change-password
methods: [GET, POST]
condition: "not context.isSyliusRoutingBcLayerEnabled('shop_account_change_password')"
controller: sylius_shop.controller.change_password

View file

@ -15,6 +15,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Sylius\Bundle\ShopBundle\Controller\CartCheckoutAction;
use Sylius\Bundle\ShopBundle\Controller\CartSummaryAction;
use Sylius\Bundle\ShopBundle\Controller\ChangePasswordAction;
use Sylius\Bundle\ShopBundle\Controller\ContactController;
use Sylius\Bundle\ShopBundle\Controller\CurrencySwitchController;
use Sylius\Bundle\ShopBundle\Controller\LocaleSwitchController;
@ -54,6 +55,19 @@ return static function (ContainerConfigurator $container) {
->tag('controller.service_arguments')
;
$services
->set('sylius_shop.controller.change_password', ChangePasswordAction::class)
->args([
service('twig'),
service('security.token_storage'),
service('form.factory'),
service('sylius.command_bus'),
service('router.default'),
service('request_stack')
])
->tag('controller.service_arguments')
;
$services
->set('sylius_shop.controller.contact', ContactController::class)
->args([

View file

@ -36,8 +36,11 @@ use Webmozart\Assert\Assert;
class UserController extends ResourceController
{
/** @deprecated This method is deprecated and will be removed in Sylius 3.0 */
public function changePasswordAction(Request $request): Response
{
trigger_deprecation('sylius/user-bundle', '2.3', '"%s" method is deprecated and will be removed in Sylius 3.0', __METHOD__);
$configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
if (!$this->container->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_REMEMBERED')) {