Форум программистов, компьютерный форум, киберфорум
PHP для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 5.00/18: Рейтинг темы: голосов - 18, средняя оценка - 5.00
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,098

Как при обновлении страницы избежать повторной отправки POST запроса с передачей переменных в шаблон?

23.06.2020, 12:23. Показов 4108. Ответов 24
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть страничка юзера, на ней форма изменения роли(админ/юзер). Реализовано на радиокнопках и кнопке подтверждения выбора.
При всех вариантах событий на страничку должно выводиться сообщение об успешной или неудачной операции.

Видел варианты - переадресовывать на ту же страничку. но я тогда не знаю, как правильно передавать сообщения в шаблон.
Пробовал делать unset, вижу. что при обновлении странички уже нет переменной $_POST['role'], но условие if(isset($_POST['role'])) не срабатывает
Сам обработчик:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
     * @param int $id
     * @throws DbException
     */
    public function roleControl(int $id)
    {
        $userById = User::getById($id);
        if (!empty($_POST)){
            if(isset($_POST['role'])){
                if ($userById->getRole() === $_POST['role']){
                    $success = 'This user role is already set as "'.$_POST['role'].'"';
                    unset($_POST['role']);
                    $this->view->renderHtml('users/user.php', ['changeResult' => $success, 'userById' => $userById,]);
                    return;
                }
                $role = $userById->changeAndSaveRole();
                $success = 'This user role is successfully changed to "'.$role.'"';
                unset($_POST['role']);
                $this->view->renderHtml('users/user.php', ['changeResult' => $success, 'userById' => $userById,]);
                return;
            }
            $success = 'You haven\'t choosen role option';
            $this->view->renderHtml('users/user.php', ['changeResult' => $success, 'userById' => $userById,]);
            return;
        }
    }
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
23.06.2020, 12:23
Ответы с готовыми решениями:

Повторение POST запроса при обновлении страницы
Доброе время суток, может кто-нибудь подсказать можно ли избавиться от повторного отправления POST запроса при обновлении страницы ? Тоесть...

Запретить повторную отправку POST запроса при обновлении страницы
Вопрос немного нубский... Но есть страница с формой, юзер вводит какие то данные, и при нажатии на кнопку происходит отправка POST...

Как избежать повторной отправки формы?
Пробовала после получения данных формы и их обработки header('Location: /file.php'); но выдает ошибку - Cannot modify header information -...

24
 Аватар для sad67man
2605 / 1509 / 689
Регистрация: 23.08.2015
Сообщений: 3,841
24.06.2020, 15:39
Лучший ответ Сообщение было отмечено СергейСереб как решение

Решение

Студворк — интернет-сервис помощи студентам
СергейСереб, Сообщения можно передавать через сессии, как вам выше уже написали. Это так называемые флеш-сообщения. Суть в том, что мы по определенному ключу сохраняем сообщение, а при получении его удаляем. Таки образом оно выведется только один раз. Если вы будете сообщения передавать через get параметры, то они будут выводиться при обновлении страницы. Что не очень хорошо, особенно, если этой ссылкой будут делиться.
Для удобства и повторного использования можно вынести в функции/хэлперы.
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Flasher
{
    public static function set($key, $message): void
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start(); // на всякий случай включаем сессию, его она еще не запущена.
        }
 
        $_SESSION['flash'][$key] = $message;
    }
 
    public static function get($key): string
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
 
        if (empty($_SESSION['flash'][$key])) {
            return '';
        }
 
        $message = $_SESSION['flash'][$key];
        unset($_SESSION['flash'][$key]);
 
        return $message;
    }
}
Далее при выводе ошибки - в форму нужно подставлять уже заполненные данные. Для этого можно использовать суперглобальный массив $_POST. Но это не очень удобно. Так как могут посыпаться нотисы или нужно тогда делать кучу проверок на isset посреди html шаблона. В данном случае контроллер должен принять данные с поста, их обработать, а потом уже использовать свою структуру данных.

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class UserRoleForm
{
    public $role;
 
    private $errors = [];
 
    public function __construct(User $user)
    {
        $this->role = $user->getRole();
    }
 
    public function load(array $data)
    {
        $this->role = $data['role'] ?? null;
    }
 
    public function validate(): bool
    {
        if (empty($this->role)) {
            $this->addError('role', 'Укажите роль');
        }
 
        return empty($this->errors);
    }
 
    public function addError($name, $message)
    {
        $this->errors[$name] = $message;
    }
 
    public function getError($name)
    {
        return $this->errors[$name] ?? null;
    }
 
    public function getErrors()
    {
        return $this->errors;
    }
}
Обратите внимание, что класс формы внутри не использует суперглобальные массивы, а использует только то, что в него передали, он не завязан на приложении, что удобно при написании тестов. А методы getError можно использовать в шаблоне, если хотим подсветить ошибки под каждым полем. Но это простые проверки, которые не лезут в БД.

Далее, смена роли - это операция, а операции принято делать таким образом, что она либо выполняется полностью, либо кидает исключение. Для этого выделяют отдельный Операционный слой (отдельный класс).

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class UserManageService
{
    private $users;
 
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }
 
    public function create(...): User
    {
        ...
    }
 
    public function edit(...): void
    {
        ...
    }
 
    public function changeRole($id, UserRoleForm $form)
    {
        $user = $this->users->getById($id);
        $user->changeRole($form->role);
        $this->users->save($user);
    }
}
И контроллер уже будет выглядеть таким образом.
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class UserController
{
    private $service;
 
    public function __construct(UserManageService $service)
    {
        $this->service = $service;
    }
 
    public function changeRole($id)
    {
        if ($user = User::getById($id)) {
            http_response_code(404);
            die();
        }
 
        $roleForm = new UserRoleForm($user);
 
        if (!empty($_POST)) {
            $roleForm->load($_POST);
 
            if ($roleForm->validate()) {
                try {
                    $this->service->changeRole($user->id, $roleForm);
                    Flasher::set('success', 'Роль успешно изменена');
                    return $this->refresh();
                } catch (DomainException $e) {
                    Flasher::set('error', $e->getMessage());
                } catch (RuntimeException $e) {
                    $this->logger->log($e->getMessage());
                    Flasher::set('error', 'Техническая ошибка');
                }
            }
        }
 
        return $this->view->renderHtml('users/role.php', ['user' => $user, 'roleForm' => $roleForm]);
    }
}
Исключения удобны тем, что их можно выкинуть из любой вложенности вызываемых методов. В данном случае исключения DomainException (исключение из предметной области) будут выкидываться из методов getById репозитория (в случае, если пользователь не найден.) И из метода changeRole(), если роль передана некорректная, либо такая роль уже установлена. А исключение RuntimeException выкидывается из метода save() на случай если что-то пошло не так, при этом логируем ошибку и не выводим ее пользователю.

В данном случае, я использовал некую абстрактную коллекцию Объектов
UserRepository, чтоб не заморачиваться с БД. Кстати это одна из основных ее функций, чтоб мы не отвлекались от логики предметоной области. Сейчас ее особо нет. Но и запросы могут быть сложными и методы операционного слоя могут быть большими.

Вывод сообщений в шаблоне
PHP/HTML
1
2
3
4
5
6
7
<?php if ($successMessage = Flasher::get('success')): ?>
    <p style="color: green"><?= $successMessage ?></p>
<?php endif ?>
 
<?php if ($errorMessage = Flasher::get('error')): ?>
    <p style="color: red"><?= $errorMessage ?></p>
<?php endif ?>
Его лучше добавить в общий шаблон, чтоб он выводился на всех страницах. Тогда, мы хоть рефрешим, хоть редиректим на другие страницы - сообщения не потеряются.
1
1460 / 1013 / 232
Регистрация: 01.10.2018
Сообщений: 3,948
24.06.2020, 19:01
Цитата Сообщение от СергейСереб Посмотреть сообщение
Я уже не первый раз слышу термин "приложение" в контексте веб-разработки. А как это выглядит технически?
Грубо говоря, это отдельный сайт. Но может быть несколько точек входа (относящихся к разным приложениям) и в рамках одного сайта, запросы по которым обычно распределяет Web-сервер. (Мультисайтовые приложение тоже бывают: это когда сайтов много, но все они управляются централизованно одним приложением.)

Цитата Сообщение от СергейСереб Посмотреть сообщение
И почему/для чего отдельно?
Для гибкости и простоты (принцип проектирования KISS, слабая связанность для упрощения переноса между разными платформами/полного переписывания и т.д.). Web-приложения - это уже давно не только сайты с браузерами. Например, я могу управлять сайтом (т.е. его лицевой частью) из командной строки, какой-то "нестандартной"/сторонней оболочки и т.д. Или наоборот управлять работой какого-то "нестандартного"/стороннего аппаратного или программного клиента из админки в браузере (причем ее пользовательский интерфейс может формироваться динамически как на сервере, так и на клиенте).

Цитата Сообщение от СергейСереб Посмотреть сообщение
У меня есть общий контроллер, от которого наследуются
Я уже ранее замечал, что вы немного путаетесь в иерархиях вызовов и наследования. Наследование меня не сильно интересует. Оно не эквивалентно цепочке вызовов. Если вы хотите ее реализовать на PHP при помощи наследования, вы все равно должны делать явные вызовы

Цитата Сообщение от СергейСереб Посмотреть сообщение
В конструктора админконтроллера(где собраны методы, предполагающие управления действиями в админке) я делаю проверку
Одного "админконтроллера" может быть недостаточно Т.е. в этом случае нужно делать проверки в разных конструкторах или вызывать/наследовать родительский конструктор. А главное зачем создавать массивный объект, который будет бесполезен при неавторизованных запросах? Проверка авторизации во фронт-контроллере в этом отношении предпочтительнее.
1
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,098
24.06.2020, 22:29  [ТС]
sad67man, Более полный, подробный, налядный ответ с примерами представить сложно) С флеш-сообщениями понравилась идея. супер. Сессиями я пользовался раньше для вывода сообщений, но в другом контексте. А тут так все четко, аккуратно организовано в ООП стиле!
Цитата Сообщение от sad67man Посмотреть сообщение
нужно тогда делать кучу проверок на isset посреди html шаблона.
Именно так и есть))
Цитата Сообщение от estic Посмотреть сообщение
Для гибкости и простоты (принцип проектирования KISS, слабая связанность для упрощения переноса между разными платформами/полного переписывания и т.д.). Web-приложения - это уже давно не только сайты с браузерами. Например, я могу управлять сайтом (т.е. его лицевой частью) из командной строки, какой-то "нестандартной"/сторонней оболочки и т.д. Или наоборот управлять работой какого-то "нестандартного"/стороннего аппаратного или программного клиента из админки в браузере (причем ее пользовательский интерфейс может формироваться динамически как на сервере, так и на клиенте).
Понятно. Я до этого еще не дорос)
Цитата Сообщение от estic Посмотреть сообщение
вы немного путаетесь в иерархиях вызовов и наследования. Наследование меня не сильно интересует. Оно не эквивалентно цепочке вызовов. Если вы хотите ее реализовать на PHP при помощи наследования, вы все равно должны делать явные вызовы
Я может выражаюсь не совсем внятно. И в целом я уже привык к имеющейся архитектуре, поэтому в некоторые нюансы непривычной архитектуры из примеров или предложений решить тот или иной вопрос - долго въезжаю, пока разберу и примерю на свою архитектуру..
Спасибо большое всем за помощь!
0
 Аватар для sad67man
2605 / 1509 / 689
Регистрация: 23.08.2015
Сообщений: 3,841
26.06.2020, 20:07
СергейСереб, Хочу подкинуть такую тему для размышления. Вообще, если говорить о суперглобальных переменных, таких как $_POST, то тут есть один интересный момент. К примеру некоторые начинающие при постройке своего MVC приложения в точке входа пишут что-то типа такого

PHP
1
2
3
4
require '../vendor/autoload.php';
 
$contoller = new Controller();
$controller->run();
Исходят из того, что все знают что такое MVC, однако забывают отразить основную функцию приложения и вообще о принципах ООП. Ведь если подумать, то нам приходит некий запрос (Request), и мы должны отдать некий ответ (Response). Т.е. точка входа должна выглядеть примерно так.

PHP
1
2
3
4
5
6
7
8
9
require '../vendor/autoload.php';
$container = reuire '../config/container.php';
 
$app = $container->get(Application::class);
$response = $app->run(new Request($_GET, $_POST, $_SERVER, $_FILES ...));
 
//далее мы должны как-то обработать ответ
$response->getHeaders();
$response->getBody();
Смотрите какая штука, что данный подход позволяет нам вручную задать данные, вызвать несколько запросов подряд, получив разные ответы.

PHP
1
2
3
4
5
$app = new Application();
$responce = $app->run(new Request([...], [...], [...], [...], ...));
$responce = $app->run(new Request([...], [...], [...], [...], ...));
$responce = $app->run(new Request(['action' => '/users/role', 'id' => 5], ['role' => 'admin'], [...], [...], ...));
//...
О чем это нам говорит? Что мы в принципе не должны дергать суперглобальные массивы внутри нашего приложения. А доставать данные из объекта Request.

Т.е. уже action контроллера уже будет выглядеть примерно так. (Код написан схематически чисто для примера.)

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class UserController
{
    private $service;
 
    public function __construct(UserManageService $service)
    {
        $this->service = $service;
    }
 
    public function changeRole(Request $request): Response
    {
        if ($user = User::getById($request->get('id'))) {
            return new NotFoundResponse();
        }
 
        $roleForm = new UserRoleForm($user);
 
        if ($request->isPost()) {
            $roleForm->load($request->post());
 
            if ($roleForm->validate()) {
                try {
                    $this->service->changeRole($user->id, $roleForm);
                    Flasher::set('success', 'Роль успешно изменена');
                    return $this->refresh();
                } catch (DomainException $e) {
                    Flasher::set('error', $e->getMessage());
                } catch (RuntimeException $e) {
                    $this->logger->log($e->getMessage());
                    Flasher::set('error', 'Техническая ошибка');
                }
            }
        }
 
        return new HtmlResponse($this->view->renderHtml('users/role.php', ['user' => $user, 'roleForm' => $roleForm]));
    }
}
К чему это все я?) Дело в том, что в принципе обращаться к суперглобальным переменным, причем где угодно - не очень хороший подход. К примеру в html шаблон всегда должны приходить уже готовые данные для вывода. Так же нельзя к ним обращаться внутри сущности/модели и т.д. Да и вообще внутри какого-либо класса по возможности нужно избегать обращение к данным из "внешней среды". Чтоб класс был более чистым нужно стараться использовать только те данные, которые приходят в конструктор, либо в методы.
1
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,098
26.06.2020, 21:46  [ТС]
Цитата Сообщение от sad67man Посмотреть сообщение
Хочу подкинуть такую тему для размышления.
Спасибо. Тема правильная.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
26.06.2020, 21:46

Как избежать добавление в БД при обновлении страницы?
Доброго всем времени суток Проблема соответствует заголовку темы Код в катором происходит автоматическое добовление данных к БД...

Защита от повторной отправки POST-данных
браузер кэширует _POST. Если рефрешнуть то пост данные остануться, а значит сработает код еще раз. Надо после того, как 1 рах отправили...

Повторный POST при обновлении страницы
Возникла такая проблема: Есть форма, которая добавляет данные в mysql, после чего чуть ниже они(данные) и выводятся... Всё работает,...

Чтение данных с XML, как избежать повторной операции при следующей загрузке формы
Допустим есть такой код который Работает после загрузки второй формы. он считывает хмл файл в AutoCompleteMode. Так это довольно...

После повторной отправки Get-запроса и парсера выдается предыдущий результат
Есть IDHTTP,проходит процесс авторизации,далее идет get запрос, а с него идет парсер входящих сообщений,короче спарсил я...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
25
Ответ Создать тему
Новые блоги и статьи
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru