Изменённое свойства в сущности отображается не сразу

verfaa

Профессор
Регистрация
29 Янв 2007
Сообщения
417
Реакции
49
Использую Symfony 6.3.0
На сайте есть тёмная тема и светлая тема.
Соответственно сущность User имеет свойство darkTheme = true || false
В профиле юзера есть настройки, в которых он может изменить тему на светлую или тёмную, кликнув на radio.
Написал для реализации контроллер

Код:
    /**
     * @Route(path="/settings", name=".settings")
     */
    public function settings(Request $request, Handler $handler): Response
    {
        $command = new Command($this->getUser()->getId(), $this->getUser()->getdark_theme());
        
        $form = $this->createForm(Form::class, $command);
        $form->handleRequest($request);
        
        if($form->isSubmitted() && $form->isValid()){
            try {
                $handler->handle($command);
                $this->addFlash("success", "Settings was successfully updated.");
            } catch (\DomainException $e) {
                $this->logger->warning($e->getMessage(), ['exception' => $e]);
                $this->addFlash("error", $e->getMessage());
            }
        }
    
        return $this->render("app/dashboard/user/settings.html.twig", [
            'form' => $form->createView(),
        ]);
    }

Form:
Код:
    class Form extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options): void
        {
            $builder
                ->add("dark_mode", ChoiceType::class, [
                    'choices' => [
                        'Dark Mode' => 1,
                        'Light Mode' => 0,
                    ],
                    'multiple' => false,
                    'expanded' => true,
                ]);
        }
    
        public function configureOptions(OptionsResolver $resolver): void
        {
            $resolver->setDefaults([
                'data_class' => Command::class,
            ]);
        }
    }

Handler, который сохраняет изменённую сущность в БД:
Код:
    class Handler
    {
        private UserRepository $users;
        private Flusher $flusher;
    
        public function __construct(UserRepository $users, Flusher $flusher)
        {
            $this->users = $users;
            $this->flusher = $flusher;
        }
    
        public function handle(Command $command): void
        {
            $user = $this->users->get($command->id);
    
            $user->setDarkTheme(
                (bool)$command->dark_mode
            );
    
            $this->flusher->flush();
        }
    }
И всё работает корректно - настройка меняется. Но проблема визуально состоит в том, что:
Например, у юзера установлена светлая тема и он желает изменить её на тёмную.
Он идёт в настройки, кликает на radio Dark Mode и кликает кнопку Submit. Происходит отправка формы, юзера редиректит на эту же страницу, показывается флеш-сообщение "Settings was successfully updated.". Но тема так и остаётся старой - светлой. А уж если затем перейти на любую другую страницу сайта - появляется уже тёмная тема.
Вопрос: почему так происходит? И как исправить это? (Что бы после сабмита формы сразу появлялась выбранная тема).
 
Проблема, о которой вы говорите, связана с тем, что после отправки формы и обновления настроек пользователя на сервере, вы не обновляете текущую сессию пользователя, чтобы отразить изменения темы. В результате пользователь видит изменения только после перезагрузки страницы или перехода на другую страницу.

Для того чтобы решить эту проблему, вам нужно обновить текущую сессию пользователя после успешной обработки формы. Вы можете сделать это следующим образом:

Код:
public function settings(Request $request, Handler $handler, SessionInterface $session): Response
{
    $command = new Command($this->getUser()->getId(), $this->getUser()->getdark_theme());
    
    $form = $this->createForm(Form::class, $command);
    $form->handleRequest($request);
    
    if ($form->isSubmitted() && $form->isValid()) {
        try {
            $handler->handle($command);
            // Обновление сессии пользователя с новой темой
            $this->getUser()->setDarkTheme((bool) $command->dark_mode);
            $session->set('_locale', $this->getUser());
            $this->addFlash("success", "Settings was successfully updated.");
        } catch (\DomainException $e) {
            $this->logger->warning($e->getMessage(), ['exception' => $e]);
            $this->addFlash("error", $e->getMessage());
        }
    }

    return $this->render("app/dashboard/user/settings.html.twig", [
        'form' => $form->createView(),
    ]);
}

Обратите внимание на строки:

Код:
$this->getUser()->setDarkTheme((bool) $command->dark_mode);
$session->set('_locale', $this->getUser());

Здесь мы обновляем сессию пользователя с новой настройкой темы и вручную обновляем данные пользователя в сессии. Это позволит пользователям видеть изменения в теме сразу же после отправки формы.
 
Комментатор выше прав,
Ваша проблема связана с тем, что после обновления настройки темы в базе данных, изменения не отражаются сразу же на текущей странице. Это происходит потому, что информация о теме, используемая в шаблоне, уже была загружена до того, как пользователь отправил форму. Поэтому, даже после успешного обновления настройки в базе данных, на текущей странице отображается старая тема.

Чтобы решить эту проблему, вы можете принудительно обновить страницу после успешного обновления настроек, или же обновить состояние темы в сессии (или там, где оно хранится для текущего пользователя) перед рендерингом страницы.
 

Возможные решения:​

1. Обновление объекта пользователя в сессии​

После изменения настройки темы можно обновить объект пользователя в сессии, чтобы изменения сразу применились. Добавьте следующее в ваш Handler, чтобы обновить объект пользователя в сессии:

Код:
php
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

class Handler
{
private UserRepository $users;
private Flusher $flusher;
private TokenStorageInterface $tokenStorage;

public function __construct(UserRepository $users, Flusher $flusher, TokenStorageInterface $tokenStorage)
{
$this->users = $users;
$this->flusher = $flusher;
$this->tokenStorage = $tokenStorage;
}

public function handle(Command $command): void
{
$user = $this->users->get($command->id);

$user->setDarkTheme((bool) $command->dark_mode);

$this->flusher->flush();

// Обновляем пользователя в сессии
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->tokenStorage->setToken($token);
}
}

Этот код сбросит данные пользователя в сессии с обновленными настройками, и изменения сразу отобразятся на текущей странице.

2. Использование AJAX для обновления темы без перезагрузки страницы​

Вы также можете сделать обработку формы с помощью AJAX. Это позволит применить тему сразу после успешного обновления, без необходимости перезагрузки страницы. Тогда после отправки формы можно сразу применить выбранную тему, используя JavaScript, без обновления всей страницы.

3. Переопределение темы на стороне Twig​

Если тема определяется непосредственно в шаблоне Twig, можно передавать darkTheme в контексте рендера. Например:

Код:
php
return $this->render("app/dashboard/user/settings.html.twig", [
'form' => $form->createView(),
'darkTheme' => $command->dark_mode,
]);

Тогда вы можете сразу применить соответствующую тему на текущей странице, основываясь на этом значении.
 
Назад
Сверху