Найти тему
ZDG

Разрабатываем сайт #7: Действие Update

Предыдущие части: CRUD, Форма редактирования, PHP и база данных, Идентификаторы, Работаем с таблицами, Создаём базу данных, Установка MySQL

Читайте также: Как работает веб-сайт, Бэкэнд на PHP, Введение в язык PHP

В предыдущей части мы сделали CRUD-контроллер, который умеет выводить список записей в виде таблице. В этой части мы запрограммируем действие update. Хотя там всего несколько строчек, его логика не так проста, поэтому будем разбираться.

Ссылка для редактирования записи выглядит так:

/controllers/note.php?action=update&id=1

Где id это идентификатор записи в базе. Следовательно, с помощью этой ссылки мы обращаемся к контроллеру note.php и в GET-запросе, то есть прямо в URL, передаём ему два параметра: action=update и id=1.

В контроллере в предыдущей части уже была написана обработка этих параметров. Получив action=update, он вызовет функцию update(), которая ещё не написана. Вот сейчас мы её и напишем.

Итак, функция update() на вход получает два параметра: $connection – соединение с базой и $id – идентификатор записи, которую надо обновить.

Очевидно, первое, что должна сделать функция – убедиться, что запись с таким id существует в базе. Если вдруг запись не нашлась, нужно предпринять какие-то действия – например, вернуть в браузер сообщение об ошибке. В крайнем случае можно просто "умереть", сообщив напоследок что-нибудь:

die("Error: $id not found");

Функция die() немедленно прекращает выполнение скрипта и пишет простенькое текстовое сообщение. Это не красивая страница с описанием ошибки, но пока сойдёт и так.

Благополучно получив из базы запись с нужным id, мы попадаем на перепутье.

-2

Дело в том, что функция update() существует одновременно в двух состояниях. Когда мы нажали на ссылку c action=update, сначала мы должны увидеть форму редактирования. Мы еще ничего не отправляли из этой формы, однако обращение было к функции update(). Когда мы заполнили форму и отправили её, обращение снова будет к функции update(). Но на этот раз ей придут данные из формы, и их надо будет сохранить в базе.

Следовательно, функция update() должна отличать при каждом запросе, когда мы просто хотим увидеть форму, а когда мы уже прислали реальные данные из формы.

Сделать это можно путём проверки массива $_POST. Если он не пустой, значит форма была отправлена. Значит, можно записывать изменённые данные в базу. Если же $_POST пустой, значит пользователь только запрашивает страницу с формой и пока ничего не отправил.

Есть и третий вариант: данные из $_POST пришли, но сохранить в базу мы их не можем, потому что в них содержится какая-то ошибка. Например, одно из полей пустое. В этом случае мы должны снова показать пользователю форму, чтобы он заполнил данные правильно. И желательно также сообщить ему об ошибке.

Итого, вырисовывается такая схема действий:

  1. Если $_POST пустой, перейти на шаг 5
  2. Если в $_POST присланы неверные данные, перейти на шаг 5
  3. Сохранить данные в базе
  4. Сделать перенаправление на страницу результата
  5. Отправить пользователю страницу с формой редактирования

Вроде как всё ясно, но вопрос вызывает пункт 4. Что значит "перенаправление на страницу результата"?

Сама страница результата – это просто отдельная страница, увидев которую, пользователь поймёт, что редактирование записи прошло успешно. Её можно оформить разными способами: просто написать, что всё ок, или вывести общий список записей, или вывести только что добавленную запись, или вывести снова форму для редактирования, только сделать пометку, что запись была сохранена.

В общем, как угодно, и как удобней/понятней пользователю. Мы будем показывать общий список записей, исходя из логики: отредактировал одну запись – вернулся к списку, можешь выбирать следующую.

Что значит "перенаправление"?

У нас в контроллере есть функция render(), с помощью которой мы можем отдать в браузер любое представление. Так, находясь в функции update(), мы можем вызвать render() с параметром 'form', чтобы вернуть страницу с формой, или 'index', чтобы вернуть главную страницу (с общим списком).

Получается, что браузер, запросив один и тот же URL, может в ответ получить совершенно разные страницы – или форму редактирования, или главную.

В этом нет ничего криминального, но есть неудобства. Во-первых, если вы обновили запись и вам вернулся список, адресная строка в браузере будет показывать URL типа ?action=update&id=1, а видеть вы будете главную страницу. Это может ввести в заблуждение, и также результат не воспроизводится, если эту сссылку отправить кому-нибудь.

Второе неудобство – последствия POST-запроса. Когда вы отправили данные формы, и браузер получил в ответ главную страницу, вы например захотите обновить её. Но браузер-то думает, что он отправил POST-запрос и получил результат этого запроса. Поэтому, когда вы нажмёте F5, браузеру придётся отправить POST-запрос ещё раз, ведь он думает, что результат можно получить только так. О чём вас и предупредит.

Перенаправление решает обе проблемы. Это специальный заголовок HTTP-ответа, который сообщает браузеру: ты обратился к URL, но чтобы получить ответ, тебе надо обратиться к другому URL...

И браузер просто обращается к новому URL и получает ответ оттуда. Что это нам даёт:

Мы отправили из формы POST-запрос с данными, и не возвращаем страницу со списком непосредственно, а делаем перенаправление на другой URL, по которому существует эта страница. Браузер делает новый запрос на тот URL и получает то, что надо – страницу со списком. Но теперь URL в адресной строке совпадает с содержимым страницы, и данные из прошлого POST-запроса уже никак не влияют на этот запрос – можно обновлять страницу сколько угодно.

Ну и после всех этих объяснений вот, наконец, наша функция update():

Найдите ошибку :)
Найдите ошибку :)

Функция find_model() ищет в базе запись (будем называть её модель в терминологии MVC) с указанным $id. Код был опубликован выше. В случае неудачи функция убьёт весь скрипт с помощью die(), так что результат не проверяем. Если живы, значит, модель найдена.

Далее, если $_POST не пустой, мы попадаем в состояние обновления данных. Вызываем функцию load_model():

-4

Cуть в том, чтобы заполнить модель данными из массива $_POST. Наша модель – это также массив. Чтоб не писать отдельно:

$model['title'] = $_POST['title'];
$model['content'] = $_POST['content'];

и т.д., мы просто перебираем ключи у модели и ищем такие же ключи в массиве данных. Если нашли – перегоняем значения в модель. Таким образом, функция load_model() универсально работает с любыми массивами.

Обратите внимание, что модель в параметре функции указана со знаком &, то есть это передача параметра по ссылке. По умолчанию массивы в PHP передаются по значению, что в данном случае нас не устраивает.

Затем составляем MySQL-инструкцию для обновления записи:

update note set date='...', title='...', content='...' where id=1

Здесь также присутствует функция quote(), в которую мы оборачиваем все значения из модели:

-5

Её задача в том, чтобы сделать вставку безопасной: во-первых, все значения должны быть в апострофах (одинарных кавычках), во-вторых, эта же функция должна экранировать различные служебные символы внутри строк, чтобы исключить SQL-инъекции, но мы это пока не делаем в угоду простоте кода.

После обновления таблицы делаем перенаправление на главную страницу с помощью функции header(), и возвращаемся из функции. Ничего, кроме заголовков, в данном случае браузеру отправлять не нужно. Он всё равно сразу перейдёт на URL, указанный в перенаправлении.

Ну а если $_POST пустой, мы всё вышенаписанное пропускаем и просто возвращаем форму редактирования. Она будет содержаться в файле views/form.php, но её мы сделаем в следующий раз, так как там тоже не всё просто.

Читайте дальше: Вставка в форму данных из модели