Предыдущие части: 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, мы попадаем на перепутье.
Дело в том, что функция update() существует одновременно в двух состояниях. Когда мы нажали на ссылку c action=update, сначала мы должны увидеть форму редактирования. Мы еще ничего не отправляли из этой формы, однако обращение было к функции update(). Когда мы заполнили форму и отправили её, обращение снова будет к функции update(). Но на этот раз ей придут данные из формы, и их надо будет сохранить в базе.
Следовательно, функция update() должна отличать при каждом запросе, когда мы просто хотим увидеть форму, а когда мы уже прислали реальные данные из формы.
Сделать это можно путём проверки массива $_POST. Если он не пустой, значит форма была отправлена. Значит, можно записывать изменённые данные в базу. Если же $_POST пустой, значит пользователь только запрашивает страницу с формой и пока ничего не отправил.
Есть и третий вариант: данные из $_POST пришли, но сохранить в базу мы их не можем, потому что в них содержится какая-то ошибка. Например, одно из полей пустое. В этом случае мы должны снова показать пользователю форму, чтобы он заполнил данные правильно. И желательно также сообщить ему об ошибке.
Итого, вырисовывается такая схема действий:
- Если $_POST пустой, перейти на шаг 5
- Если в $_POST присланы неверные данные, перейти на шаг 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():
Cуть в том, чтобы заполнить модель данными из массива $_POST. Наша модель – это также массив. Чтоб не писать отдельно:
$model['title'] = $_POST['title'];
$model['content'] = $_POST['content'];
и т.д., мы просто перебираем ключи у модели и ищем такие же ключи в массиве данных. Если нашли – перегоняем значения в модель. Таким образом, функция load_model() универсально работает с любыми массивами.
Обратите внимание, что модель в параметре функции указана со знаком &, то есть это передача параметра по ссылке. По умолчанию массивы в PHP передаются по значению, что в данном случае нас не устраивает.
Затем составляем MySQL-инструкцию для обновления записи:
update note set date='...', title='...', content='...' where id=1
Здесь также присутствует функция quote(), в которую мы оборачиваем все значения из модели:
Её задача в том, чтобы сделать вставку безопасной: во-первых, все значения должны быть в апострофах (одинарных кавычках), во-вторых, эта же функция должна экранировать различные служебные символы внутри строк, чтобы исключить SQL-инъекции, но мы это пока не делаем в угоду простоте кода.
После обновления таблицы делаем перенаправление на главную страницу с помощью функции header(), и возвращаемся из функции. Ничего, кроме заголовков, в данном случае браузеру отправлять не нужно. Он всё равно сразу перейдёт на URL, указанный в перенаправлении.
Ну а если $_POST пустой, мы всё вышенаписанное пропускаем и просто возвращаем форму редактирования. Она будет содержаться в файле views/form.php, но её мы сделаем в следующий раз, так как там тоже не всё просто.
Читайте дальше: Вставка в форму данных из модели