Передача данных после HTTP-заголовка Location

Информирование пользователя о результатах его действия на сайте - вещь очень полезная. Это создаёт дружественный интерфейс и более ясную картину действий, которые совершила система после запросов пользователя. Давайте рассмотрим несколько ситуаций, когда нам нужно сделать обработку входящего запроса (не важно, POST или GET), а после чего показать пользователю какой-то текст, информирующий его о результатах.

Предлагаю расмотреть вполне реальный пример - добавления объявления на сайт бесплатных объявлений.

Пользователь ввёл свои данные в поля ввода, нажал кнопку "сохранить". Если ошибок нет, мы обычно делаем переадрессацию Header('Location: ...') на какой-то адрес, что бы в случае, если пользователь случайно или умышленно нажмёт кнопку "обновить" в браузере, не совершился повторный аналогичный POST-запрос. Предположим, что мы делаем переадресацию на страницу только что добавленного объявления, на адрес /advert/123.html (123 в данном случае - ID только что созданного объявления). При использовании преадресации возникает вопрос - как на адресуемой странице показать какие-либо данные, например, текст сообщения информирующий пользователя о том, что данные сохранены и все ок?

Первая мысль, что приходит в голову - передавать в QUERY_STRING некий параметр, на основании которого мы могли бы показать на странице текст:

<?php
// Скрипт добавления объявления.
// Тут мы записали объявление в базу данных и делаем переадресацию
// на страницу только что созданного объявления
header ("Location: /advert/$id.html?result=success");
exit;
?>

Соответственно, в шаблоне страницы показа объявлений пишем:

...
<body>
<? if (isset($_GET['result']) && $_GET['result'] == 'success'): ?>
    <p>Данные сохранены</p>
<? endif; ?>
</body>
...

Что в итоге получилось? В QUERY_STRING висит конструкция, при наличии которой всегда будет отображаться текст "Данные сохранены". Т.е. если пользователь скопирует адрес http://server.com/advert/123.html?result=success и вставит его в свой блог для саморекламы, то все посетители страницы будут видеть текст "Данные сохранены". Это первый минус.

Второй минус - это то, что в шаблоне страницы объявления мы должны жёстко зашивать текст, привязывая его к GET-переменной result. Давайте на минуту представим, что на странице объявления присутствует ссылка "Поднять объявление в результатах поиска", после нажатия на которую мы опять должны вывести сообщение, информирующее пользователя о совершённом действии. Получается, что в шаблоне появляется ещё одна конструкция:

...
<body>
<? if (isset($_GET['result']): ?>
    <? if ($_GET['result'] == 'success')): ?>
        <p>Данные сохранены</p>
    <? elseif ($_GET['result'] == 'moved_up'): ?>
        <p>Ваше объявление поднято в результатах поиска</p>
    <? endif; ?>
<? endif; ?>
</body>
...

Всё это ужасно не красиво. В шаблоне появилось if-else ветвление, а в строке адреса висят параметры, при наличии которых всегда будет отображаться текст, предназначенный только для владельца объявления.

Решение проблемы

Решение проблемы заключается в следующем: каждый раз, когда нам нужно показать пользователю какой-то текст после Location, необходимо сначала записать этот текст в базу данных, а в URL-адресе страницы, на которую произойдёт переход, поставить идентификатор этого сообщения:

<?php
// Скрипт добавления объявления.
// Записали объявление в базу данных и получили идентификатор объявления ($id). 
// Записали в базу данных текст, который необходимо отобразить после переадресации
// и получили идентификатор этой записи ($id_notif).
// Делаем переадресацию, указав в QUERY_STRING идентификатор записи $id_notif: 
header ("Location: /advert/$id.html?notif=$id_notif");
exit;
?>

При открытии страницы с объявлением мы в контроллере страницы смотрим, имеется ли в QUERY_STRING переменная $_GET['notif']. Если переменная определена, мы:

  1. получаем из СУБД запись с этим ключом и помещаем её в переменную для последующего вывода в шаблоне
  2. сразу же удаляем из базы эту запись - она нам больше не нужна

В шаблоне страницы мы просто выводим текст записи, полученной по ключу $_GET['notif']:

...
<body>
<? if ($notification): ?>
    <p><?=$notification?></p>
<? endif; ?>
</body>
...

Всё! Теперь мы можем на одной странице выводить хоть сто различных системных сообщений - шаблон остается как есть, без ветвлений и привязкам к различным именам переменных, как в примерах выше. Если же пользователь во второй раз откроет страницу с GET-переменной notif в QUERY_STRING, то системное уведомление просто не отобразится, т.к. было удалено из базы при первом обращении.