HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Формы, которые изменяют данные, устроены сложнее как с клиентской стороны, так и с серверной. Для уверенной работы с ними необходимо разбираться в следующих вопросах:</p>
1 <p>Формы, которые изменяют данные, устроены сложнее как с клиентской стороны, так и с серверной. Для уверенной работы с ними необходимо разбираться в следующих вопросах:</p>
2 <ul><li>Знание соответствующих HTML-тегов</li>
2 <ul><li>Знание соответствующих HTML-тегов</li>
3 <li>Понимание того, как отправляются формы по HTTP</li>
3 <li>Понимание того, как отправляются формы по HTTP</li>
4 <li>Обработка на стороне сервера</li>
4 <li>Обработка на стороне сервера</li>
5 <li>Валидация и вывод ошибок</li>
5 <li>Валидация и вывод ошибок</li>
6 </ul><p>Начнем с того, что за вывод формы и ее обработку должны отвечать два разных обработчика, значит, это разные маршруты. Ниже пример маршрутов для создания нового пользователя:</p>
6 </ul><p>Начнем с того, что за вывод формы и ее обработку должны отвечать два разных обработчика, значит, это разные маршруты. Ниже пример маршрутов для создания нового пользователя:</p>
7 <ul><li>GET<em>/users/new</em>- страница с формой, которую заполняет пользователь. Эта форма отправляет POST-запрос на адрес<em>/users</em>, указанный в атрибуте action</li>
7 <ul><li>GET<em>/users/new</em>- страница с формой, которую заполняет пользователь. Эта форма отправляет POST-запрос на адрес<em>/users</em>, указанный в атрибуте action</li>
8 <li>POST<em>/users</em>- маршрут, обрабатывающий данные формы</li>
8 <li>POST<em>/users</em>- маршрут, обрабатывающий данные формы</li>
9 </ul><p>Подобная схема именования<a>рекомендуется</a>и автоматически создается многими фреймворками, такими как Rails. Она хорошо ложится на REST-архитектуру, о которой мы еще поговорим.</p>
9 </ul><p>Подобная схема именования<a>рекомендуется</a>и автоматически создается многими фреймворками, такими как Rails. Она хорошо ложится на REST-архитектуру, о которой мы еще поговорим.</p>
10 <h2>Форма</h2>
10 <h2>Форма</h2>
11 <p>Каждое имя определяется как ключ в массиве user. Такой способ определения имен - необязательный, но он удобен для массовой обработки значений формы. Их изоляция в одном массиве позволяет избежать потенциальных пересечений с другими данными. В поисковых формах эта схема тоже удобна, если количество элементов больше одного.</p>
11 <p>Каждое имя определяется как ключ в массиве user. Такой способ определения имен - необязательный, но он удобен для массовой обработки значений формы. Их изоляция в одном массиве позволяет избежать потенциальных пересечений с другими данными. В поисковых формах эта схема тоже удобна, если количество элементов больше одного.</p>
12 <p>С точки зрения HTTP не существует способа передавать массивы. Если не указано иного, то данные формы кодируются в теле запроса как<em>application/x-www-form-urlencoded</em>. Чисто технически это выглядит как строка запроса с парами ключ-значение, объединенные символом<em>&amp;</em>:</p>
12 <p>С точки зрения HTTP не существует способа передавать массивы. Если не указано иного, то данные формы кодируются в теле запроса как<em>application/x-www-form-urlencoded</em>. Чисто технически это выглядит как строка запроса с парами ключ-значение, объединенные символом<em>&amp;</em>:</p>
13 <p>POST /users HTTP/1.1 Host: example.com Content-type: application/x-www-form-urlencoded Content-length: 42 key=value&amp;key2=value2&amp;user%5Bname%5D%3Djohn</p>
13 <p>POST /users HTTP/1.1 Host: example.com Content-type: application/x-www-form-urlencoded Content-length: 42 key=value&amp;key2=value2&amp;user%5Bname%5D%3Djohn</p>
14 <p>В конце тела закодирован ключ user[name]. Превращение таких ключей в массив идет на уровне интерпретатора в случае PHP. Либо на уровне самого фреймворка в случае остальных языков.</p>
14 <p>В конце тела закодирован ключ user[name]. Превращение таких ключей в массив идет на уровне интерпретатора в случае PHP. Либо на уровне самого фреймворка в случае остальных языков.</p>
15 <h2>Обработка данных</h2>
15 <h2>Обработка данных</h2>
16 <p>Обработка данных формы начинается с извлечения данных из тела запроса. Это можно сделать двумя способами, похожими на то, как мы извлекаем параметры запроса:</p>
16 <p>Обработка данных формы начинается с извлечения данных из тела запроса. Это можно сделать двумя способами, похожими на то, как мы извлекаем параметры запроса:</p>
17 <ul><li>getParsedBody() - извлекает все данные</li>
17 <ul><li>getParsedBody() - извлекает все данные</li>
18 <li>getParsedBodyParam($name, $defaultValue) - извлекает значение конкретного параметра. Вторым параметром принимает значение по умолчанию</li>
18 <li>getParsedBodyParam($name, $defaultValue) - извлекает значение конкретного параметра. Вторым параметром принимает значение по умолчанию</li>
19 </ul><p>Далее нужно убедиться в том, что данные введены верно. Этот процесс называется<strong>валидацией</strong>.</p>
19 </ul><p>Далее нужно убедиться в том, что данные введены верно. Этот процесс называется<strong>валидацией</strong>.</p>
20 <p>Slim не предоставляет механизмов для валидации. Ее можно получить из сторонних библиотек. В нашем случае валидация реализуется классом с одним методом validate(), который проверяет данные формы. Также он возвращает специальный массив $errors. В нем ключ - это название поля, а значение - текст ошибки, который нужно вывести в форме:</p>
20 <p>Slim не предоставляет механизмов для валидации. Ее можно получить из сторонних библиотек. В нашем случае валидация реализуется классом с одним методом validate(), который проверяет данные формы. Также он возвращает специальный массив $errors. В нем ключ - это название поля, а значение - текст ошибки, который нужно вывести в форме:</p>
21 <p>Если ошибок нет, то данные формы сохраняются, например, в базу данных. Об этом подробнее в следующем уроке. После сохранения выполняется перенаправление (HTTP redirect). За перенаправление отвечает заголовок<em>Location</em>и статусы с кодом 3XX:</p>
21 <p>Если ошибок нет, то данные формы сохраняются, например, в базу данных. Об этом подробнее в следующем уроке. После сохранения выполняется перенаправление (HTTP redirect). За перенаправление отвечает заголовок<em>Location</em>и статусы с кодом 3XX:</p>
22 <p>Если в процессе обработки возникли ошибки, выполняется рендеринг формы из шаблона, который мы использовали для<em>/users/new</em>. В этот шаблон передаются как данные формы, так и список ошибок.</p>
22 <p>Если в процессе обработки возникли ошибки, выполняется рендеринг формы из шаблона, который мы использовали для<em>/users/new</em>. В этот шаблон передаются как данные формы, так и список ошибок.</p>
23 <p>Редиректа не происходит, в адресной строке остается адрес<em>/users</em>. Если попробовать в этот момент нажать f5, то браузер выдаст предупреждение о том, что мы пытаемся повторно отправить данные. Это сообщение предупреждает о том, что метод POST не идемпотентен, и повторная отправка формы может привести к повторному созданию пользователя:</p>
23 <p>Редиректа не происходит, в адресной строке остается адрес<em>/users</em>. Если попробовать в этот момент нажать f5, то браузер выдаст предупреждение о том, что мы пытаемся повторно отправить данные. Это сообщение предупреждает о том, что метод POST не идемпотентен, и повторная отправка формы может привести к повторному созданию пользователя:</p>
24 <p>Вернемся к нашей форме и изменим ее так, чтобы в нее подставлялись возникающие ошибки и значения полей, введенные пользователем:</p>
24 <p>Вернемся к нашей форме и изменим ее так, чтобы в нее подставлялись возникающие ошибки и значения полей, введенные пользователем:</p>
25 <p>Такое изменение формы требует изменения обработчика<em>/users/new</em>. Чтобы избежать ошибок, нужно передать в шаблон пустой массив $errors и массив $user. В последнем необходимо задать значения по умолчанию для соответствующих полей формы. Так в шаблоне не придется выполнять проверку данных формы на существование:</p>
25 <p>Такое изменение формы требует изменения обработчика<em>/users/new</em>. Чтобы избежать ошибок, нужно передать в шаблон пустой массив $errors и массив $user. В последнем необходимо задать значения по умолчанию для соответствующих полей формы. Так в шаблоне не придется выполнять проверку данных формы на существование:</p>
26 <p>Форма увеличилась. На практике она будет еще больше из-за дополнительного оформления, например, отступов и подсветки ошибок. С опытом станет понятно, что так невозможно работать. Ради простейшей обработки придется писать много идентичного кода в HTML. Эту работу нужно автоматизировать.</p>
26 <p>Форма увеличилась. На практике она будет еще больше из-за дополнительного оформления, например, отступов и подсветки ошибок. С опытом станет понятно, что так невозможно работать. Ради простейшей обработки придется писать много идентичного кода в HTML. Эту работу нужно автоматизировать.</p>
27 <p>Для генерации форм используются специальные билдеры. Микрофреймворки не имеют встроенных билдеров, поэтому придется искать их самостоятельно.</p>
27 <p>Для генерации форм используются специальные билдеры. Микрофреймворки не имеют встроенных билдеров, поэтому придется искать их самостоятельно.</p>
28 <p>Довольно популярны<a>формы</a>из фреймворка Symfony. В этом компоненте каждая форма представлена своим классом. Компонент поддерживает валидацию, имеет встроенные механизмы защиты от некоторых атак и многое другое.</p>
28 <p>Довольно популярны<a>формы</a>из фреймворка Symfony. В этом компоненте каждая форма представлена своим классом. Компонент поддерживает валидацию, имеет встроенные механизмы защиты от некоторых атак и многое другое.</p>