КулЛиб - Классная библиотека! Скачать книги бесплатно 

Laravel 8. Быстрая разработка веб-сайтов на PHP [Владимир Александрович Дронов] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
Владимир Дронов

Санкт-Петербург
«БХВ-Петербург»
2021

УДК 004.738.5+004.43
ББК 32.973.26-018.2
Д75
Д75

Дронов В. А.

Laravel 8. Быстрая разработка веб-сайтов на PHP. — СПб.: БХВ-Петербург,
2021. — 688 с.: ил. — (Профессиональное программирование)
ISBN 978-5-9775-6695-7
Книга представляет собой полное описание фреймворка Laravel 8 для быстрой
разработки сайтов на языке PHP. Дан краткий вводный курс для начинающих,
в котором описывается разработка простого учебного сайта — электронной доски
объявлений. Раскрыты основы программирования сайтов на Laravel. Приведено
наиболее полное описание инструментов Laravel: моделей, контроллеров, шаблонов, средств обработки пользовательского ввода, включая валидаторы, сохранения
выгруженных файлов, разграничения доступа, обработки событий, отправки электронной почты и оповещений и пр. Рассказано об использовании очередей и отложенных заданий. Рассмотрены встроенный планировщик, инструменты кэширования,
журналирования и локализации сайтов, утилита artisan. Описаны дополнительные
библиотеки для обработки BBCode-тегов и CAPTCHA, вывода графических миниатюр, аутентификации через социальные сети (в частности, «ВКонтакте»). Рассмотрено программирование веб-служб REST, реализация вещания по протоколу
WebSocket и публикация сайта.
Электронный архив на сайте издательства содержит исходный код описанного
в книге сайта.
Для веб-программистов
УДК 004.738.5+004.43
ББК 32.973.26-018.2

Группа подготовки издания:
Руководитель проекта
Зав. редакцией
Компьютерная верстка
Дизайн серии
Оформление обложки

Евгений Рыбаков
Екатерина Сависте
Ольги Сергиенко
Инны Тачиной
Карины Соловьевой

"БХВ-Петербург", 191036, Санкт-Петербург, Гончарная ул., 20.

ISBN 978-5-9775-6695-7

© ООО "БХВ", 2021
© Оформление. ООО "БХВ-Петербург", 2021

Оглавление

Предисловие ................................................................................................................... 19
Почему именно Laravel? ............................................................................................................... 19
О чем эта книга? ............................................................................................................................ 20
Используемое ПО .......................................................................................................................... 21
Типографские соглашения ............................................................................................................ 22
ЧАСТЬ I. ОСНОВЫ LARAVEL НА ПРАКТИЧЕСКОМ ПРИМЕРЕ ................ 25
Глава 1. Простейший веб-сайт — доска объявлений ............................................. 27
1.1. Подготовительные действия .................................................................................................. 27
1.2. Проект и его создание. Папка проекта.................................................................................. 28
Теория .......................................................................................................................................... 28
Практика ...................................................................................................................................... 28
1.3. Запуск проекта. Отладочный веб-сервер PHP ...................................................................... 29
1.4. Контроллеры и действия ........................................................................................................ 30
Теория .......................................................................................................................................... 30
Практика ...................................................................................................................................... 31
1.5. Маршруты и списки маршрутов. Фасады............................................................................. 33
Теория .......................................................................................................................................... 33
Практика ...................................................................................................................................... 34
1.6. Настройки проекта. Подготовка проекта к работе с базой данных SQLite ....................... 35
Теория .......................................................................................................................................... 35
Практика ...................................................................................................................................... 35
1.7. Миграции................................................................................................................................. 37
Теория .......................................................................................................................................... 37
Практика ...................................................................................................................................... 37
1.8. Модели..................................................................................................................................... 39
1.9. Консоль Laravel....................................................................................................................... 40
1.10. Работа с базой данных .......................................................................................................... 41
1.11. URL-параметры. Внедрение зависимостей ........................................................................ 45
Теория .......................................................................................................................................... 45
Практика ...................................................................................................................................... 45
1.12. Шаблоны ............................................................................................................................... 47
Теория .......................................................................................................................................... 47
Практика ...................................................................................................................................... 47

4

Оглавление

1.13. Наследование шаблонов....................................................................................................... 52
Теория .......................................................................................................................................... 52
Практика ...................................................................................................................................... 52
1.14. Именованные маршруты ...................................................................................................... 54
1.15. Статические файлы ............................................................................................................... 55

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление,
правка и удаление объявлений .................................................................................. 57
2.1. Межтабличные связи. Работа со связанными записями...................................................... 57
2.2. Вход и выход. Раздел пользователя ...................................................................................... 61
Теория .......................................................................................................................................... 61
Практика ...................................................................................................................................... 62
2.3. Добавление, правка и удаление записей ............................................................................... 67
2.4. Валидация данных .................................................................................................................. 72
2.5. Разграничение доступа. Посредники, политики и провайдеры .......................................... 76
Теория .......................................................................................................................................... 76
Практика ...................................................................................................................................... 77
2.6. Получение сведений о текущем пользователе ..................................................................... 79
ЧАСТЬ II. БАЗОВЫЕ ИНСТРУМЕНТЫ ................................................................ 81
Глава 3. Создание, настройка и отладка проекта ................................................... 83
3.1. Подготовка платформы .......................................................................................................... 83
3.2. Создание проекта .................................................................................................................... 83
3.3. Папки и файлы проекта .......................................................................................................... 84
3.4. Настройки проекта ................................................................................................................. 86
3.4.1. Две разновидности настроек проекта ............................................................................. 86
3.4.1.1. Локальные настройки ............................................................................................. 86
3.4.1.2. Рабочие настройки .................................................................................................. 88
3.4.2. Настройки проекта по категориям .................................................................................. 89
3.4.2.1. Базовые настройки проекта .................................................................................... 89
3.4.2.2. Настройки режима работы веб-сайта .................................................................... 90
3.4.2.3. Настройки шифрования .......................................................................................... 91
3.4.2.4. Настройки баз данных ............................................................................................ 91
3.4.3. Доступ к настройкам из программного кода.................................................................. 94
3.4.4. Создание своих настроек ................................................................................................. 95
3.5. Базовые инструменты отладки ..............................................................................................96
3.5.1. Отладочный веб-сервер .................................................................................................... 96
3.5.2. Веб-страница с сообщением об ошибке ......................................................................... 97
Глава 4. Миграции и сидеры ...................................................................................... 99
4.1. Миграции................................................................................................................................. 99
4.1.1. Создание миграций ......................................................................................................... 100
4.1.2. Класс миграции ............................................................................................................... 100
4.1.3. Создание таблиц ............................................................................................................. 101
4.1.3.1. Создание полей ......................................................................................................101
4.1.3.2. Реализация «мягкого» удаления в таблицах........................................................ 105
4.1.3.3. Указание дополнительных параметров полей .................................................... 105
4.1.3.4. Создание индексов ................................................................................................ 107

Оглавление

5

4.1.3.5. Создание полей внешнего ключа ......................................................................... 108
4.1.3.6. Задание дополнительных параметров таблиц ..................................................... 110
4.1.4. Правка и удаление таблиц ..............................................................................................110
4.1.4.1. Правка и удаление полей ...................................................................................... 110
4.1.4.2. Переименование и удаление индексов ................................................................ 112
4.1.4.3. Удаление полей внешнего ключа и управление соблюдением
ссылочной целостности ..................................................................................................... 112
4.1.4.4. Переименование и удаление таблиц .................................................................... 113
4.1.5. Проверка существования таблиц и полей ..................................................................... 113
4.1.6. Указание базы данных, с которой будут работать миграции ..................................... 114
4.1.7. Обработка миграций....................................................................................................... 114
4.1.7.1. Применение миграций .......................................................................................... 114
4.1.7.2. Откат миграций, обновление, сброс и очистка базы данных ............................ 115
4.1.7.3. Создание журнала миграций и просмотр их состояния ..................................... 116
4.1.8. Дамп базы данных как альтернатива миграциям ......................................................... 117
4.2. Сидеры ................................................................................................................................... 117
4.2.1. Использование корневого сидера.................................................................................. 118
4.2.2. Использование подчиненных сидеров .......................................................................... 118
4.2.3. Выполнение сидеров ...................................................................................................... 119

Глава 5. Модели: базовые инструменты ................................................................ 120
5.1. Создание моделей ................................................................................................................. 120
5.2. Класс модели и соглашения по умолчанию ....................................................................... 121
5.3. Параметры модели................................................................................................................ 122
5.3.1. Параметры полей модели............................................................................................... 122
5.3.2. Параметры обслуживаемой таблицы ............................................................................ 122
5.3.3. Параметры преобразования типов ................................................................................ 123
5.3.4. Реализация «мягкого» удаления в моделях .................................................................. 124
5.4. Создание связей между моделями....................................................................................... 125
5.4.1. Связь «один-со-многими» .............................................................................................. 125
5.4.2. Связь «один-с-одним» .................................................................................................... 127
5.4.3. Пометка записи первичной модели как исправленной при правке или удалении
связанных записей вторичной модели .................................................................................... 128
5.4.4. Связь «многие-со-многими» .......................................................................................... 128
5.4.4.1. Использование связующих моделей .................................................................... 131
5.4.5. Сквозная связь «один-со-многими» .............................................................................. 132
5.4.6. Сквозная связь «один-с-одним» .................................................................................... 133
5.4.7. Записи-заглушки ............................................................................................................. 133
5.4.8. Замкнутая связь............................................................................................................... 134
5.5. Методы моделей ................................................................................................................... 135
5.6. Преобразование значений полей. Акцессоры и мутаторы ................................................ 136

Глава 6. Запись данных.............................................................................................. 137
6.1. Добавление, правка и удаление записей с помощью моделей .......................................... 137
6.1.1. Добавление записей. Построитель запросов ................................................................ 137
6.1.2. Правка записей................................................................................................................ 140
6.1.2.1. Правка значений отдельных полей ...................................................................... 141
6.1.2.2. Проверка, значения каких полей изменились ..................................................... 141
6.1.3. Удаление записей ........................................................................................................... 143
6.1.3.1. «Мягкое» удаление записей .................................................................................. 143

6

Оглавление

6.1.4. Работа со связанными записями .................................................................................... 144
6.1.4.1. Связи «один-со-многими» и «один-с-одним»: связывание записей .................. 144
6.1.4.2. Связи «один-со-многими» и «один-с-одним»: добавление и правка
связанных записей .............................................................................................................. 145
6.1.4.3. Связь «многие-со-многими»: связывание записей ............................................. 146
6.1.4.4. Связь «многие-со-многими»: добавление и правка связанных записей ........... 148
6.1.5. Копирование записей ..................................................................................................... 149
6.2. Массовые добавление, правка и удаление записей............................................................ 150
6.2.1. Массовое добавление записей ....................................................................................... 150
6.2.2. Массовая правка записей ...............................................................................................151
6.2.3. Массовое удаление записей ........................................................................................... 151
6.2.4. Использование фасада DB для записи данных............................................................. 152

Глава 7. Выборка данных .......................................................................................... 153
7.1. Извлечение значений из полей записи ................................................................................ 153
7.2. Доступ к связанным записям ...............................................................................................153
7.2.1. Связь «один-со-многими»: доступ к связанным записям ............................................ 153
7.2.2. Связь «один-с-одним»: доступ к связанным записям .................................................. 154
7.2.3. Связь «многие-со-многими»: доступ к связанным записям ........................................ 155
7.3. Выборка записей: базовые средства ................................................................................... 156
7.3.1. Выборка всех записей .................................................................................................... 156
7.3.2. Извлечение одной записи...............................................................................................156
7.3.3. Поиск одной записи ........................................................................................................ 157
7.3.4. Фильтрация записей ....................................................................................................... 159
7.3.4.1. Фильтрация записей по значениям полей типа JSON ........................................ 163
7.3.5. Сортировка записей ........................................................................................................ 164
7.3.6. Выборка указанного количества записей ..................................................................... 165
7.3.7. Выборка уникальных записей ....................................................................................... 166
7.3.8. Задание параметров запросов на основании выполнения какого-либо условия ....... 166
7.3.9. Смена типа выдаваемых значений ................................................................................ 166
7.3.10. Выполнение запроса и получение результата ............................................................ 167
7.3.11. Проверка наличия записей в полученном результате................................................ 167
7.3.12. Объединение результатов от разных запросов .......................................................... 168
7.4. Выборка связанных записей ................................................................................................168
7.5. Выборка записей: расширенные средства .......................................................................... 172
7.5.1. Указание выбираемых полей ......................................................................................... 172
7.5.2. Вставка фрагментов SQL-кода в запрос ....................................................................... 173
7.5.3. Связывание таблиц ......................................................................................................... 173
7.5.4. Использование вложенных запросов ............................................................................ 176
7.5.5. Использование фасада DB для выборки данных ......................................................... 178
7.6. Агрегатные вычисления ....................................................................................................... 179
7.6.1. Агрегатные вычисления по всем записям .................................................................... 179
7.6.2. Агрегатные вычисления по группам записей ............................................................... 179
7.6.3. Получение количества связанных записей ................................................................... 181
7.7. Извлечение «мягко» удаленных записей ............................................................................ 182
7.8. Сравнение записей ................................................................................................................ 182
7.9. Получение значения заданного поля................................................................................... 183
7.10. Повторное считывание записей ......................................................................................... 183

Оглавление

7

Глава 8. Маршрутизация ........................................................................................... 184
8.1. Настройки маршрутизатора .................................................................................................184
8.2. Списки маршрутов................................................................................................................ 185
8.3. Создание простых маршрутов ............................................................................................. 186
8.3.1. Специализированные маршруты ................................................................................... 187
8.3.2. Резервный маршрут ........................................................................................................ 188
8.4. Именованные маршруты ...................................................................................................... 188
8.5. URL-параметры и параметризованные маршруты ............................................................ 188
8.5.1. Указание шаблонов для значений URL-параметров.................................................... 189
8.5.2. Внедрение моделей......................................................................................................... 191
8.5.2.1. Неявное внедрение моделей ................................................................................. 191
8.5.2.2. Явное внедрение моделей..................................................................................... 192
8.5.3. Значения по умолчанию для URL-параметров ............................................................ 194
8.6. Дополнительные параметры маршрутов ............................................................................ 195
8.7. Группы маршрутов ............................................................................................................... 196
8.8. Маршруты на ресурсные контроллеры ............................................................................... 198
8.8.1. Маршруты на подчиненные ресурсные контроллеры ................................................. 199
8.8.2. Дополнительные параметры маршрутов на ресурсные контроллеры ....................... 200
8.9. Как Laravel обрабатывает списки маршрутов? .................................................................. 201
8.10. Вывод списка созданных маршрутов................................................................................ 202
Глава 9. Контроллеры и действия. Обработка запросов
и генерирование ответов............................................................................................ 203
9.1. Разновидности контроллеров и особенности работы с ними ........................................... 203
9.1.1. Контроллеры-функции ...................................................................................................203
9.1.2. Контроллеры-классы ...................................................................................................... 204
9.1.2.1. Ресурсные контроллеры ....................................................................................... 204
9.1.2.2. Контроллеры одного действия ............................................................................. 206
9.1.2.3. Создание контроллеров-классов .......................................................................... 206
9.1.2.4. Связывание посредников с контроллерами ........................................................ 207
9.2. Внедрение зависимостей в контроллерах ........................................................................... 208
9.3. Обработка клиентских запросов.......................................................................................... 208
9.3.1. Извлечение данных, отправленных посетителем ........................................................ 209
9.3.2. Определение, присутствует ли в запросе нужное значение ........................................ 211
9.3.3. Получение сведений о запросе ...................................................................................... 212
9.4. Генерирование интернет-адресов ........................................................................................ 215
9.5. Генерирование ответов ........................................................................................................ 217
9.5.1. Ответы на основе шаблонов .......................................................................................... 217
9.5.1.1. Ответы в виде объектов класса View ................................................................... 217
9.5.1.2. Ответы в виде объектов класса Response ............................................................ 219
9.5.2. Специальные ответы ...................................................................................................... 219
9.5.2.1. Отображение файла в веб-обозревателе .............................................................. 219
9.5.2.2. Сохранение файла на локальном диске ............................................................... 220
9.5.2.3. Отправка данных в форматах JSON и JSONP..................................................... 220
9.5.2.4. Текстовый ответ .................................................................................................... 221
9.5.2.5. «Пустой» ответ ......................................................................................................222
9.5.3. Дополнительные параметры ответов ............................................................................ 222
9.5.4. Перенаправления ............................................................................................................ 223
9.6. Обработка ошибок ................................................................................................................ 225

8

Оглавление

Глава 10. Обработка введенных данных. Валидация .......................................... 227
10.1. Извлечение введенных данных.......................................................................................... 227
10.2. Валидация данных .............................................................................................................. 229
10.2.1. Валидаторы ................................................................................................................... 229
10.2.1.1. Быстрая валидация с неявным созданием валидатора ..................................... 229
10.2.1.2. Валидация с явным созданием валидатора ....................................................... 231
10.2.1.3. Валидация массивов элементов управления ..................................................... 233
10.2.2. Формальные запросы ...................................................................................................234
10.2.3. Написание правил валидации ...................................................................................... 237
10.2.4. Написание сообщений об ошибках ввода .................................................................. 245
10.2.5. Извлечение ранее введенных данных ......................................................................... 246
10.2.6. Извлечение сообщений об ошибках ввода ................................................................. 246
10.2.7. Создание своих правил валидации .............................................................................. 247
10.2.7.1. Правила-функции ................................................................................................ 247
10.2.7.2. Правила-расширения .......................................................................................... 247
10.2.7.3. Правила-объекты ................................................................................................. 248
10.3. Удаление начальных и конечных пробелов ..................................................................... 250
10.4. Вывод веб-страниц добавления, правки и удаления записей.......................................... 250
Глава 11. Шаблоны: базовые инструменты .......................................................... 252
11.1. Настройки шаблонизатора .................................................................................................252
11.2. Директивы шаблонизатора ................................................................................................253
11.2.1. Директивы вывода данных .......................................................................................... 253
11.2.2. Управляющие директивы............................................................................................. 254
11.2.2.1. Условные директивы и директивы выбора ....................................................... 254
11.2.2.2. Директивы циклов ............................................................................................... 256
11.2.3. Прочие директивы ........................................................................................................ 258
11.2.4. Запрет на обработку директив ..................................................................................... 259
11.3. Вывод веб-форм и элементов управления ........................................................................ 259
11.3.1. Вывод веб-форм ............................................................................................................ 259
11.3.2. Вывод элементов управления ...................................................................................... 260
11.3.3. Вывод сообщений об ошибках ввода.......................................................................... 261
11.4. Наследование шаблонов..................................................................................................... 262
11.5. Стеки .................................................................................................................................... 265
11.6. Включаемые шаблоны ....................................................................................................... 266
11.6.1. Псевдонимы включаемых шаблонов .......................................................................... 267
11.7. Компоненты ........................................................................................................................ 268
11.7.1. Полнофункциональные компоненты .......................................................................... 268
11.7.1.1. Создание полнофункциональных компонентов ............................................... 268
11.7.1.2. Передача данных в компоненты. Атрибуты компонентов .............................. 270
11.7.1.3. Передача HTML-содержимого в компоненты. Слоты ..................................... 272
11.7.2. Упрощенные компоненты ............................................................................................ 273
11.7.2.1. Бесшаблонные компоненты ............................................................................... 273
11.7.2.2. Бесклассовые компоненты ................................................................................. 274
11.7.3. Динамический компонент ............................................................................................ 274
11.8. Передача данных в шаблоны: другие способы ................................................................ 275
11.8.1. Разделяемые значения ..................................................................................................275
11.8.2. Составители значений ..................................................................................................276
11.8.3. Создатели значений ...................................................................................................... 277
11.9. Обработка статических файлов ......................................................................................... 278

Оглавление

9

Глава 12. Пагинация................................................................................................... 280
12.1. Автоматическое создание пагинатора .............................................................................. 280
12.2. Дополнительные параметры пагинатора .......................................................................... 282
12.3. Настройка отображения пагинатора ................................................................................. 282
12.4. Создание пагинатора вручную .......................................................................................... 285
Глава 13. Разграничение доступа: базовые инструменты .................................. 287
13.1. Настройки подсистемы разграничения доступа .............................................................. 287
13.2. Создание недостающих модулей, реализующих разграничение доступа ...................... 289
13.3. Маршруты, ведущие на контроллеры разграничения доступа ....................................... 290
13.4. Служебные таблицы и модель ........................................................................................... 293
13.5. Регистрация новых пользователей .................................................................................... 294
13.6. Вход на веб-сайт ................................................................................................................. 296
13.7. Раздел пользователя ........................................................................................................... 299
13.8. Собственно разграничение доступа .................................................................................. 299
13.8.1. Разграничение доступа: простейшие инструменты ................................................... 299
13.8.1.1. Разграничение доступа с помощью посредников............................................. 299
13.8.1.2. Разграничение доступа в шаблонах ................................................................... 300
13.8.2. Гейты ............................................................................................................................. 301
13.8.2.1. Написание гейтов ................................................................................................ 301
13.8.2.2. Разграничение доступа посредством гейтов ..................................................... 302
13.8.2.3. Предварительные и завершающие проверки .................................................... 304
13.8.2.4. Гейты с развернутыми ответами ........................................................................ 305
13.8.3. Политики ....................................................................................................................... 306
13.8.3.1. Создание и регистрация политик ....................................................................... 307
13.8.3.2. Разграничение доступа посредством политик .................................................. 309
13.8.3.3. Разграничение доступа в ресурсных контроллерах.......................................... 312
13.8.4. Разграничение доступа с помощью формальных запросов ...................................... 313
13.9. Получение сведений о текущем пользователе ................................................................. 313
13.10. Подтверждение пароля ..................................................................................................... 314
13.11. Выход с веб-сайта ............................................................................................................. 315
13.12. Проверка существования адреса электронной почты ................................................... 315
13.13. Сброс пароля ..................................................................................................................... 318
13.13.1. Отправка электронного письма с гиперссылкой сброса пароля............................. 318
13.13.2. Собственно сброс пароля ........................................................................................... 318
13.13.3. Команда auth:clear-resets ........................................................................................... 319
Глава 14. Обработка строк, массивов и функции-хелперы ................................ 320
14.1. Обработка строк.................................................................................................................. 320
14.1.1. Составление строк ........................................................................................................ 321
14.1.2. Сравнение строк и получение сведений о строках .................................................... 322
14.1.3. Преобразование строк ..................................................................................................323
14.1.4. Извлечение фрагментов строк ..................................................................................... 325
14.1.5. Поиск и замена в строках .............................................................................................327
14.1.6. Обработка путей к файлам ........................................................................................... 330
14.1.7. Прочие инструменты для обработки строк ................................................................ 330
14.2. Обработка массивов ........................................................................................................... 331
14.2.1. Добавление, правка и удаление элементов массивов ................................................ 331
14.2.2. Извлечение элементов массива ................................................................................... 333
14.2.3. Проверка существования элементов массивов .......................................................... 335

10

Оглавление

14.2.4. Получение сведений о массиве ................................................................................... 336
14.2.5. Упорядочивание элементов массивов ........................................................................ 336
14.2.6. Прочие инструменты для обработки массивов .......................................................... 337
14.3. Функции-хелперы ............................................................................................................... 338
14.3.1. Функции, выдающие пути к ключевым папкам ......................................................... 339
14.3.2. Служебные функции.....................................................................................................339

Глава 15. Коллекции Laravel .................................................................................... 343
15.1. Обычные коллекции ........................................................................................................... 343
15.1.1. Создание обычных коллекций ..................................................................................... 343
15.1.2. Добавление, правка и удаление элементов коллекции .............................................. 344
15.1.3. Извлечение отдельных элементов и частей коллекции ............................................. 346
15.1.4. Получение сведений об элементах коллекции ........................................................... 351
15.1.5. Перебор элементов коллекции .................................................................................... 353
15.1.6. Поиск и фильтрация элементов коллекции ................................................................ 353
15.1.7. Упорядочивание элементов коллекции ...................................................................... 358
15.1.8. Группировка элементов коллекций ............................................................................. 360
15.1.9. Агрегатные вычисления в коллекциях ........................................................................ 362
15.1.10. Получение сведений о коллекции ............................................................................. 363
15.1.11. Прочие инструменты для обработки коллекций ........................................................ 363
15.2. Коллекции, заполняемые по запросу ................................................................................ 368
15.2.1. Создание коллекций, заполняемых по запросу .......................................................... 368
15.2.2. Работа с коллекциями, заполняемыми по запросу .................................................... 368
ЧАСТЬ III. РАСШИРЕННЫЕ ИНСТРУМЕНТЫ
И ДОПОЛНИТЕЛЬНЫЕ БИБЛИОТЕКИ ............................................................. 371
Глава 16. Базы данных и модели: расширенные инструменты ........................ 373
16.1. Отложенная и немедленная выборка связанных записей................................................ 373
16.2. Обработка коллекций записей по частям ......................................................................... 375
16.3. Полиморфные связи ........................................................................................................... 377
16.3.1. Создание поля внешнего ключа для полиморфной связи ......................................... 377
16.3.2. Создание полиморфных связей ................................................................................... 378
16.3.2.1. Полиморфная связь «один-со-многими» ........................................................... 378
16.3.2.2. Полиморфная связь «один-с-одним» ................................................................. 380
16.3.2.3. Полиморфная связь «многие-со-многими» ....................................................... 381
16.3.3. Работа с записями, связанными полиморфной связью ............................................. 383
16.3.4. Указание своих типов связываемых записей ............................................................. 384
16.4. Пределы ............................................................................................................................... 385
16.4.1. Локальные пределы ...................................................................................................... 385
16.4.2. Глобальные пределы ....................................................................................................387
16.5. Выполнение «сырых» SQL-запросов ................................................................................ 388
16.5.1. «Сырые» вызовы функций СУБД................................................................................ 389
16.5.2. «Сырые» команды SQL ................................................................................................ 389
16.5.3. «Сырые» SQL-запросы целиком.................................................................................. 391
16.6. Блокировка записей ............................................................................................................ 392
16.7. Управление транзакциями .................................................................................................392
16.7.1. Автоматическое управление транзакциями ............................................................... 392
16.7.2. Ручное управление транзакциями ............................................................................... 393

Оглавление

11

Глава 17. Шаблоны: расширенные инструменты
и дополнительные библиотеки ................................................................................. 394
17.1. Библиотека Laravel HTML: создание веб-форм и элементов управления ..................... 394
17.1.1. Создание элементов управления ................................................................................. 394
17.1.2. Создание веб-форм ....................................................................................................... 398
17.1.3. Создание гиперссылок .................................................................................................400
17.2. Библиотека genertorg/bbcode: поддержка BBCode........................................................... 401
17.2.1. Использование библиотеки genertorg/bbcode ............................................................. 402
17.2.2. Поддерживаемые BBCode-теги ................................................................................... 403
17.2.3. Добавление своих BBCode-тегов ................................................................................ 404
17.3. Библиотека Captcha for Laravel: поддержка CAPTCHA .................................................. 405
17.3.1. Настройка Captcha for Laravel ..................................................................................... 406
17.3.2. Использование Captcha for Laravel.............................................................................. 407
17.4. Написание своих директив шаблонизатора ...................................................................... 408
17.4.1. Написание простейших директив................................................................................ 408
17.4.2. Написание условных директив .................................................................................... 409
17.5. Пакет Laravel Mix ............................................................................................................... 410
17.5.1. Исходные файлы и их расположение.......................................................................... 411
17.5.2. Конфигурирование Laravel Mix ................................................................................... 411
17.5.2.1. Обработка таблиц стилей ................................................................................... 412
17.5.2.2. Обработка веб-сценариев ................................................................................... 413
17.5.2.3. Копирование файлов и папок ............................................................................. 414
17.5.2.4. Мечение файлов .................................................................................................. 415
17.5.3. Запуск Laravel Mix ........................................................................................................ 416
17.6. Использование Bootstrap .................................................................................................... 417
Глава 18. Обработка выгруженных файлов .......................................................... 419
18.1. Настройки подсистемы обработки выгруженных файлов .............................................. 419
18.2. Создание символических ссылок на выгруженные файлы ............................................. 422
18.3. Хранение выгруженных файлов ........................................................................................ 423
18.4. Базовые средства для обработки выгруженных файлов.................................................. 423
18.4.1. Валидаторы для выгруженных файлов ....................................................................... 423
18.4.2. Получение выгруженных файлов ................................................................................ 424
18.4.3. Получение сведений о выгруженных файлах............................................................. 425
18.4.4. Сохранение выгруженных файлов .............................................................................. 426
18.4.5. Выдача выгруженных файлов посетителям ............................................................... 428
18.4.5.1. Вывод выгруженных файлов .............................................................................. 428
18.4.5.2. Реализация загрузки выгруженного файла ....................................................... 429
18.4.6. Удаление выгруженных файлов .................................................................................. 429
18.5. Расширенные средства для работы с выгруженными файлами...................................... 430
18.5.1. Чтение из файлов и запись в них ................................................................................. 430
18.5.2. Получение сведений о файле ....................................................................................... 431
18.5.3. Прочие манипуляции с файлами ................................................................................. 431
18.5.4. Работа с папками .......................................................................................................... 432
18.6. Библиотека bkwld/croppa: вывод миниатюр ..................................................................... 433
18.6.1. Настройки библиотеки bkwld/croppa .......................................................................... 434
18.6.2. Использование библиотеки bkwld/croppa ................................................................... 435
18.6.3. Команда croppa:purge .................................................................................................. 438

12

Оглавление

Глава 19. Разграничение доступа: расширенные инструменты
и дополнительная библиотека .................................................................................. 439
19.1. Низкоуровневые средства для выполнения входа и выхода ........................................... 439
19.2. Библиотека Laravel Socialite: вход через сторонние интернет-службы ......................... 442
19.2.1. Создание приложения «ВКонтакте» ........................................................................... 442
19.2.2. Установка и настройка Laravel Socialite ..................................................................... 443
19.2.3. Использование Laravel Socialite .................................................................................. 445
19.2.3.1. Действие первое: обращение к сторонней интернет-службе........................... 445
19.2.3.2. Действие второе: поиск (регистрация) пользователя и вход ........................... 446
19.2.3.3. Завершающие операции: создание маршрутов и гиперссылки входа ............ 447
19.3. Защита от атак CSRF .......................................................................................................... 448
19.4. Управление скоростью запросов ....................................................................................... 449
19.4.1. Управление скоростью запросов: базовые инструменты .......................................... 449
19.4.2. Использование ограничителей скорости запросов .................................................... 449
19.5. Корректная правка пароля ................................................................................................. 452
Глава 20. Внедрение зависимостей, провайдеры и фасады ................................ 453
20.1. Внедрение зависимостей .................................................................................................... 453
20.1.1. Простейшие случаи внедрения зависимостей ............................................................ 453
20.1.2. Управление внедрением зависимостей ....................................................................... 455
20.1.2.1. Простая регистрация классов и объектов ......................................................... 456
20.1.2.2. Подмена классов и реализации .......................................................................... 458
20.1.2.3. Гибкая подмена классов и реализации .............................................................. 459
20.1.2.4. Гибкая регистрация значений произвольного типа .......................................... 461
20.1.2.5. Переопределение регистрации ........................................................................... 461
20.1.2.6. Вызов методов и функций, в которых используется
внедрение зависимостей .................................................................................................... 462
20.1.2.7. Подмена методов................................................................................................. 463
20.2. Провайдеры ......................................................................................................................... 464
20.2.1. Список провайдеров, используемых веб-сайтом ....................................................... 464
20.2.2. Создание своих провайдеров ....................................................................................... 466
20.3. Фасады ................................................................................................................................. 468

Глава 21. Посредники ................................................................................................. 469
21.1. Посредники, используемые веб-сайтом ............................................................................ 469
21.1.1. Управление очередностью выполнения посредников ............................................... 472
21.1.2. Параметры посредников .............................................................................................. 472
21.2. Написание своих посредников .......................................................................................... 472
21.2.1. Как исполняется посредник? ....................................................................................... 473
21.2.2. Создание посредников .................................................................................................473
21.2.3. Посредники с завершающими действиями ................................................................ 476
Глава 22. События и их обработка .......................................................................... 477
22.1. События-классы .................................................................................................................. 477
22.1.1. Обработка событий-классов: слушатели .................................................................... 477
22.1.1.1. Создание слушателей-классов............................................................................ 477
22.1.1.2. Явная привязка слушателей-классов к событиям ............................................. 479
22.1.1.3. Автоматическая привязка слушателей-классов к событиям ........................... 480
22.1.1.4. Просмотр списков слушателей-классов, привязанных к событиям ................ 481
22.1.1.5. Слушатели-функции............................................................................................ 481

Оглавление

13

22.1.2. Обработка событий-классов: подписчики .................................................................. 482
22.1.3. События-классы, поддерживаемые фреймворком..................................................... 484
22.1.3.1. События подсистемы разграничения доступа .................................................. 484
22.1.3.2. События других подсистем ................................................................................ 485
22.1.4. Создание и использование своих событий-классов ................................................... 486
22.1.4.1. Создание событий-классов ................................................................................. 486
22.1.4.2. Создание событий-классов и их слушателей .................................................... 487
22.1.4.3. Генерирование своих событий ........................................................................... 487
22.2. События-строки .................................................................................................................. 488
22.2.1. Привязка обработчиков к событиям-строкам ............................................................ 488
22.2.2. Генерирование событий-строк .................................................................................... 489
22.3. События моделей ................................................................................................................ 490
22.3.1. Обработка событий моделей ....................................................................................... 490
22.3.1.1. Обработка событий моделей посредством слушателей-функций ................... 490
22.3.1.2. Связывание событий моделей с событиями-классами ..................................... 490
22.3.1.3. Использование обозревателей............................................................................ 491
22.3.2. Список событий моделей ............................................................................................. 492
22.3.3. Временное отключение событий в моделях ............................................................... 493

Глава 23. Отправка электронной почты ................................................................ 494
23.1. Настройки подсистемы отправки электронной почты .................................................... 494
23.2. Создание электронных писем ............................................................................................ 497
23.2.1. Создание классовэлектронных писем ........................................................................ 497
23.2.2. Генерирование электронных писем ............................................................................ 498
23.2.3. Написание шаблонов электронных писем .................................................................. 501
23.2.4. Написание электронных писем на языке Markdown.................................................. 502
23.2.4.1. Классы писем, написанных на Markdown ......................................................... 502
23.2.4.2. Написание шаблонов писем на Markdown ........................................................ 502
23.2.4.3. Управление генерированием писем, написанных на Markdown ..................... 504
23.3. Отправка электронных писем ............................................................................................ 505
23.4. Предварительный просмотр электронных писем ............................................................ 507
23.5. События, генерируемые при отправке электронных писем ............................................ 507
23.6. Доступ к письмам, отправленным посредством службы array ...................................... 507
Глава 24. Оповещения................................................................................................ 509
24.1. Создание оповещений ........................................................................................................ 509
24.2. Написание оповещений ...................................................................................................... 511
24.2.1. Почтовые оповещения ................................................................................................. 511
24.2.1.1. Генерирование простых почтовых оповещений............................................... 511
24.2.1.2. Генерирование почтовых оповещений на основе текстовых
и HTML-шаблонов ............................................................................................................. 513
24.2.1.3. Генерирование почтовых оповещений на основе Markdown-шаблонов ........ 513
24.2.1.4. Указание адреса получателя ............................................................................... 514
24.2.2. SMS-оповещения .......................................................................................................... 514
24.2.2.1. Подготовительные действия и настройка службы SMS-оповещений ............ 514
24.2.2.2. Генерирование произвольных SMS-оповещений ............................................. 515
24.2.2.3. Генерирование SMS-оповещений на основе шаблонов ................................... 516
24.2.2.4. Указание телефона получателя .......................................................................... 517

14

Оглавление

24.2.3. Slack-оповещения ......................................................................................................... 517
24.2.3.1. Генерирование Slack-оповещений ..................................................................... 517
24.2.3.2. Добавление вложений ......................................................................................... 518
24.2.3.3. Указание интернет-адреса получателя .............................................................. 520
24.2.4. Табличные оповещения................................................................................................ 520
24.2.4.1. Создание таблицы для хранения табличных оповещений ............................... 521
24.2.4.2. Генерирование табличных оповещений ............................................................ 521
24.2.5. Оповещения, отправляемые по нескольким каналам ................................................ 522
24.3. Отправка оповещений ........................................................................................................ 522
24.3.1. Отправка оповещений произвольным получателям .................................................. 523
24.4. Предварительный просмотр почтовых оповещений ....................................................... 523
24.5. Работа с табличными оповещениями................................................................................ 524
24.6. События, генерируемые при отправке оповещений ........................................................ 525

Глава 25. Очереди и отложенные задания ............................................................. 526
25.1. Настройка подсистемы очередей ...................................................................................... 526
25.1.1. Настройка самих очередей........................................................................................... 526
25.1.2. Настройка баз данных Redis ........................................................................................ 529
25.1.3. Подготовка таблиц для хранения отложенных заданий ............................................ 530
25.2. Отложенные задания-классы ............................................................................................. 531
25.2.1. Создание отложенных заданий-классов ..................................................................... 531
25.2.1.1. Создание отложенных заданий-классов: базовые инструменты ..................... 531
25.2.1.2. Параметры отложенных заданий-классов ......................................................... 533
25.2.1.3. Обработка ошибок в отложенных заданиях-классах ....................................... 534
25.2.1.4. Взаимодействие с очередью ............................................................................... 535
25.2.1.5. Неотложные задания ........................................................................................... 535
25.2.2. Запуск отложенных заданий-классов .......................................................................... 536
25.3. Отложенные задания-функции .......................................................................................... 537
25.4. Цепочки отложенных заданий ........................................................................................... 537
25.5. Специфические разновидности отложенных заданий ..................................................... 539
25.5.1. Отложенные слушатели событий ................................................................................ 539
25.5.2. Отложенные электронные письма............................................................................... 541
25.5.3. Отложенные оповещения ............................................................................................. 542
25.6. События, генерируемые при выполнении отложенных заданий .................................... 543
25.7. Выполнение отложенных заданий .................................................................................... 544
25.7.1. Запуск обработчика отложенных заданий .................................................................. 544
25.7.2. Работа с проваленными заданиями ............................................................................. 546
Глава 26. Cookie, сессии, всплывающие сообщения и криптография.............. 547
26.1. Cookie .................................................................................................................................. 547
26.1.1. Настройки cookie .......................................................................................................... 547
26.1.2. Создание cookie ............................................................................................................ 548
26.1.3. Считывание cookie........................................................................................................ 550
26.1.4. Удаление cookie ............................................................................................................ 550
26.2. Сессии .................................................................................................................................. 551
26.2.1. Подготовка к работе с сессиями .................................................................................. 551
26.2.1.1. Настройки сессий ................................................................................................ 551
26.2.1.2. Создание таблицы для хранения сессий ............................................................ 552

Оглавление

15

26.2.2. Работа с сессиями ......................................................................................................... 553
26.2.2.1. Запись данных в сессию и их изменение........................................................... 553
26.2.2.2. Чтение данных из сессии .................................................................................... 554
26.2.2.3. Удаление данных из сессии ................................................................................ 554
26.2.2.4. Повторное генерирование идентификатора сессии ......................................... 555
26.3. Всплывающие сообщения ..................................................................................................555
26.4. Криптография...................................................................................................................... 556
26.4.1. Шифрование данных ....................................................................................................556
26.4.2. Хеширование и сверка паролей ................................................................................... 557
26.4.2.1. Настройки хеширования ..................................................................................... 557
26.4.2.2. Хеширование и сверка ........................................................................................ 558
26.4.3. Генерирование подписанных интернет-адресов ........................................................ 558

Глава 27. Планировщик заданий ............................................................................. 561
27.1. Создание заданий планировщика ...................................................................................... 561
27.1.1. Как пишутся задания планировщика .......................................................................... 561
27.1.2. Параметры заданий планировщика ............................................................................. 563
27.1.2.1. Расписание запуска заданий ............................................................................... 563
27.1.2.2. Дополнительные параметры заданий ................................................................ 565
27.1.3. Обработка вывода, генерируемого заданиями планировщика ................................. 567
27.1.4. Исполнение указанного кода перед выполнением задания и после него ................ 568
27.1.5. Отправка сигналов по указанным интернет-адресам ................................................ 568
27.2. Запуск планировщика заданий .......................................................................................... 569
27.3. События, генерируемые при выполнении заданий планировщика ................................ 571
Глава 28. Локализация ............................................................................................... 572
28.1. Быстрая локализация .......................................................................................................... 572
28.2. Локализация с применением обозначений ....................................................................... 573
28.2.1. Подстановка параметров в переведенные строки ...................................................... 575
28.2.2. Вывод существительных во множественном числе .................................................. 575
28.2.3. Локализация сообщений об ошибках ввода ............................................................... 577
28.3. Реализация переключения на другой язык ....................................................................... 578
28.4. Библиотека Laravel-lang: локализация на множество языков ......................................... 580
Глава 29. Кэширование .............................................................................................. 581
29.1. Кэширование на стороне сервера ...................................................................................... 581
29.1.1. Подготовка подсистемы кэширования ....................................................................... 581
29.1.1.1. Настройка подсистемы кэширования ................................................................ 581
29.1.1.2. Создание таблицы для хранения кэша............................................................... 583
29.1.2. Работа с кэшем стороны сервера................................................................................. 584
29.1.2.1. Сохранение данных в кэше и их правка ............................................................ 584
29.1.2.2. Чтение данных из кэша ....................................................................................... 585
29.1.2.3. Удаление данных из кэша................................................................................... 586
29.1.3. Распределенные блокировки ....................................................................................... 587
29.1.3.1. Немедленные распределенные блокировки ...................................................... 587
29.1.3.2. Распределенные блокировки с ожиданием ....................................................... 589
29.1.3.3. Передача распределенных блокировок между процессами ............................ 590
29.1.4. События, генерируемые кэшем ................................................................................... 591
29.2. Кэширование на стороне клиента ..................................................................................... 592

16

Оглавление

Глава 30. Разработка веб-служб ............................................................................... 593
30.1. Бэкенды: базовые инструменты ........................................................................................ 593
30.1.1. Выдача данных в формате JSON ................................................................................. 594
30.1.2. Задание структуры генерируемых JSON-объектов.................................................... 596
30.2. Бэкенды: ресурсы и ресурсные коллекции ....................................................................... 598
30.2.1. Ресурсы .......................................................................................................................... 598
30.2.1.1. Как пишутся ресурсы? ........................................................................................ 598
30.2.1.2. Задание структуры JSON-объектов, генерируемых ресурсами ....................... 599
30.2.1.3. Дополнительные параметры ресурсов .............................................................. 602
30.2.1.4. Использование ресурсов ..................................................................................... 604
30.2.2. Ресурсные коллекции ...................................................................................................604
30.2.2.1. Быстрое JSON-кодирование коллекции записей .............................................. 604
30.2.2.2. Как пишутся и используются ресурсные коллекции? ...................................... 605
30.2.2.3. Пагинация в ресурсных коллекциях .................................................................. 606
30.3. Бэкенды: обработка данных ...............................................................................................607
30.3.1. Выдача записей ............................................................................................................. 607
30.3.2. Добавление, правка и удаление записей ..................................................................... 608
30.3.3. Совмещенная обработка данных ................................................................................. 609
30.4. Бэкенды: разграничение доступа....................................................................................... 610
30.5. Фронтенды: взаимодействие с бэкендами ........................................................................ 613
30.6. Фронтенды: использование React и Vue ........................................................................... 615
Глава 31. Вещание ....................................................................................................... 616
31.1. Бэкенд: подготовка подсистемы вещания ........................................................................ 616
31.1.1. Настройка подсистемы вещания ................................................................................. 616
31.1.2. Установка и настройка laravel-echo-server.................................................................. 617
31.1.3. Подготовка проекта к реализации вещания ............................................................... 620
31.2. Бэкенд: вещаемые события и оповещения ....................................................................... 620
31.2.1. Вещаемые события ....................................................................................................... 620
31.2.2. Вещаемые оповещения ................................................................................................ 623
31.3. Бэкенд: каналы вещания .................................................................................................... 625
31.3.1. Общедоступные каналы вещания................................................................................ 625
31.3.2. Закрытые каналы вещания ........................................................................................... 625
31.3.2.1. Закрытые каналы вещания: создание ................................................................ 626
31.3.2.2. Закрытые каналы вещания: авторизация........................................................... 626
31.3.3. Каналы присутствия ..................................................................................................... 628
31.4. Фронтенд: прослушивание каналов вещания ................................................................... 629
31.4.1. Использование Laravel Echo ........................................................................................ 629
31.4.2. Прослушивание общедоступных каналов .................................................................. 631
31.4.3. Прослушивание закрытых каналов ............................................................................. 632
31.4.4. Прослушивание каналов присутствия......................................................................... 633
31.4.5. Отправка произвольных уведомлений ........................................................................ 635
31.5. Проблема с laravel-echo-server и ее решение .................................................................... 635
31.6. Запуск вещания ................................................................................................................... 636
Глава 32. Команды утилиты artisan ........................................................................ 637
32.1. Получение сведений о командах утилиты artisan ............................................................ 637
32.2. Команды-классы ................................................................................................................. 638
32.2.1. Создание команд-классов ............................................................................................ 638

Оглавление

17

32.2.2. Описание формата вызова команд .............................................................................. 640
32.2.3. Получение значений аргументов ................................................................................. 641
32.2.4. Получение данных от пользователя ............................................................................ 643
32.2.5. Вывод данных ............................................................................................................... 644
32.2.5.1. Вывод индикатора процесса............................................................................... 645
32.2.6. Вызов из команд других команд ................................................................................. 646
32.2.7. Регистрация команд-классов ....................................................................................... 646
32.3. Команды-функции .............................................................................................................. 647
32.4. Программный вызов команд.............................................................................................. 648
32.5. События утилиты artisan .................................................................................................... 649

Глава 33. Обработка ошибок .................................................................................... 650
33.1. Настройка веб-страниц с сообщениями об ошибках ....................................................... 650
33.2. Создание своих исключений..............................................................................................651
33.2.1. Вывод сообщений об ошибках в коде стандартного обработчика исключений ........ 654
33.3. Подавление исключений .................................................................................................... 654
Глава 34. Журналирование и дополнительные средства отладки .................... 656
34.1. Подсистема журналирования............................................................................................. 656
34.1.1. Настройка подсистемы журналирования ................................................................... 656
34.1.2. Запись сообщений в журнал ........................................................................................ 659
34.1.3. Событие, генерируемое при записи сообщения в журнал ........................................ 660
34.2. Дополнительные средства отладки ................................................................................... 661
Глава 35. Публикация веб-сайта .............................................................................. 663
35.1. Подготовка веб-сайта к публикации ................................................................................. 663
35.1.1. Удаление ненужного кода и данных ........................................................................... 663
35.1.2. Настройка под платформу публикации ...................................................................... 663
35.1.3. Переключение в режим эксплуатации ........................................................................ 664
35.1.4. Задание списка доверенных прокси-серверов ............................................................ 664
35.1.5. Задание списка доверенных хостов............................................................................. 665
35.1.6. Компиляция шаблонов ................................................................................................. 666
35.1.7. Кэширование маршрутов ............................................................................................. 666
35.1.8. Кэширование настроек .................................................................................................667
35.1.9. Кэширование обработчиков событий ......................................................................... 667
35.1.10. Приведение таблиц стилей и веб-сценариев к виду, оптимальному
для публикации ......................................................................................................................... 668
35.2. Перенос веб-сайта на платформу для публикации........................................................... 668
35.3. Настройка веб-сервера и запуск сторонних программ .................................................... 669
35.4. Режим обслуживания.......................................................................................................... 670

Заключение ................................................................................................................... 673
Приложение. Описание электронного архива ....................................................... 675
Предметный указатель .............................................................................................. 677

Предисловие
Laravel — на данный момент самый популярный в мире PHP-фреймворк, предназначенный для разработки веб-сайтов. Более того, он остается номером один
с 2015 года, до сих пор никому не уступив призовое место.

Почему именно Laravel?
Да потому, что:
 Laravel — полнофункциональный фреймворк.
Он содержит все программные подсистемы, необходимые для разработки среднестатистического сайта: шаблонизатор, маршрутизатор, средства разграничения доступа, валидации, сохранения выгруженных файлов, базовую функциональность контроллеров и моделей. После установки самого фреймворка ничего
доустанавливать не нужно.
 Функциональными возможностями Laravel могут похвастаться далеко не все
конкуренты.
Хотите рассылать пользователям сайта короткие оповещения о каких-либо событиях (например, появлении новых статей)? Нет ничего проще: подсистема
оповещений Laravel позволяет отправлять такие оповещения не только традиционно, по электронной почте, но и по SMS. Хотите производить на сайте какиелибо технические работы по определенному расписанию (например, очищать
мусорные данные каждый месяц)? Никаких проблем: встроенный планировщик
Laravel запустит выполнение задачи в нужный момент. Может, желаете повысить отзывчивость сайта, перенеся наиболее «медленные» операции (скажем,
рассылку почты) в параллельный процесс? И это не составит труда — достаточно лишь задействовать удобную подсистему очередей фреймворка!
 У Laravel низкий порог вхождения.
Для программирования сайтов с применением Laravel достаточно базовых знаний PHP и основ веб-разработки. Фреймворк не требует сложного конфигурирования и готов к работе сразу после установки. Отдельные модули, составляющие
код сайта, не требуется явно связывать друг с другом, достаточно «разложить»
их по нужным папкам — и сайт будет прекрасно работать.

20

Предисловие

 Существует множество дополнительных библиотек, расширяющих функцио-

нальность фреймворка, и программ, помогающих в работе.
Библиотеки добавляют фреймворку поддержку BBCode, создания миниатюр
графических изображений, выполнения входа на сайт через сторонние интернетслужбы (например, социальные сети) и многое другое. В число дополнительных
программ входят мощные отладочные панели, гибко настраиваемые административные подсистемы, различные инструментальные утилиты и пр. Часть этих
библиотек и программ написана самим сообществом разработчиков Laravel.
На фоне столь значимых достоинств теряются отдельные недостатки фреймворка:
 местами — чрезмерная функциональность.

Например, существуют функции __() (два подчеркивания) и trans(), выполняющие одинаковое действие. Ряд классов содержат методы, выполняющие одну и ту же задачу. Зачем это было сделано — непонятно…
 плохая официальная документация.

Она неполна, местами поверхностна, местами хаотична. Некоторые ключевые
моменты не описаны, и приходится искать информацию о них в сторонних источниках. Совершенно отсутствует руководство для начинающих, дающее
основные знания в процессе разработки какого-либо учебного сайта (хотя подобное руководство, правда, рассчитанное на старые версии Laravel, есть на сайте русского сообщества поклонников фреймворка).
ВНИМАНИЕ!
Автор предполагает, что читатели этой книги знакомы с языками HTML, CSS, JavaScript,
PHP, принципами работы СУБД и имеют базовые навыки в веб-разработке. В книге
все это описываться не будет.

О чем эта книга?
Автор книги поставил перед собой задачи:
 привести в начале книги вводный курс для начинающих.

Он описывает программирование учебного веб-сайта — простой доски объявлений. И попутно объясняет основные понятия и принципы Laravel;
 дать максимально полное описание всех программных инструментов Laravel,

применяемых при разработке среднестатистического сайта.
«За кадром» остались лишь средства, чья полезность, по мнению автора, сомнительна (наподобие подсистемы автоматического тестирования), или применяемые в крайне специфических случаях (скажем, встроенный HTTP-клиент),
а также создание дополнительных служб и библиотек для Laravel. Также не были
описаны наиболее сложные дополнительные библиотеки и программы: средства
для электронной коммерции, отладочные панели и административные подсистемы — поскольку объем книги ограничен;

Предисловие

21

 в том числе — рассказать о том, о чем молчит официальная документация.

Для чего автор изучал сторонние источники информации, исходные коды
Laravel и активно экспериментировал;
 привести побольше практических примеров применения того или иного про-

граммного инструмента.
Код многих примеров взят с работающего экспериментального сайта, написанного автором.
ЭЛЕКТРОННЫЙ АРХИВ
Сопровождающий книгу электронный архив содержит программный код учебного сайта электронной доски объявлений, разработка которого описывается в части I книги.
Архив доступен для закачки с FTP-сервера издательства «БХВ» по ссылке ftp://
ftp.bhv.ru/9785977566957.zip, ссылка на него также ведет со страницы книги на сайте
https://bhv.ru/ (см. приложение).

Используемое ПО
Автор применял в работе над книгой следующее ПО:
 Microsoft Windows 10, русская 64-разрядная редакция со всеми установленными

обновлениями;
 PHP — 7.4.4;
 Composer — 1.10.15;
 утилита laravel — 4.0.5;
 фреймворк Laravel — 8.10.0;
 laravel/ui — 3.0.0;
 doctrine/dbal — 2.11.3;
 Laravel HTML — 6.2.0;
 genertorg/bbcode — 1.1.2;
 Captcha for Laravel — 3.2.1;
 bkwld/croppa — 4.10.0;
 Laravel Socialite — 5.0.1;
 провайдер «ВКонтакте» для Laravel Socialite — 4.1.0;
 predis — 1.1.6;
 Laravel-lang — 7.0.8;
 laravel-echo-server — 1.6.2;
 Laravel Echo — 1.9.0;
 socket.io — 2.3.0.

22

Предисловие

Типографские соглашения
В книге будут часто приводиться форматы написания различных языковых конструкций, применяемых в PHP. В них использованы особые типографские соглашения, приведенные далее.
 В угловые скобки () заключаются наименования различных значений, под-

ставляемых в исходный код (например, параметров функций и методов), которые дополнительно выделяются курсивом. Например:
environment()

Здесь вместо слова режим должно быть подставлено реальное обозначение режима, в котором работает сайт.
 В квадратные скобки ([]) заключаются фрагменты кода, необязательные к ука-

занию. Например:
php artisan key:generate [--force]

Здесь командный ключ --force может быть указан, а может и не указываться.
У необязательных параметров функций и методов ставятся значения по умолчанию. Пример:
unsignedInteger([, =false])

Здесь у необязательного параметра автоинкрементное значение по умолчанию —
false.
 Вертикальной чертой (|) разделяются различные варианты языковой конструк-

ции, из которых следует указать лишь какой-то один. Пример:
dropColumn(|)

Здесь в качестве параметра метода dropColumn() следует поставить либо имя
поля, либо массив имен полей.
 Слишком длинные, не помещающиеся на одной строке языковые конструкции
автор разрывал на несколько строк и в местах разрывов ставил знак . Например:
background: url("/images/logo.jpg") left / auto 100% no-repeat, 
url("/images/logo.jpg") right / auto 100% no-repeat;

Приведенный код разбит на две строки, но должен быть набран в одну. Символ  при этом нужно удалить.
 Троеточием (. . .) помечены фрагменты кода, пропущенные ради сокращения

объема текста. Пример:

. . .


Здесь пропущено содержимое тега , в данный момент не представляющее интереса.

Предисловие

23

Обычно такое можно встретить в исправленных впоследствии фрагментах
кода — приведены лишь собственно исправленные выражения, а оставшиеся
неизмененными пропущены. Также троеточие используется, чтобы показать,
в какое место должен быть вставлен вновь написанный код, — в начало исходного фрагмента, в его конец или в середину, между уже присутствующими в нем
выражениями.
 Полужирным шрифтом выделен вновь добавленный или исправленный код.

Пример:
class Bb extends Model {
. . .
protected $fillable = ['title', 'content', 'price'];
}

Здесь в класс Bb был добавлен код, объявляющий свойство fillable.
 Зачеркнутым шрифтом выделяется код, подлежащий удалению. Пример:
public function detail(Bb $bb) {
$bb = Bb::find($bb);
$s = $bb->title . "\r\n\r\n";
. . .
}

Первое выражение тела метода detail()следует удалить.

ЧАСТЬ

I

Основы Laravel
на практическом примере
Глава 1.

Простейший веб-сайт — доска объявлений

Глава 2.

Доска объявлений 2.0: разграничение доступа, добавление,
правка и удаление объявлений

ГЛАВА

1

Простейший веб-сайт —
доска объявлений
В этой главе мы начнем писать с применением Laravel простой сайт — электронную доску объявлений. И на практическом примере рассмотрим принципы, положенные в основу этого PHP-фреймворка.

1.1. Подготовительные действия
1. Установим исполняющую среду PHP. Ее дистрибутив и инструкции по установке можно найти на «домашнем» сайте платформы (https://www.php.net/).
Для запуска разрабатываемых сайтов удобнее всего применять отладочный вебсервер, встроенный в PHP. Отдельную программу веб-сервера для этого использовать необязательно.
2. Установим Composer, чей дистрибутив находится на «домашнем» сайте этой
утилиты (https://getcomposer.org/).
Composer — это установщик PHP-библиотек и утилит вместе с зависимостями
(библиотеками, используемыми устанавливаемой библиотекой). Composer самостоятельно ищет указанную библиотеку в интернет-репозитории https://repo.
packagist.org/, загружает и распаковывает ее.
В процессе установки Composer необходимо указать путь к файлу php.exe —
консольной редакции исполняющей среды PHP.
3. Установим утилиту laravel, которая служит для создания новых проектов (о проектах — чуть позже). Для этого откроем командную строку и наберем следующую команду:
composer global require laravel/installer

Получив ее, Composer на уровне системы (основная команда global) установит
(дополнительная команда require) утилиту установщика Laravel (записана в интернет-репозитории под именем laravel/installer).
Когда все установится, начнем разработку сайта, создав его проект.

28

Часть I. Основы Laravel на практическом примере

1.2. Проект и его создание. Папка проекта
Теория
Проект — это совокупность файлов, хранящих программный, HTML- и CSS-код
сайта, а также всевозможные дополнительные данные (например, параметры сайта
и используемую им базу данных). Можно сказать, что проект — это и есть сайт.
Все файлы, составляющие проект, должны храниться в одной папке, называемой
папкой проекта. Папка проекта может иметь произвольное местоположение в файловой системе.
При создании проекта его папка получает то же имя, что и сам проект. Однако
в дальнейшем ее можно переименовать и даже переместить в другое место.
Также следует установить дополнительную библиотеку laravel/ui, служащую для
быстрого создания базовых средств разграничения доступа (с ними мы познакомимся в главе 2). Эта библиотека устанавливается на уровне конкретного проекта,
а не всей системы.

Практика
Создадим проект нашего сайта, дав ему имя bboard.
1. В командной строке выполним переход в папку, в которой будет находиться
папка создаваемого проекта (у автора книги это папка C:\work\projects):
c:
cd \
cd work\projects

2. Создадим проект bboard:
laravel new bboard

Команда new утилиты laravel (установленной в разд. 1.1) создает проект с именем, указанным далее через пробел.
Через некоторое время в текущей папке появится папка bboard, содержащая
только что созданный проект сайта.
Нам понадобится дополнительная библиотека laravel/ui, чтобы в главе 2 быстро
наделить наш сайт средствами разграничения доступа.
3. Перейдем в папку проекта, отдав команду:
cd bboard

4. Установим библиотеку laravel/ui командой:
composer require laravel/ui

Папка проекта хранит множество вложенных папок и файлов. Так, папка app содержит все программные PHP-модули, составляющие код сайта, относящиеся
к разным типам и «разложенные» по разным папкам, папка config — модули конфи-

Глава 1. Простейший веб-сайт — доска объявлений

29

гурации, папка database\migrations — модули миграций, папка public является корневой папкой сайта (в ней можно сохранять файлы с таблицами стилей и вебсценариями), папка resources\views хранит шаблоны, папка routes — модули со списками маршрутов, папка vendor — сам фреймворк и все используемые им библиотеки, а файл .env — локальные настройки проекта. Более подробно содержимое папки
проекта мы рассмотрим в главе 3.
ПОЛЕЗНО ЗНАТЬ
Установщик Composer может устанавливать библиотеки и утилиты на уровне проекта
или на уровне системы.


При установке на уровне проекта — библиотеки и утилиты записываются в папку
vendor текущего проекта и доступны только в текущем проекте. Обычно таким образом устанавливаются библиотеки, используемые в коде проекта. Установка на
уровне проекта выполняется по умолчанию.



При установке на уровне системы — библиотеки и утилиты записываются в папке
\AppData\Roaming\Composer\vendor и доступны везде. Таким образом устанавливаются инструментальные утилиты. Установка на
уровне системы выполняется отдачей основной команды global и дополнительной
команды require.

Только что созданный «пустой» проект Laravel можно запустить на исполнение и открыть в веб-обозревателе.

1.3. Запуск проекта.
Отладочный веб-сервер PHP
1. В командной строке проверим, находимся ли мы в папке проекта (см. разд. 1.2),
и если это не так, перейдем в эту папку.
2. Запустим отладочный веб-сервер PHP:
php artisan serve

Artisan — это утилита, написанная на PHP, хранящаяся непосредственно в папке
проекта и служащая для выполнения различных действий над проектом. Команда serve этой утилиты запускает встроенный в PHP отладочный веб-сервер.
3. Прочитаем появившееся в командной строке сообщение, выведенное утилитой
artisan:
Starting Laravel development server: http://127.0.0.1:8000

В нем говорится, что текущий проект Laravel-сайта скоро будет запущен и доступен через TCP-порт 8000.
4. Запустим веб-обозреватель и перейдем по интернет-адресу http://localhost:8000/.
Веб-обозреватель выведет единственную страницу «пустого» сайта (рис. 1.1).
В процессе работы отладочный веб-сервер будет выводить в командной строке
журнал работы. В каждой строке журнала будут показываться дата и время получения очередного клиентского запроса, TCP-порт и состояние ответа.

30

Часть I. Основы Laravel на практическом примере

Рис. 1.1. Единственная веб-страница «пустого» веб-сайта Laravel

5. Завершим работу отладочного веб-сервера, переключившись в командную строку, где он был запущен, и нажав комбинацию клавиш + или
+.
ЕСЛИ ВЫ ИСПРАВИЛИ ИСХОДНЫЙ КОД,

НО НЕ ВИДИТЕ НА ЭКРАНЕ РЕЗУЛЬТАТА ПРАВОК...

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

Создадим первый контроллер нашего сайта.

1.4. Контроллеры и действия
Теория
Контроллер — это программный модуль, реализующий функциональность одного
из разделов сайта (например, раздела, выводящего объявления). Действие
(action) — одна из операций, выполняемых контроллером (вывод страницы с перечнем объявлений, вывод отдельного объявления, вывод страницы для добавления
объявления, сохранение добавленного объявления в базе и пр.).

Глава 1. Простейший веб-сайт — доска объявлений

31

Сайт может содержать произвольное количество контроллеров (обычно по числу
входящих в него разделов), а каждый контроллер — произвольное количество действий.
Laravel поддерживает три разновидности контроллеров:
 контроллер-класс — реализуется в виде класса, а его действия — в виде методов
этого класса. Позволяет свести всю функциональность раздела сайта в один программный модуль.
По принятому в Laravel соглашению модули с контроллерами-классами сохраняются в папке app\Http\Controllers папки проекта, а имена их классов должны заканчиваться словом Controller;
 контроллер-функция — реализуется в виде анонимной функции и содержит
лишь одно действие. Применяется для выполнения самых простых операций,
наподобие вывода служебной страницы.
Контроллеры-функции записываются непосредственно в списках маршрутов
(о них — чуть позже);
 контроллеры одного действия — нечто промежуточное между классами и
функциями — реализуются в виде классов, но содержат лишь одно действие.

Практика
Напишем контроллер BbsController, обрабатывающий список объявлений, с действием index(), которое в будущем станет выводить страницу с перечнем объявлений, а сейчас — временную «заглушку» в виде обычного текста.
1. В командной строке проверим, находимся ли мы в папке проекта, и если это не
так, перейдем в эту папку.
ИМЕЙТЕ В ВИДУ!
В дальнейшем автор больше не будет напоминать об этом.

2. Создадим контроллер BbsController:
php artisan make:controller BbsController

Команда make:controller утилиты artisan создает модуль с классом контроллера,
чье имя указано после команды через пробел.
3. Откроем только что сгенерированный модуль app\Http\Controllers\BbsController.php
в текстовом редакторе и посмотрим на его код, приведенный в листинге 1.1
(служебный код и комментарии опущены ради краткости).
Листинг 1.1. Код «пустого» контроллера BbsController
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class BbsController extends Controller {
}

32

Часть I. Основы Laravel на практическом примере

Контроллер объявлен в пространстве имен App\Http\Controllers и является производным от суперкласса Controller. Изначально он «пуст» — не содержит ни
одного метода-действия.
4. Объявим в контроллере-классе BbsController действие index(), выводящее временную текстовую «заглушку»:
class BbsController extends Controller {
public function index() {
return response('Здесь будет перечень объявлений.')
->header('Content-Type', 'text/plain');
}
}

Функция response() генерирует серверный ответ, представленный объектом
класса Illuminate\Http\Response, на основе строки, переданной в параметре,
и возвращает его в качестве результата.
Метод header() класса Illuminate\Http\Response помещает в текущий серверный
ответ заголовок с заданными в параметрах именем и значением. Мы используем
этот метод, чтобы поместить в ответ заголовок Content-Type со значением
text/plain, тем самым указав веб-обозревателю, что ответ содержит обычный
текст.
Готовый ответ следует вернуть из метода-действия в качестве результата —
чтобы Laravel смог отправить его клиенту.
НЕ ЗАБЫВАЕМ СОХРАНЯТЬ ИСПРАВЛЕННЫЕ ФАЙЛЫ!
Автор далее не будет напоминать об этом.

5. Запустим отладочный веб-сервер и откроем разрабатываемый сайт в веб-обозревателе.
В результате мы опять увидим страницу, показанную на рис. 1.1, но не заданную
нами текстовую «заглушку». А все потому, что мы не исправили маршрут.
ПОЛЕЗНО ЗНАТЬ


В Laravel используется принятое в PHP соглашение, согласно которому вложенные
друг в друга пространства имен, в которых объявлен класс, должны соответствовать вложенным друг в друга папкам файловой системы, в которых хранится модуль с кодом этого класса. Так, код класса App\Http\Controllers\BbsController
должен храниться в модуле app\Http\Controllers\BbsController.php.
Следование этому соглашению позволяет программному ядру Laravel быстро найти модуль с нужным классом.



Итак, класс App\Http\Controllers\Controller, являющийся базовым для всех контроллеров-классов Laravel, хранится в модуле app\Http\Controllers\Controller.php. Изначально он «пуст» — лишь включает три трейта (которые мы рассмотрим в следующих главах).

Глава 1. Простейший веб-сайт — доска объявлений

33

1.5. Маршруты и списки маршрутов.
Фасады
Теория
Каждая операция, производимая сайтом (вывод страницы, сохранение введенных
данных в базе и пр.), выполняется при получении им от веб-обозревателя клиентского запроса по определенному пути, выполненного с применением определенного HTTP-метода (GET, POST, PATCH и др.).
Путь — это часть интернет-адреса, находящаяся между адресом хоста и набором
GET-параметров и идентифицирующая запрашиваемую страницу (например, интернет-адрес http://localhost:8000/items/34?from=index содержит путь items/34).
Следовательно, чтобы какое-либо действие контроллера выполнилось при получении запроса по определенному пути, выполненного определенным HTTP-методом,
его следует связать с этими путем и методом, создав маршрут.
Маршрут Laravel — это объект особого класса, содержащий следующие сведения:
 шаблонный путь — задает нужный формат путей;
 допустимый HTTP-метод — которым должен быть выполнен клиентский

запрос;

 действие контроллера — выполняется при совпадении шаблонного пути и до-

пустимого метода с путем и методом, извлеченными из запроса (т. е. если маршрут является совпавшим).

В качестве примера рассмотрим следующие маршруты (записаны в формате «шаблонный путь — допустимый метод — выполняемая операция»):
 / (прямой слеш — «корень» сайта) — GET — вывод перечня объявлений;
 // — GET — вывод объявления с заданным ключом;
 /add/ — GET — вывод страницы для добавления объявления;
 / — POST — сохранение добавленного объявления в базе.

Созданные маршруты записываются в один из двух списков:
 список веб-маршрутов — содержит список маршрутов, ведущих на действия

контроллеров, которые выдают обычные веб-страницы. Хранится в модуле
routes\web.php;

 список API-маршрутов — содержит список маршрутов, ведущих на действия

контроллеров, которые выдают данные в формате JSON. Хранится в модуле
routes\api.php.

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

34

Часть I. Основы Laravel на практическом примере

Практика
Создадим веб-маршрут, связывающий шаблонный путь / («корень» сайта) и допустимый HTTP-метод GET с действием index() контроллера BbsController.
1. Откроем модуль routes\web.php со списком веб-маршрутов в текстовом редакторе
и посмотрим на имеющийся там код (листинг 1.2) — служебный код и комментарии в нем опущены.
Листинг 1.2. Код списка маршрутов routes\web.php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});

Класс Route — это фасад маршрутизатора (фасадом называется класс, служащий
своего рода «пультом управления» одной из подсистем фреймворка). Статический метод get(), вызванный у этого фасада, указывает маршрутизатору создать
новый объект маршрута, связывающий допустимый HTTP-метод GET (одноименный методу фасада), шаблонный путь из первого параметра (у нас — /,
«корень» сайта) и контроллер-функцию, заданную вторым параметром. Последний генерирует на основе шаблона welcome.blade.php (шаблонами мы займемся
далее в этой главе) страницу, что показана на рис. 1.1.
2. Свяжем изначально созданный маршрут с действием index() контроллера
BbsController, переписав его код следующим образом:
use App\Http\Controllers\BbsController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/', [BbsController::class, 'index']);

Запустим отладочный веб-сервер, откроем сайт и посмотрим на выведенную текстовую «заглушку» (рис. 1.2).

Рис. 1.2. Текстовая «заглушка», временно выводимая вместо перечня объявлений

Это была разминка. Сейчас мы займемся серьезным делом — создадим базу данных, запишем в нее пару-тройку объявлений и будем генерировать перечень объявлений на основе содержимого базы. Но сначала произведем необходимые настройки.

Глава 1. Простейший веб-сайт — доска объявлений

35

1.6. Настройки проекта. Подготовка проекта
к работе с базой данных SQLite
Теория
Настройки Laravel-проекта хранятся в двух местах:
 в файле .env — локальные, задающие параметры текущей платформы (сведения

для подключения к базам данных, серверу электронной почты, службе кэширования и пр.);

 в папке config — рабочие, затрагивающие все аспекты функционирования сайта и

непосредственно используемые Laravel для получения всех сведений о нем.

Рабочие настройки хранятся в 14 разных PHP-модулях. Так, модуль app.php содержит настройки самого проекта (его имя, режим работы и др.), модуль
database.php — настройки подключения к базе данных, а cache.php — настройки
подсистемы кэширования.
Каждый из этих модулей включает ассоциативный массив, элементы которых
содержат значения соответствующих настроек — как обычные скалярные величины, так и массивы, в том числе ассоциативные.
Значения некоторых рабочих настроек загружаются из файла .env. Таким образом, Laravel фактически объединяет локальные и рабочие настройки для удобства программирования.

Практика
Настроим проект для работы с базой данных формата SQLite, хранящейся в файле
database\data.sqlite, и заодно создадим эту базу данных.
1. Откроем файл .env, хранящий локальные настройки, в текстовом редакторе и
найдем в нем фрагмент кода, настраивающий подключение к базе данных:
DB_CONNECTION=mysql
. . .
DB_DATABASE=laravel

2. Укажем формат базы данных SQLite и дадим файлу, хранящему базу, имя
data.sqlite, исправив значения настроек DB_CONNECTION и DB_DATABASE следующим образом:
DB_CONNECTION=sqlite
. . .
DB_DATABASE=data.sqlite

В настройке DB_DATABASE мы указали лишь имя файла, в то время как фреймворку для работы с базой SQLite требуется дать абсолютный путь к файлу. Сейчас
сделаем так, чтобы этот путь формировался автоматически.

36

Часть I. Основы Laravel на практическом примере

3. Откроем модуль config\database.php, хранящий рабочие настройки баз данных,
в текстовом редакторе и найдем в нем следующий код:
return [
. . .
'connections' => [
'sqlite' => [
. . .
'database' => env('DB_DATABASE',
database_path('database.sqlite')),
. . .
],
. . .
],
. . .
];

Настройка connections.sqlite.database задает абсолютный путь к файлу базы
данных.
Функция env() возвращает значение, записанное в файле .env, в настройке, чье
имяуказано в первом параметре. Если в файле .env нет такой настройки, функция env() возвращает значение заданного в ней второго параметра.
Функция database_path() принимает в качестве параметра относительный путь
к файлу, заданный от папки database, и возвращает абсолютный путь этого файла.
Таким образом, изначально в рабочую настройку connections.sqlite.database
заносится значение локальной настройки DB_DATABASE или, если таковая отсутствует, — абсолютный путь к несуществующему файлу database\database.sqlite.
К сожалению, значение, извлекаемое из настройки DB_DATABASE файла .env, не
«пропускается» через функцию database_path(), что вынуждает программистов
указывать там абсолютный путь к файлу с базой. Устраним эту досадную недоработку разработчиков Laravel, для чего...
4. ...перепишем приведенный ранее код следующим образом:
. . .
'database' => database_path(env('DB_DATABASE',
'database.sqlite')),
. . .

5. Создадим в папке database «пустой» файл data.sqlite.
Проще всего сделать это, воспользовавшись контекстным меню Создать | Текстовый документ и переименовав получившийся файл в data.sqlite.
Итак, база данных у нас есть. Осталось создать в ней необходимые таблицы, поля,
индексы и связи. Используем для этого миграцию.

Глава 1. Простейший веб-сайт — доска объявлений

37

1.7. Миграции
Теория
Миграция — программный PHP-модуль, вносящий какие-либо изменения в структуру базы данных. Миграция может, например, создать таблицу вместе со всеми
полями и индексами, исправить имя или тип поля в существующей таблице, создать или удалить индекс. Миграция реализуется в виде класса.
Миграцию можно применить или откатить. При применении миграция выполняет
все описанные в ней действия. При откате же она возвращает базу данных в состояние, существовавшее перед применением этой миграции. Понятно, что откатить можно лишь миграцию, примененную ранее.
Программные модули с миграциями хранятся в папке database\migrations. Список
всех примененных миграций в хронологическом порядке сохраняется в особой таблице базы данных, создаваемой перед применением самой первой миграции.

Практика
Напишем миграцию create_bbs_table, создающую в базе данных таблицу bbs со
следующими полями:
 title — заголовок объявления с названием продаваемого товара (тип — строко-

вый, длина — 50 символов);

 content — сам текст объявления, описание товара (тип — memo);
 price — цена (тип — вещественное число).

1. В командной строке создадим «пустую» миграцию create_bbs_table:
php artisan make:migration create_bbs_table --create=bbs

Команда make:migration утилиты artisan создает миграцию с заданным именем.
Дополнительный параметр --create предписывает вставить в миграцию код,
создающий таблицу, чье имя указано в параметре.
2. Откроем только что созданный модуль с именем формата database\migrations\
_create_bbs_table.php, хранящий созданную нами
миграцию, в текстовом редакторе и посмотрим на содержащийся в нем код (листинг 1.3).
Листинг 1.3. Код «пустой» миграции CreateBbsTable
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateBbsTable extends Migration {
public function up() {

38

Часть I. Основы Laravel на практическом примере
Schema::create('bbs', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
public function down() {
Schema::dropIfExists('bbs');
}
}

Класс миграции содержит два метода: up(), выполняющийся при применении
миграции, и down(), запускаемый при ее откате. Параметр --create предписывает утилите artisan сразу же вставить в эти методы код, соответственно создающий и удаляющий заданную в параметре таблицу.
В методе up() миграции выполняется создание таблицы. Для этого у фасада
за которым «прячется» подсистема, манипулирующая структурой базы
данных, вызывается метод create(). В первом параметре методу передается имя
создаваемой таблицы, а во втором — анонимная функция, в качестве параметра
принимающая объект класса Blueprint, который представляет структуру создаваемой таблицы. Добавление полей в нее выполняется вызовом различных методов у этого объекта.

Schema,

Параметр --create также предписывает утилите artisan вставить в код этой
функции вызовы методов id() и timestamps(). Первый метод добавляет в формируемую таблицу поле id, хранящее ключ (какое-либо уникальное значение,
однозначно идентифицирующее запись, обычно — уникальный номер, генерируемый встроенным автоинкрементом. Поле, хранящее ключ, называется ключевым), а второй — поля для хранения временны́х отметок создания записи (отметка создания) и ее последней правки (отметка правки).
В методе down() производится удаление таблицы. Для этого у фасада Schema вызывается метод dropIfExists(), которому передается имя удаляемой таблицы.
3. Добавим в метод up() миграции код, создающий поля title, content и price:
public function up() {
Schema::create('bbs', function (Blueprint $table) {
$table->id();
$table->string('title', 50);
$table->text('content');
$table->float('price');
$table->timestamps();
});
}

Метод string() класса Blueprint создает строковое поле типа VARCHAR с именем
и длиной, заданными в параметрах метода. Для создания поля memo типа TEXT
и поля вещественного типа FLOAT используются методы text() и float().

Глава 1. Простейший веб-сайт — доска объявлений

39

Мы будем выводить объявления в хронологическом порядке, отсортированными
по значению поля отметки создания записи. Это поле создается вызовом метода
timestamps() и по умолчанию имеет имя created_at. Для ускорения сортировки
создадим по нему индекс.
4. Добавим код, создающий обычный индекс по полю created_at:
public function up() {
Schema::create('bbs', function (Blueprint $table) {
. . .
$table->timestamps();
$table->index('created_at');
});
}

Мы вызвали метод index() класса Blueprint, задав в параметре имя индексируемого поля.
5. Применим только что созданную миграцию, набрав в командной строке команду:
php artisan migrate

В результате Laravel выполнит все еще не выполненные миграции, находящиеся
в папке database\migrations.
ПОЛЕЗНО ЗНАТЬ
Только что созданный проект Laravel изначально уже содержит две миграции, одна из
которых создает таблицу со списком зарегистрированных пользователей (подробности будут приведены в главе 13), а другая — таблицу со списком проваленных заданий (см. главу 25). При первом выполнении миграций они также будут выполнены.

1.8. Модели
Модель — программный модуль, служащий для взаимодействия с обслуживаемой
им таблицей базы данных: выборки записей, извлечения значений полей, добавления, правки и удаления записей. Модель реализуется в виде класса.
Файлы с моделями по умолчанию хранятся в папке app\Models.
Напишем модель Bb, предназначенную для работы с таблицей bb базы данных.
1. В командной строке создадим модуль с классом модели:
php artisan make:model Bb

2. Откроем модуль app\Models\bb.php, хранящий созданную модель, в текстовом
редакторе и посмотрим на его содержимое (листинг 1.4).
Листинг 1.4. Код «пустой» модели Bb
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

40

Часть I. Основы Laravel на практическом примере
class Bb extends Model {
use HasFactory;
}

Всю функциональность модели, необходимую для работы с базой данных, реализует базовый класс Illuminate\Database\Eloquent\Model. Трейт Illuminate\
Database\Eloquent\Factories\HasFactory используется лишь при автоматизированном тестировании (которое в этой книге не описывается), так что его можно
удалить.
3. Сделаем поля title, content и price доступными для массового присваивания
(о нем — чуть позже), добавив в класс модели выделенный полужирным шрифтом код, и заодно удалим трейт HasFactory:
class Bb extends Model {
use HasFactory;
protected $fillable = ['title', 'content', 'price'];
}

Массив с именами полей, доступных для массового присваивания, заносится
в защищенное свойство fillable.

1.9. Консоль Laravel
Консоль Laravel позволяет работать с классами фреймворка в интерактивном
режиме. В частности, с ее помощью удобно заносить в информационную базу какие-либо отладочные данные.
1. В командной строке запустим консоль Laravel:
php artisan tinker

Приглашение к вводу команды обозначается префиксом >>>. Вывод результатов
производится без всякого префикса.
2. Проверим консоль в работе, выведя полное имя класса модели Bb, для чего наберем следующие выражения, завершая каждое из них нажатием клавиши :
>>> use App\Models\Bb;
>>> echo Bb::class;
App\Models\Bb

Следует помнить, что префиксом >>> обозначается приглашение к вводу команды. Результат выполнения команды выводится без префикса.
Консоль Laravel позволяет набирать выражения в несколько строк, разрывая их
нажатием клавиши .
3. Введем последнее из набранных выражений в две строки:
>>> use
... App\Models\Bb;

Глава 1. Простейший веб-сайт — доска объявлений

41

Префиксом ... (три точки) обозначается приглашение к вводу следующей строки многострочного выражения.
4. Завершим работу консоли Laravel, нажав комбинацию клавиш +
или +.
Также можно набрать в консоли команду exit.

1.10. Работа с базой данных
Добавим несколько записей в таблицу объявлений bb, исправим какую-либо запись,
удалим другую и попробуем выполнить выборку записей с фильтрацией и сортировкой. Затем переделаем действие index() контроллера HomeController таким
образом, чтобы оно выводило перечень объявлений из базы данных.
1. Запустим консоль Laravel (как это сделать, было рассказано в разд. 1.9).
2. Добавим первое объявление, набрав код:
>>> use App\Models\Bb;
>>> $bb = new Bb();
=> App\Models\Bb {#3019}
>>> $bb->title = 'Шкаф';
=> "Шкаф"
>>> $bb->content = 'Совсем новый, полированный, двухстворчатый';
=> "Совсем новый, полированный, двухстворчатый"
>>> $bb->price = 2000;
=> 2000
>>> $bb->save();
=> true

Если выполненное выражение возвращает какой-либо результат, но не предполагает его явного вывода, результат все равно выводится в следующей строке,
предваренный префиксом =>. Далее для краткости вывод такого рода, если он не
нужен, показываться не будет.
Объект модели представляет одну запись таблицы. Следовательно, для добавления записи мы можем создать новый объект модели, занести в его свойства,
представляющие отдельные поля, нужные значения и вызвать метод save(), выполняющий сохранение записи.
Метод save() вернет значение true (см. приведенный ранее код) — это значит,
что запись была успешно сохранена.
3. Удостоверимся, что объявление действительно сохранилось в таблице, для чего
выведем его ключ, хранящийся в поле id:
>>> echo $bb->id;
1

Как видим, запись № 1 действительно сохранилась.

42

Часть I. Основы Laravel на практическом примере

4. Добавим другое объявление — другим способом:
>>> $bb = $bb->create(['title' => 'Пылесос',
...
'content' => 'Старый, ржавый, без шланга', 'price' => 1000]);

Метод create() создает новую запись, заносит в ее поля значения из указанного
ассоциативного массива, сохраняет запись и возвращает ее в качестве результата. Он использует массовое присваивание, при котором значения заносятся сразу
в несколько полей создаваемой записи. Отметим, что таким образом можно занести значения только в поля, перечисленные в списке доступных для массового
присваивания (задается свойством fillable класса модели — см. разд. 1.8).
Любопытно, что метод create(), хотя и вызывается у модели, но выполняется
построителем запросов — подсистемой, которая на основании заданных нами
параметров выборки данных (условий фильтрации, сортировки записей, набора
выводимых полей и др.) формирует готовый SQL-запрос, отправляет его СУБД,
получает результат и представляет его в удобном для обработки виде. При попытке вызвать метод построителя запросов у модели ее объект создает новый
объект построителя запросов и «передает» ему вызов метода.
5. Добавим еще два объявления:
>>> $bb = Bb::create(['title' => 'Грузовик',
...
'content' => 'Грузоподъемность - 5 т', 'price' => 10000000]);
>>> $bb = Bb::create(['title' => 'Снег', 'content' => 'Прошлогодний',
...
'price' => 50]);

Методы построителя запроса можно вызывать не только у объекта модели, но и
у ее класса — как статические.
6. Извлечем запись № 2:
>>> $bb = Bb::find(2);

Метод find(), опять же, выполняемый построителем запросов, ищет и возвращает объект модели, хранящий запись с заданным ключом.
7. Посмотрим, что это за объявление:
>>> echo $bb->title, ' | ', $bb->content, ' | ', $bb->price;
Пылесос | Старый, ржавый, без шланга | 1000.0

Получить значения, хранящиеся в полях записи, можно, обратившись к одноименным свойствам модели.
Тысяча рублей за старый пылесос без шланга — не многовато ли?.. Уценим его.
8. Изменим цену товара (значение поля price):
>>> $bb->price = 500;
>>> $bb->save();

Мы занесли новое значение в нужное свойство модели и вызвали у объекта записи метод save().
9. Извлечем все объявления, отсортированные по увеличению цены:
>>> $bbs = Bb::orderBy('price')->get();

Глава 1. Простейший веб-сайт — доска объявлений

43

Метод orderBy() построителя запросов сортирует записи по указанному полю.
В качестве результата он возвращает тот же объект построителя запросов, что
позволяет записывать «цепочки» методов.
Мы и так сделали, «сцепив» с методом orderBy() метод get(), который отправит базе данных готовый SQL-запрос и вернет результат его выполнения в виде
коллекции объектов модели Bb, хранящих выбранные записи.
10. Просмотрим отсортированные объявления:
>>> foreach ($bbs as $bb) {
...
echo $bb->title, ' | ', $bb->content, ' | ', $bb->price, "\r\n";
... }
Снег | Прошлогодний | 50.0
Пылесос | Старый, ржавый, без шланга | 500.0
Шкаф | Совсем новый, полированный, двухстворчатый | 2000.0
Грузовик | Грузоподъемность - 5 т | 10000000.0

Коллекцию записей, возвращенную методом get(), как и любую другую, можно перебрать в цикле.
11. Извлечем объявления с ценами более 1000 рублей и выведем их в обратном
хронологическом порядке:
>>> $bbs = Bb::where('price', '>', 1000)->latest()->get();
>>> foreach ($bbs as $bb) {
...
echo $bb->title, ' | ', $bb->created_at, "\r\n";
... }
Грузовик | 2020-04-13 12:11:59
Шкаф | 2020-04-13 10:27:12

Метод where() построителя запросов фильтрует записи по значению поля, указанного в первом параметре. Второй параметр задает SQL-оператор сравнения,
а третий — сравниваемое значение.
Метод latest() сортирует записи по убыванию значения поля отметки создания (т. е. в обратном хронологическом порядке). Знакомый нам метод get()
выполнит запрос и вернет результат.
12. Удалим объявление о продаже прошлогоднего снега (вряд ли на него будет
спрос):
>>> $bb = Bb::where('title', 'Снег')->first();
>>> $bb->delete();

Ищем объявление вызовом метода where() (поскольку оператор сравнения
в нем не указан, будет использован оператор равенства =). Метод first() возвращает первую запись из полученного результата. А метод delete() модели
удаляет текущую запись.
13. Добавим еще несколько объявлений о продаже каких-либо товаров с произвольными содержанием и ценами.

44

Часть I. Основы Laravel на практическом примере

14. Откроем модуль app\Http\Controllers\BbsController.php, хранящий код контроллера
BbsController, в текстовом редакторе и переделаем действие index(), чтобы оно
выводило перечень объявлений, взятых из базы данных, в обратном хронологическом порядке:
use App\Models\Bb;
class BbsController extends Controller {
public function index() {
$bbs = Bb::latest()->get();
$s = "Объявления\r\n\r\n";
foreach ($bbs as $bb) {
$s .= $bb->title . "\r\n";
$s .= $bb->price . " руб.\r\n";
$s .= "\r\n";
}
return response('Здесь будет перечень объявлений.'$s)
->header('Content-Type', 'text/plain');
}
}

Запустив отладочный веб-сервер и открыв сайт, мы увидим перечень, показанный
на рис. 1.3.
Теперь сделаем так, чтобы при переходе по пути формат // наш
сайт выводил объявление с записанным в пути ключом.

Рис. 1.3. Перечень объявлений, извлеченный из базы данных

Глава 1. Простейший веб-сайт — доска объявлений

45

1.11. URL-параметры.
Внедрение зависимостей
Теория
URL-параметр — значение, присутствующее в пути, который был получен в составе клиентского запроса. Обозначение URL-параметра записывается в шаблонном пути под уникальным именем.
Значение, передаваемое в пути посредством URL-параметра, впоследствии отправляется связанному с маршрутом действию контроллера через параметр, имя которого совпадает с именем самого URL-параметра.
Например, чтобы извлечь из пути формата // записанный там
ключ, мы можем создать в шаблонном пути маршрута URL-параметр с именем bb.
Чтобы отправить его действию, связанному с этим маршрутом, мы объявим в действии параметр с тем же именем — bb.
Совпадение имен URL-параметра и параметра действия служит фреймворку своего
рода «подсказкой» занести в параметр действия значение одноименного URLпараметра. Такого рода автоматическое занесение в параметры методов требуемых
значений, основанное на совпадении имен и (или) типов этих параметров, называется внедрением зависимостей.

Практика
Реализуем вывод отдельного объявления, выбранного посетителем сайта. Для этого
добавим в контроллер BbsController действие detail(), которое будет выполняться
при получении запроса, произведенного HTTP-методом GET по пути формата
//. Для извлечения из пути ключа объявления объявим в шаблонном пути URL-параметр bb.
1. Откроем модуль routes\web.php, хранящий список веб-маршрутов, и добавим
в него маршрут, связанный с действием detail() контроллера HomeController:
Route::get('/', [BbsController::class, 'index']);
Route::get('/{bb}', [BbsController::class, 'detail']);

Конечный слеш в шаблонных путях не ставится. Обозначения URL-параметров
берутся в фигурные скобки (например, {bb} — это URL-параметр с именем bb).
2. Откроем модуль app\Http\Controllers\BbsController.php, хранящий код контроллера
BbsController, в текстовом редакторе и добавим действие detail():
class BbsController extends Controller {
. . .
public function detail($bb) {
$bb = Bb::find($bb);
$s = $bb->title . "\r\n\r\n";
$s .= $bb->content . "\r\n";
$s .= $bb->price . " руб.\r\n";

46

Часть I. Основы Laravel на практическом примере
return response($s)->header('Content-Type', 'text/plain');
}
}

В объявлении метода detail() мы указали параметр с именем bb. Laravel сразу
«сообразит», что в него нужно поместить значение одноименного URL-параметра.
3. Запустим отладочный сервер и выполним переход на объявление № 1, набрав
интернет-адрес http://localhost:8000/1/. Результат показан на рис. 1.4.

Рис. 1.4. Объявление № 1

Попробуем вывести объявления с номерами 2, 3 и т. д., только не объявление
№ 4, которое мы удалили (если попытаемся вывести его, получим страницу
с сообщением об ошибке).
Теперь немного сократим код действия detail().
4. Предпишем фреймворку помещать в параметр bb действия detail() не ключ выводимой записи, а сразу объект этой записи, внеся в код следующие правки:
public function detail(Bb $bb) {
$bb = Bb::find($bb);
$s = $bb->title . "\r\n\r\n";
. . .
}

Мы указали у параметра bb действия класс модели Bb в качестве типа — тогда
«сообразительный» Laravel сам найдет запись по полученному ключу и подставит содержащий ее объект модели в этот параметр. Выражение, выполняющее
поиск записи, станет ненужным и может быть удалено.
Снова протестируем сайт и убедимся, что он работает.
Настала пора сделать так, чтобы наш сайт выдавал результаты не обычным текстом, а в виде полноценных веб-страниц.
ПОЛЕЗНО ЗНАТЬ
Не все веб-фреймворки реализуют внедрение зависимостей. Например, популярнейший фреймворк Django, написанный на языке Python, такого не «умеет».

Глава 1. Простейший веб-сайт — доска объявлений

47

1.12. Шаблоны
Теория
Шаблон — это образец для генерирования страницы, отправляемой клиенту в составе ответа. Процесс генерирования страницы называется рендерингом, а подсистема фреймворка, выполняющая рендеринг, — шаблонизатором.
Для рендеринга шаблонизатору, помимо шаблона, нужны данные, которые будут
выводиться на генерируемой странице (в нашем случае это перечень объявлений и
содержание выбранного объявления). Эти данные оформляются в виде особого набора, называемого контекстом шаблона. Контекст шаблона формируется контроллером в виде ассоциативного массива, который преобразуется шаблонизатором
в набор обычных переменных, доступных внутри шаблона.
В Laravel шаблоны сохраняются в файлах с расширением blade.php и помещаются
в папке resources\views или вложенных в нее папках.

Практика
Реализуем вывод перечня объявлений и содержимого выбранного объявления в виде обычных веб-страниц, для чего напишем шаблоны index.blade.php и detail.blade.php
соответственно. Для оформления страниц применим популярный CSS-фреймворк
Bootstrap (https://getbootstrap.com/).
1. Откроем модуль app\Http\Controllers\BbsController.php, хранящий код контроллера
BbsController, и перепишем действие index() таким образом, чтобы оно формировало страницу на основе шаблона index.blade.php:
public function index() {
$context = ['bbs' => Bb::latest()->get()];
return view('index', $context);
}

Мы создаем контекст шаблона в виде ассоциативного массива и помещаем в него один элемент bbs — список объявлений, извлеченный из базы. При рендеринге этот элемент будет преобразован в переменную bbs, доступную внутри шаблона.
Функция view() готовит шаблон к рендерингу (сам рендеринг выполняется позже, непосредственно перед отправкой ответа клиенту). В первом параметре она
принимает имя шаблона без расширения blade.php, а во втором — контекст шаблона. Возвращенный ей результат — подготовленный к рендерингу шаблон —
следует вернуть из действия в качестве результата.
Кстати, с функцией view() мы уже знакомы — видели ее в коде контроллера-функции, выводящей страницу, показанную на рис. 1.1 (см. листинг 1.2). Эта
страница формируется шаблоном resources\views\welcome.blade.php, который теперь можно удалить, поскольку он больше не нужен.

48

Часть I. Основы Laravel на практическом примере

2. Создадим шаблон resources\views\index.blade.php и запишем в него «заготовку»
шаблона, создающего страницу с перечнем объявлений, из листинга 1.5.
Листинг 1.5. Код «заготовки» шаблона resources\views\index.blade.php




Объявления



Объявления



Товар
Цена, руб.
 







Подробнее...








Для вывода перечня объявлений применяется обычная таблица из трех столбцов: названия товара, его цены и гиперссылки Подробнее..., ведущей на страницу объявления. К тегам привязаны специфические стилевые классы Bootstrap,
задающие оформление для элементов страницы.
3. Перейдем
на
страницу
https://getbootstrap.com/docs/4.4/getting-started/
1
introduction/ , найдем под заголовком CSS HTML-код, привязывающий таблицу
1

На эту страницу также можно попасть, открыв «домашний» сайт Bootstrap (https://getbootstrap.com/)
и перейдя в раздел Documentation | Getting started | Introduction.

Глава 1. Простейший веб-сайт — доска объявлений

49

стилей Bootstrap (он содержит тег ), и вставим его в секцию заголовка
шаблона:

. . .
Объявления



4. Найдем на той же странице под заголовком JS HTML-код, привязывающий
файлы сценариев Bootstrap (три тега ), и вставим его туда же, под только что вставленным фрагментом:

. . .
Объявления






В секции основного содержания таблицы (теге ) присутствует «заготовка» строки, в которой будет выводиться очередное объявление (формирующий
ее тег здесь подчеркнут):





Подробнее...




5. Добавим в шаблон код, который выведет строку-«заготовку» столько раз, сколько объявлений имеется в списке (как мы помним, он хранится в переменной bbs,
доступной в шаблоне):

@foreach ($bbs as $bb)

. . .

@endforeach


Шаблонизатор Laravel предоставляет набор своих собственных команд, аналогичных языковым конструкциям PHP и называемых директивами. Так, парная
директива @foreach . . . @endforeach аналогична циклу по массиву foreach,

50

Часть I. Основы Laravel на практическом примере

т. е. выводит помещенный в ней код столько раз, сколько элементов присутствует в заданном массиве или коллекции, и помещает очередной элемент в заданную переменную (у нас — bb), доступную в «теле» цикла.
6. Добавим код, выводящий название товара, цену из очередного объявления и
формирующий интернет-адрес гиперссылки Подробнее...:

@foreach ($bbs as $bb)

{{ $bb->title }}
{{ $bb->price }}

Подробнее...


@endforeach


Для вывода какого-либо значения применяется директива шаблонизатора
{{ . . . }}. Выводимое значение помещается между двойными фигурными
скобками.
Осталось сделать так, чтобы, если в списке нет объявлений, таблица вообще не
выводилась на странице.
7. Добавим код, выводящий таблицу лишь в том случае, если список объявлений
не пуст:
@if (count($bbs) > 0)

. . .

@endif

Парная директива @if . . . @endif шаблонизатора аналогична по назначению
условному выражению PHP.
Страница перечня объявлений готова. Приступим к странице, выводящей содержание выбранного объявления.
8. Перепишем действие detail() контроллера BbsController таким образом, чтобы
оно выводило страницу, основанную на шаблоне detail.blade.php:
public function detail(Bb $bb) {
return view('detail', ['bb' => $bb]);
}

9. Пересохраним шаблон index.blade.php под именем detail.blade.php в той же папке
resources\views.
10. Запишем в шаблон detail.blade.php код, выводящий содержание выбранного объявления:

Глава 1. Простейший веб-сайт — доска объявлений

51




{{ $bb->title }} :: Объявления
. . .



Объявления
{{ $bb->title }}
{{ $bb->content }}
{{ $bb->price }} руб.
На перечень объявлений




Запустим отладочный сервер и откроем сайт. Посмотрим, правильно ли выводится
главная страница с перечнем объявлений (рис. 1.5). Щелкнем на любой из гиперссылок Подробнее... и проверим, как отображается выбранное объявление
(рис. 1.6).
Наши шаблоны содержат много одинакового кода — в частности, объемистые теги
и , привязывающие к страницам фреймворк Bootstrap. Мало того,
что это увеличивает размер шаблонов и затрудняет сопровождение, но и характеризует плохой стиль программирования. Решим эту проблему.

Рис. 1.5. Главная веб-страница с перечнем объявлений

52

Часть I. Основы Laravel на практическом примере

Рис. 1.6. Веб-страница с содержанием выбранного объявления

1.13. Наследование шаблонов
Теория
Подобно классам PHP, шаблоны Laravel могут наследовать друг от друга содержание (наследование шаблонов Laravel).
В базовом шаблоне записывается код, общий для всех страниц сайта: структурирующие теги, метаданные, привязки таблиц стилей и веб-сценариев, теги, формирующие разметку, шапку, поддон, панель навигации и др. Производные шаблоны,
напротив, определяют содержание, уникальное для каждой конкретной страницы.
Фрагменты уникального содержания в производных шаблонах оформляются в виде
так называемых секций, имеющий уникальные имена. В базовом шаблоне помечаются места, куда будет помещено содержание той или иной секции, идентифицируемой по ее имени.
По принятому в Laravel соглашению базовые шаблоны хранятся в папке layouts,
вложенной в папку resources\views.

Практика
Создадим базовый шаблон resources\views\layouts\base.blade.php, в который вынесем
все повторяющиеся элементы из шаблонов index.blade.php и detail.blade.php. Оба этих
шаблона сделаем производными от layouts\base.blade.php.
1. Создадим в папке resources\views папку layouts для хранения базового шаблона.
2. Откроем шаблон resources\views\index.blade.php (или detail.blade.php из той же папки) в текстовом редакторе и пересохраним его под именем resources\views\
layouts\base.blade.php.
3. Исправим код базового шаблона resources\views\layouts\base.blade.php следующим
образом:

Глава 1. Простейший веб-сайт — доска объявлений

53




@yield('title') :: Объявления
. . .



Объявления
@yield('main')




Директива @yield шаблонизатора выводит на страницу содержание секции
с именем, указанным в скобках. Мы создали секции title и main — для вывода
соответственно заголовка раздела сайта и основного содержания.
4. Откроем шаблон resources\views\index.blade.php и превратим его в производный от
только что написанного базового шаблона, внеся следующие правки:
@extends('layouts.base')
@section('title', 'Главная')
@section('main')
@if (count($bbs) > 0)

. . .

@endif
@endsection('main')

Директива @extends указывает базовый шаблон, от которого наследует текущий.
Для разделения имен папок и файлов в путях к шаблонам используются не слеши, а точки.
Директива @section создает секцию, чье имя указано в первом параметре. Она
может быть записана в двух форматах:
• как одинарная — тогда содержание секции задается вторым параметром;
• как парная (@section . . . @endsection) — тогда содержание помещается
в тело директивы.
5. Внесем аналогичные правки в шаблон resources\views\detail.blade.php:
@extends('layouts.base')
@section('title', $bb->title)

54

Часть I. Основы Laravel на практическом примере
@section('main')
{{ $bb->title }}
. . .
На перечень объявлений
@endsection('main')

Проверим сайт в работе и убедимся, что все страницы выводятся правильно.
Наш сайт имеет недостаток: интернет-адреса в гиперссылках генерируются непосредственно в программном коде. Например, интернет-адреса страниц объявлений
формата // генерируются так (выводящий их код подчеркнут):
Подробнее...

Если мы решим поменять формат этих адресов, скажем, на /detail//, то будем вынуждены вносить правки во все шаблоны, в которых присутствуют гиперссылки с такими адресами. Решим и эту проблему.

1.14. Именованные маршруты
Именованный маршрут — маршрут, которому было дано уникальное имя. Laravel
может генерировать интернет-адреса формата, задаваемого именованным маршрутом, для чего достаточно задать имя маршрута и значения URL-параметров (если
таковые имеются в шаблонном пути).
Дадим имена маршрутам:
 ведущему на главную страницу — имя index;
 ведущему на страницу объявления — имя detail.

И сделаем так, чтобы интернет-адреса гиперссылок генерировались на основании
имен маршрутов.
1. Откроем модуль routes\web.php, хранящий список веб-маршрутов, и укажем имена у маршрутов, добавив следующий код:
Route::get('/', [BbsController::class, 'index'])->name('index');
Route::get('/{bb}', [BbsController::class, 'detail'])->name('detail');

Метод get() маршрутизатора в качестве результата возвращает объект, представляющий маршрут. Метод name(), вызванный у маршрута, дает ему заданное
в параметре имя.
2. Откроем модуль resources\views\index.blade.php, где хранится шаблон перечня объявлений, и исправим код, выводящий гиперссылки на страницы объявлений:

. . .


Функция route() генерирует интернет-адрес на основе именованного маршрута,
имя которого задано в первом параметре. Во втором параметре может быть указан ассоциативный массив со значениями URL-параметров.

Глава 1. Простейший веб-сайт — доска объявлений

55

3. Откроем модуль resources\views\detail.blade.php с шаблоном выбранного объявления и исправим код гиперссылки на перечень объявлений:
На перечень объявлений

Проверим сайт в работе. И напоследок немного разукрасим его.

1.15. Статические файлы
Статическими называются файлы, пересылаемые клиенту без какой бы то ни было
обработки: внешние таблицы стилей, веб-сценарии, изображения, документы,
архивы и пр.
Статические файлы Laravel-сайта располагаются в папке public, фактически являющейся корневой папкой этого сайта, и во вложенных в нее папках.
Выведем в заголовке сайта, слева и справа, две копии небольшого графического
изображения. Само изображение сохраним в файле public\images\logo.jpg, а таблицу
стилей, задающую оформление, — в файле public\styles\main.css.
1. Создадим в папке public папки images и styles.
2. Найдем в Интернете подходящее изображение и сохраним его под именем
logo.jpg в папке public\images.
Если найденное изображение записано в графическом формате, отличном от
JPEG, следует дать файлу соответствующее расширение.
3. Создадим в папке public\styles файл main.css и запишем в него код таблицы стилей
из листинга 1.6.
Листинг 1.6. Код таблицы стилей public\styles\main.css
h1 {
background: url("/images/logo.jpg") left / auto 100% no-repeat, 
url("/images/logo.jpg") right / auto 100% no-repeat;
}

Если файл с изображением имеет расширение, отличное от jpg, CSS-код следует
соответственно исправить.
Абсолютный интернет-адрес файла с изображением указывается относительно
папки public.
4. Откроем модуль resources\views\layouts\base.blade.php, содержащий код базового
шаблона, и вставим в секцию заголовка тег , привязывающий нашу таблицу стилей:

. . .


56

Часть I. Основы Laravel на практическом примере




Теперь наша доска объявлений выглядит симпатичнее — рис. 1.7.
В следующей главе мы продолжим совершенствовать сайт, добавив средства разграничения доступа, создания, правки и удаления объявлений.

Рис. 1.7. Заголовок веб-сайта после применения стилей

ГЛАВА

2

Доска объявлений 2.0:
разграничение доступа, добавление,
правка и удаление объявлений
В этой главе мы свяжем каждое объявление с определенным зарегистрированным
пользователем, добавим инструменты для создания, правки и удаления объявлений,
регистрации пользователей и защитим сайт от несанкционированного доступа.
Любой Laravel-сайт изначально содержит миграцию, создающую таблицу users,
которая хранит список зарегистрированных пользователей. Соответственно, эта
таблица создается при первом выполнении миграций (см. разд. 1.7) — нам самим
формировать ее не придется.

2.1. Межтабличные связи.
Работа со связанными записями
Создание связи между таблицами выполняется в два этапа:
 формирование поля, хранящего ключ связанной записи первичной таблицы (по-

ля внешнего ключа), — во вторичной таблице;

 объявление «прямой» связи (между первичной и вторичной моделями) — в мо-

дели первичной таблицы;
 объявление «обратной» связи (между вторичной и первичной моделями) —

в модели вторичной таблицы.
Свяжем каждое объявление с определенным пользователем-автором, для чего установим связь «один-со-многими» между таблицами: users (она станет первичной)
и bbs (а она — вторичной). Далее добавим в список нового пользователя, создадим
разными способами три связанных с ним объявления и реализуем вывод на странице объявления имени его автора.
Чтобы избежать проблем с заполнением поля вторичного ключа в таблице bbs, не
будем вносить правки в уже имеющуюся таблицу, а удалим ее и создадим заново.
Для удаления таблицы bbs достаточно откатить последнюю миграцию:

58

Часть I. Основы Laravel на практическом примере

1. В командной строке выполним откат последней миграции:
php artisan migrate:rollback --step=1

Количество откатываемых миграций указывается в параметре --step команды
migrate:rollback.
2. Откроем в текстовом редакторе модуль database\migrations\_create_bbs_table.php с нашей миграцией и добавим код, создающий
поле внешнего ключа:
public function up() {
Schema::create('bbs', function (Blueprint $table) {
. . .
$table->float('price');
$table->foreignId('user_id')->constrained()
->onDelete('cascade');
$table->timestamps();
. . .
});
}

Метод foreignId() объекта структуры таблицы создает поле внешнего ключа и
возвращает представляющий его объект. Мы дали этому полю «говорящее» имя
user_id и поэтому для создания собственно связи можем вызвать у возвращенного объекта связи метод constrained(), который извлечет из имени все необходимые ему сведения: имя связываемой первичной таблицы (users) и ее ключевое
поле (id). Метод onDelete() укажет операцию, выполняемую над связанными
записями вторичной таблицы при удалении записи первичной таблицы (у нас —
cascade, т. е. каскадное удаление).
3. В командной строке применим исправленную миграцию:
php artisan migrate

Исправленная таблица объявлений готова. Займемся моделями пользователей и
объявлений.
4. Откроем в текстовом редакторе модуль app\Models\User.php с классом модели
пользователя User и добавим код, объявляющий «прямую» связь между текущей, первичной, и заданной вторичной моделями:
use App\Models\Bb;
class User extends Authenticatable {
. . .
public function bbs() {
return $this->hasMany(Bb::class);
}
}

«Прямая» связь объявляется в виде обычного метода (у нас — bbs()). В нем вызывается метод hasMany() класса модели, принимающий имя класса связываемой

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

59

вторичной модели и возвращающий объект созданной связи. Последний следует
вернуть из метода, объявляющего связь.
5. Откроем в текстовом редакторе модуль app\Models\Bb.php с классом модели объявления Bb и добавим код, объявляющий «обратную» связь между текущей, вторичной, и заданной первичной моделями:
use App\Models\User;
class Bb extends Model {
. . .
public function user() {
return $this->belongsTo(User::class);
}
}

«Обратная» связь также объявляется с помощью метода (у нас — user()). Метод
belongsTo() класса модели принимает имя класса связываемой первичной модели и возвращает объект созданной связи, который следует вернуть из метода,
объявляющего связь.
6. Запустим консоль Laravel (см. разд. 1.9) и добавим нового пользователя с именем admin, адресом электронной почты admin@bboard.ru (адрес можно указать
произвольный, поскольку Laravel по умолчанию не проверяет его существование) и паролем «admin» (задается в виде хеша, вычисленного вызовом метода
make() у подсистемы шифровальщика, представляемой фасадом Hash):
>>> use Illuminate\Support\Facades\Hash;
>>> use App\Models\User;
>>> $user = User::create(['name' => 'admin',
...
'email' => 'admin@bboard.ru',
...
'password' => Hash::make('admin')]);

7. Добавим объявление от имени этого пользователя:
>>>
>>>
>>>
>>>
>>>
>>>

use App\Models\Bb;
$bb = new Bb();
$bb->title = 'Пылесос';
$bb->content = 'Старый, ржавый, без шланга';
$bb->price = 500;
$user->bbs()->save($bb);

Метод первичной модели, объявляющий «прямую» связь, возвращает объект
«прямой» связи. Последний поддерживает метод save(), связывающий переданную в параметре запись вторичной модели с текущей записью и одновременно
сохраняющий переданную запись.
8. Создадим еще одно объявление другим способом:
>>> $user->bbs()->create(['title' => 'Грузовик',
...
'content' => 'Грузоподъемность - 5 т',
...
'price' => 10000000]);

60

Часть I. Основы Laravel на практическом примере

А еще объект «прямой» связи поддерживает метод create(), знакомый нам по
разд. 1.10.
9. Добавим третье объявление — третьим способом:
>>> $bb = new Bb(['title' => 'Шкаф',
...
'content' => 'Совсем новый, полированный, двухстворчатый',
...
'price' => 1000]);
>>> $bb->user()->associate($user);
>>> $bb->save();

Конструктору модели можно передать ассоциативный массив со значениями
полей (как и методу create()).
Метод вторичной модели, объявляющий «обратную» связь, возвращает объект
«обратной» связи. Последний поддерживает метод associate(), связывающий
переданную в параметре запись с текущей. После чего останется сохранить
созданную запись вызовом метода save().
10. Переберем все объявления и для каждого выведем название товара, имя пользователя-автора и цену:
>>> foreach (Bb::all() as $bb) {
...
$user = $bb->user;
...
echo $bb->title, ' | ', $user->name, ' | ', $bb->price, "\r\n";
... }
Пылесос | admin | 500.0
Грузовик | admin | 10000000.0
Шкаф | admin | 1000.0

Метод all(), вызываемый у модели, но выполняемый построителем запросов,
возвращает все записи таблицы. Свойство user объявления (одноименное с ранее объявленным в модели Bb методом) хранит связанного с объявлением пользователя.
11. Переберем все объявления, оставленные пользователем admin, и выведем названия хранящихся в них товаров:
>>> $user = User::where('name' ,'admin')->first();
>>> foreach ($user->bbs as $bb) { echo $bb->title, ' '; }
Пылесос Грузовик Шкаф

Свойство bbs пользователя (одноименное с ранее объявленным в модели User
методом) хранит список связанных объявлений.
12. Откроем модуль resources\views\detail.blade.php, хранящий код шаблона объявления, и добавим вывод имени пользователя, оставившего объявление:
{{ $bb->price }} руб.
Автор: {{ $bb->user->name }}
На перечень объявлений

Напоследок проверим сайт в работе.

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

61

Чтобы добавляемое объявление связывалось с каким-либо из зарегистрированных
пользователей, следует реализовать вход на сайт, раздел пользователя и выход
с сайта.

2.2. Вход и выход. Раздел пользователя
Теория
К работе с внутренними данными сайта (в нашем случае — со списком объявлений) следует допускать только посетителей, указанных в особом списке, — зарегистрированных пользователей, или просто пользователей.
Список пользователей практически всегда хранится в одной из таблиц базы данных. Каждая позиция списка содержит внутреннее имя пользователя («логин»),
адрес его электронной почты, пароль (в закодированном виде) и, возможно, дополнительные сведения: настоящие имя и фамилию пользователя, наименования операций, которые пользователь может выполнять с данными (привилегии, или права),
и др.
Посетитель, желающий получить доступ к данным сайта, должен выполнить процедуру входа на сайт (или аутентификации). Для этого он переходит на вебстраницу входа и вводит в веб-форму свои адрес электронной почты и пароль.
Laravel находит в списке пользователя с заданными адресом и паролем и помечает
его как выполнившего вход — текущего пользователя (записывая в серверную сессию особый признак).
При попытке перейти на какую-либо страницу Laravel проверяет, кому должна
быть доступна эта страница: всем, только зарегистрированным пользователям, выполнившим вход, только зарегистрированным пользователям с особыми привилегиями или только посетителям, не выполнившим вход (гостям). Если целевая
страница не должна быть доступна текущему пользователю, выполняется перенаправление на страницу входа (если вход на сайт не был выполнен) или выдается
сообщение об ошибке 403 (пользователь не имеет необходимых прав) — если вход
на сайт был выполнен. Такого рода проверки называются разграничением доступа
(или авторизацией).
Раздел пользователя — это страница, выводящая какие-либо данные, которые принадлежат текущему пользователю, например, перечень принадлежащих ему объявлений. Обычно именно в этот раздел выполняется перенаправление, как только
пользователь войдет на сайт.
Закончив работу, пользователь выполняет выход с сайта. При этом фреймворк «забывает», что пользователь ранее выполнил вход, и начинает считать его гостем.
Библиотека laravel/ui, установленная в разд. 1.2, позволяет сформировать в разрабатываемом сайте базовые средства для разграничения доступа: контроллеры, реализующие вход, функциональность раздела пользователя, выход, а также необходимые маршруты и шаблоны страниц.

62

Часть I. Основы Laravel на практическом примере

Практика
Создадим «костяк» подсистемы разграничения доступа и раздел пользователя, выводящий объявления, чьим автором является текущий пользователь.
1. В командной строке создадим базовые средства разграничения доступа:
php artisan ui:auth

В результате будут созданы, в частности:
• контроллеры (имена классов указаны относительно пространства имен
App\Http\Controllers):
 HomeController — выводит раздел пользователя;
 Auth\RegisterController — регистрирует нового пользователя;
 Auth\LoginController — выполняет вход на сайт и выход с него;
• шаблоны (пути указаны относительно папки resources\views):
 layouts\app.blade.php — базовый шаблон для всех остальных шаблонов, созданных утилитой artisan;
 home.blade.php — шаблон страницы с разделом пользователя;
 auth\register.blade.php — шаблон страницы регистрации нового пользователя;
 auth\login.blade.php — шаблон страницы входа.
Также будут созданы еще несколько контроллеров (и соответствующих им шаблонов), выполняющих проверку, сброс пароля и активацию нового пользователя
по электронной почте. Мы рассмотрим их в главе 13.
В список веб-маршрутов будут добавлены маршруты, ведущие на действия
вновь созданных контроллеров.
2. Откроем модуль routes\web.php со списком веб-маршрутов и посмотрим, какие
выражения были добавлены в него (здесь они подчеркнуты):
Route::get('/', [BbsController::class, 'index'])->name('index');
Route::get('/{bb}', [BbsController::class,'detail'])->name('detail');
Auth::routes();
Route::get('/home',
[App\Http\Controllers\HomeController::class, 'index'])
->name('home');

Метод routes() фасада Auth (за ним «прячется» подсистема безопасности) создает маршруты на действия контроллеров, созданных командой ui:auth утилиты
artisan. Из второго добавленного выражения видно, что раздел пользователя
выводится действием index() контроллера HomeController при запросе по пути
/home/ методом GET, а соответствующий маршрут имеет имя home — запомним
его.

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

63

3. В командной строке выведем список маршрутов, созданных методом routes()
фасада Auth:
php artisan route:list

В ответ утилита artisan выведет довольно широкую таблицу с перечнем созданных к настоящему моменту маршрутов. Нас интересуют лишь следующие (приведены в формате: «допустимый HTTP-метод — действие — описание»):
• GET — register — вывод страницы регистрации;
• GET — login — вывод страницы входа;
• POST — logout — выполнение выхода.
Полученных сведений достаточно для создания гиперссылок. Затруднения возникнут лишь с маршрутом logout, поскольку в нем указан допустимый метод
POST. Но мы избежим их, создав веб-форму с кнопкой, отправляющую данные
по этому маршруту методом POST.
4. Откроем модуль resources\views\layouts\base.blade.php с базовым шаблоном и добавим в него горизонтальную панель навигации:


Главная
Регистрация
Вход
Мои объявления

@csrf



Объявления
@yield('main')


Директива @csrf шаблонизатора вставляет в форму скрытое поле с электронным
маркером безопасности (подробности — в главе 11). Если мы не вставим этот
маркер, то получим сообщение об ошибке 419 (страница устарела).
5. Откроем модуль resources\views\auth\login.blade.php, хранящий шаблон страницы
входа, и внесем небольшие правки:
@extends('layouts.base')
@section('title', 'Вход')

64

Часть I. Основы Laravel на практическом примере
@section('main')
. . .
@endsection

Здесь мы, во-первых, заменили базовый шаблон layouts\app.blade.php, сформированный утилитой artisan, на написанный нами в разд. 1.13 шаблон layouts\base.
blade.php, во-вторых, добавили секцию title с названием раздела сайта и,
в-третьих, поменяли имя секции с основным содержанием на main.
6. Откроем модуль resources\views\auth\register.blade.php, хранящий шаблон страницы
регистрации, и внесем аналогичные правки.
Займемся разделом пользователя, который будет выводить перечень объявлений, оставленных текущим пользователем, в обратном хронологическом порядке.
7. Откроем модуль app\Http\Controllers\HomeController.php с кодом контроллера
HomeController и внесем следующие правки:
use Illuminate\Support\Facades\Auth;
class HomeController extends Controller {
. . .
public function index() {
return view('home',
['bbs' => Auth::user()->bbs()->latest()->get()]);
}
}

Метод user() фасада Auth возвращает объект модели User, представляющий
текущего пользователя. Вызвав у пользователя метод bbs(), получим объект
«прямой» связи. Этот объект имеет функциональность построителя запросов,
поддерживает все его методы (latest(), where(), get() и др., подробности —
в разд. 1.10) и настроен на обработку только связанных записей.
8. Откроем модуль resources\views\home.blade.php, где записан шаблон страницы
с разделом пользователя, удалим весь имеющийся там код и запишем в него код,
показанный в листинге 2.1.
Листинг 2.1. Код шаблона resources\views\home.blade.php
@extends('layouts.base')
@section('title', 'Мои объявления')
@section('main')
Добавить объявление
@if (count($bbs) > 0)



Товар

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

65

Цена, руб.
 



@foreach ($bbs as $bb)

{{ $bb->title }}
{{ $bb->price }}

Изменить


Удалить


@endforeach


@endif
@endsection

Мы сразу создали гиперссылки для добавления, правки и удаления объявлений.
Соответствующие интернет-адреса занесем в них позже.
9. Запустим отладочный сервер, откроем сайт и попытаемся перейти на страницу
входа, щелкнув на гиперссылке Вход.
Результатом станет стандартная страница с сообщением об ошибке 404, выводимая Laravel. Мы где-то допустили ошибку...
Вернемся к списку маршрутов:
Route::get('/', [BbsController::class, 'index'])->name('index');
Route::get('/{bb}', [BbsController::class, 'detail'])->name('detail');
Auth::routes();
Route::get('/home',
[App\Http\Controllers\HomeController::class, 'index'])
->name('home');

Маршрутизатор Laravel при просмотре списка маршрутов выбирает самый первый маршрут, имеющий совпадающие шаблонный путь и допустимый HTTPметод. Остальные маршруты при этом не просматриваются.
У нас страница входа располагается по пути /login/. Просматривая список маршрутов, маршрутизатор обнаружит, что этот путь совпадает с шаблонным путем
из второго по счету маршрута, ведущего на действие detail() контроллера
BbsController (оно выводит страницу объявления). Попытка извлечь объявление
с ключом login увенчается неудачей и возбуждением исключения, выводящего
сообщение об ошибке 404.

66

Часть I. Основы Laravel на практическом примере

Чтобы устранить проблему, достаточно передвинуть маршрут, ведущий на действие detail() контроллера BbsController, в самый конец списка.
10. Откроем файл routes\web.php со списком веб-маршрутов и внесем в него нужные
правки:
Route::get('/', [BbsController::class, 'index'])->name('index');
Route::get('/{bb}', [BbsController::class, 'detail'])->name('detail');
Auth::routes();
Route::get('/home',
[App\Http\Controllers\HomeController::class, 'index'])
->name('home');
Route::get('/{bb}', 'BbsController@detail')->name('detail');

Рис. 2.1. Веб-страница входа

Рис. 2.2. Веб-страница раздела пользователя

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

67

Теперь все должно работать. Откроем сайт, перейдем на страницу входа (рис. 2.1),
укажем адрес электронной почты admin@bboard.ru, пароль admin и нажмем кнопку Login. Если мы не допустили ошибок при вводе, окажемся на странице раздела
пользователя (рис. 2.2). Напоследок выйдем с сайта, нажав кнопку Выйти.
ПОЛЕЗНО ЗНАТЬ
По умолчанию Laravel идентифицирует пользователя по адресу его электронной почты. Подавляющее большинство других фреймворков используют для этого регистрационное имя пользователя («логин»).

2.3. Добавление, правка и удаление записей
Наделим наш сайт средствами для добавления новых объявлений. Автором добавляемых объявлений станет текущий пользователь. Позже реализуем правку и удаление объявлений. Всю необходимую логику запишем в контроллер HomeController.
1. Откроем список веб-маршрутов routes\web.php и добавим два маршрута, необходимых для реализации добавления объявлений:
use App\Http\Controllers\HomeController;
. . .
Route::get('/home', [HomeController::class, 'index'])->name('home');
Route::get('/home/add', [HomeController::class, 'showAddBbForm'])
->name('bb.add');
Route::post('/home', [HomeController::class, 'storeBb'])
->name('bb.store');
Route::get('/{bb}', [BbsController::class, 'detail'])->name('detail');

Первый из добавленных маршрутов (с шаблонным путем /home/add/ и допустимым методом GET) связан с действием showAddBbForm() контроллера
HomeController и выведет страницу с веб-формой для занесения нового объявления. Второй маршрут (шаблонный путь — /home/, допустимый HTTP-метод —
POST, поскольку он был создан вызовом метода post()) связан с действием
storeBb() того же контроллера, добавляющим введенное объявление в базу.
2. Откроем контроллер app\Http\Controllers\HomeController.php и добавим в него действие showAddBbForm(), выводящее страницу добавления объявления:
class HomeController extends Controller {
. . .
public function showAddBbForm() {
return view('bb_add');
}
}

Шаблон bb_add.blade.php, создающий страницу для ввода объявления, мы создадим позже. На этой странице предусмотрим поле ввода с наименованием title,
область редактирования content и поле ввода price.

68

Часть I. Основы Laravel на практическом примере

3. Добавим в контроллер HomeController действие storeBb(), сохраняющее новое
объявление и выполняющее перенаправление на раздел пользователя:
class HomeController extends Controller {
. . .
public function storeBb(Request $request) {
Auth::user()->bbs()->create(['title' => $request->title,
'content' => $request->content,
'price' => $request->price]);
return redirect()->route('home');
}
}

Мы задали у действия storeBb() параметр типа Request, тем самым «намекнув»
подсистеме внедрения зависимостей Laravel, что хотим получить в этом параметре объект класса Request, представляющий запрос. Из его свойств, чьи имена
совпадают с наименованиями элементов управления, извлекаем занесенные
в веб-форму значения для сохранения в записи.
Далее, вызвав функцию redirect(), получаем «пустой» объект перенаправления
и, вызвав у этого объекта метод route(), заносим в него целевой интернет-адрес,
сгенерированный на основе именованного маршрута home. И не забываем вернуть из действия готовый объект перенаправления.
4. Создадим модуль resources\views\bb_add.blade.php и запишем в него код шаблона
страницы для ввода нового объявления из листинга 2.2.
Листинг 2.2. Код шаблона resources\views\bb_add.blade.php
@extends('layouts.base')
@section('title', 'Добавление объявления :: Мои объявления')
@section('main')

@csrf

Товар



Описание



Цена



Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

69



@endsection

Не забываем поместить в форму электронный маркер безопасности, воспользовавшись директивой @csrf.
5. Откроем шаблон раздела пользователя resources\views\home.blade.php и запишем
интернет-адрес в гиперссылку Добавить объявление:
Добавить
объявление

6. Запустим отладочный сервер, выполним вход на сайт от имени пользователя
admin@bboard.ru, перейдем на страницу добавления объявления (рис. 2.3)
и создадим объявление с произвольным содержанием. Затем выйдем с сайта,
перейдем на страницу регистрации и создадим еще одного пользователя с именем editor, адресом editor@bboard.ru и паролем «supereditor». После создания
пользователя Laravel автоматически выполнит вход на сайт от его имени. Добавим еще пару произвольных объявлений и выйдем с сайта.
Наконец, дадим пользователем возможность править и удалять объявления.

Рис. 2.3. Веб-страница добавления объявления

7. Откроем список веб-маршрутов routes\web.php и добавим еще четыре маршрута:
Route::post('/home', [HomeController::class, 'storeBb'])
->name('bb.store');
Route::get('/home/{bb}/edit',
[HomeController::class, 'showEditBbForm'])
->name('bb.edit');

70

Часть I. Основы Laravel на практическом примере
Route::patch('/home/{bb}', [HomeController::class, 'updateBb'])
->name('bb.update');
Route::get('/home/{bb}/delete',
[HomeController::class, 'showDeleteBbForm'])
->name('bb.delete');
Route::delete('/home/{bb}', [HomeController::class, 'destroyBb'])
->name('bb.destroy');
Route::get('/{bb}', [BbsController::class, 'detail'])->name('detail');

Первый добавленный маршрут выведет страницу с веб-формой для правки объявления, второй — сохранит исправленное объявление, третий — выведет страницу с веб-формой удаления объявления, четвертый — удалит объявление.
У второго маршрута в качестве допустимого HTTP-метода указан PATCH
(вызовом метода patch() у фасада Route), а у четвертого — DELETE (вызовом
метода delete()).
8. Откроем контроллер app\Http\Controllers\HomeController.php и добавим в него действия: showEditBbForm() (вывод страницы правки объявления) и updateBb() (сохранение исправленного объявления):
use App\Models\Bb;
class HomeController extends Controller {
. . .
public function showEditBbForm(Bb $bb) {
return view('bb_edit', ['bb' => $bb]);
}
public function updateBb(Request $request, Bb $bb) {
$bb->fill(['title' => $request->title,
'content' => $request->content,
'price' => $request->price]);
$bb->save();
return redirect()->route('home');
}
}

В действии showEditBbForm() не забываем занести в контекст шаблона выбранную запись — чтобы подставить значения ее полей в поля ввода веб-формы.
В действии updateBb() мы указали два параметра: типа Request — для объекта
запроса, и типа Bb — для объекта модели, представляющего выбранное объявление. Подсистема внедрения зависимостей Laravel сама занесет в них требуемые
значения.
Для массового присваивания исправленных значений полям объявления применяем метод fill() объекта модели. Сохраним запись вызовом метода save().
9. Добавим в контроллер HomeController действия: showDeleteBbForm() (вывод
страницы удаления объявления) и destroyBb() (собственно удаление):

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

71

class HomeController extends Controller {
. . .
public function showDeleteBbForm(Bb $bb) {
return view('bb_delete', ['bb' => $bb]);
}
public function destroyBb(Bb $bb) {
$bb->delete();
return redirect()->route('home');
}
}

В действии showDeleteBbForm() заносим в контекст шаблона выбранную запись — чтобы вывести ее на странице.
10. Пересохраним модуль resources\views\bb_add.blade.php под именем bb_edit.blade.php,
тем самым создав шаблон страницы для правки объявления, и исправим его
код:
@section('title', 'Правка объявления :: Мои объявления')
@section('main')

@csrf
@method('PATCH')

. . .



. . .
{{ $bb->content }}


. . .




@endsection

В маршруте, сохраняющем исправленное объявление, мы указали допустимый
HTTP-метод PATCH. Проблема в том, что он не поддерживается вебобозревателями, — мы не можем записать его в атрибуте method тега .
Однако можно поместить в форму скрытое поле с наименованием нужного
HTTP-метода. Это выполняется директивой @method шаблонизатора.
В поля ввода и область редактирования следует подставить значения полей исправляемой записи.

72

Часть I. Основы Laravel на практическом примере

11. Пересохраним модуль resources\views\detail.blade.php под именем bb_delete.blade.php,
создав шаблон страницы для удаления объявления, и исправим его код:
@section('title', 'Удаление объявления :: Мои объявления')
@section('main')
. . .
Автор: {{ $bb->user->name }}

@csrf
@method('DELETE')


@endsection('main')

Чтобы отправить запрос на удаление объявления HTTP-методом DELETE (который также не поддерживается веб-обозревателями), мы создали на странице
веб-форму с кнопкой отправки данных и с помощью директивы @method поместили в форму скрытое поле с наименованием нужного HTTP-метода.
12. Откроем шаблон раздела пользователя resources\views\home.blade.php и вставим
интернет-адреса в гиперссылки Изменить и Удалить:
Изменить
. . .
Удалить

Войдем на сайт от имени любого из зарегистрированных пользователей и попробуем исправить какое-либо объявление. Добавим произвольное объявление и попытаемся удалить его.
На этом этапе сайт еще не проверяет корректность заносимых данных. Например,
если в поле ввода Цена вместо числа ввести строку, она будет успешно сохранена
в базе (формат базы данных SQLite позволяет хранить в поле значения любого типа, даже не совпадающего с типом, указанным при объявлении поля). А если вообще не заносить значение в какое-либо поле, мы получим страницу с системным сообщением об ошибке, которое ничего не скажет конечному пользователю сайта
(зато многое скажет хакеру).
Поэтому самое время заняться валидацией.

2.4. Валидация данных
Валидация — это проверка данных на корректность на основе заданных правил.
Реализуем валидацию добавляемых и исправляемых объявлений согласно следующим правилам:
 поле title — обязательно к заполнению, максимальная длина значения 50 сим-

волов;

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

73

 поле content — обязательно к заполнению;
 поле price — обязательно к заполнению, значение должно быть числом.

1. Откроем контроллер app\Http\Controllers\HomeController.php и объявим в его классе
константу BB_VALIDATOR с перечнем правил валидации:
class HomeController extends Controller {
private const BB_VALIDATOR = [
'title' => 'required|max:50',
'content' => 'required',
'price' => 'required|numeric'
];
. . .
}

Набор правил валидации записывается в виде ассоциативного массива, каждый
элемент которого задает набор правил для отдельного элемента управления.
У поля ввода title мы указали правила: required (обязательно к заполнению)
и max:50 (длина значения — не более 50 символов), у области редактирования:
content — required, у поля ввода: price — required и numeric (значение должно
представлять собой число). Отдельные правила разделяются символом вертикальной черты.
2. Добавим в контроллер HomeController константу BB_ERROR_MESSAGES с перечнем
сообщений об ошибках ввода:
class HomeController extends Controller {
. . .
private const BB_ERROR_MESSAGES = [
'price.required' => 'Раздавать товары бесплатно нельзя',
'required' => 'Заполните это поле',
'max' => 'Значение не должно быть длиннее :max символов',
'numeric' => 'Введите число'
];
. . .
}

Перечень сообщений об ошибках записывается также в виде ассоциативного
массива, отдельный элемент которого задает сообщение для одного из правил.
У правила required, указанного у поля ввода title, мы задали отдельное сообщение, запрещающее раздавать товары бесплатно. Далее задали сообщение для
того же правила, но без указания на конкретный элемент управления, — оно
будет выводиться у остальных элементов.
Вместо литерала :max, записанного в третьем сообщении об ошибке, Laravel при
выводе подставит длину строки, записанную в правиле max.

74

Часть I. Основы Laravel на практическом примере

ЕСЛИ СООБЩЕНИЕ ОБ ОШИБКЕ НЕ ЗАДАНО,
LARAVEL ВЫВЕДЕТ СООБЩЕНИЕ ПО УМОЛЧАНИЮ...
...на английском языке. Впрочем, существует дополнительная библиотека, которая
содержит сообщения об ошибках на других языках, — ее мы рассмотрим в главе 28.

3. Добавим в действия storeBb() и updateBb() того же контроллера код, выполняющий валидацию согласно записанным ранее правилам:
public function storeBb(Request $request) {
$validated = $request->validate(self::BB_VALIDATOR,
self::BB_ERROR_MESSAGES);
Auth::user()->bbs()->create(['title' => $validated['title'],
'content' => $validated['content'],
'price' => $validated['price']]);
return redirect()->route('home');
}
. . .
public function updateBb(Request $request, Bb $bb) {
$validated = $request->validate(self::BB_VALIDATOR,
self::BB_ERROR_MESSAGES);
$bb->fill(['title' => $validated['title'],
'content' => $validated['content'],
'price' => $validated['price']]);
$bb->save();
. . .
}

Валидация запускается вызовом метода validate() объекта запроса, в котором
и содержатся подлежащие валидации данные из веб-формы. В качестве параметров этому методу передаются перечни правил валидации и сообщений об
ошибках.
Если валидация прошла успешно, метод validate() вернет ассоциативный массив с данными из веб-формы, очищенными от лишних символов (начальных
и конечных пробелов, букв, поставленных после цифр в числах, и др.). Данные
из этого массива можно сохранить в базе.
Если валидация не увенчалась успехом, Laravel сохранит в серверной сессии все
данные из веб-формы и все сообщения об ошибках ввода, после чего выполнит
перенаправление на предыдущую страницу — ту самую, на которой находится
веб-форма. В результате посетитель сможет исправить некорректные данные.
Теперь нужно сделать так, чтобы на страницах с веб-формами добавления и
правки объявления выводились сообщения об ошибках ввода и данные, ранее
введенные в веб-форму и не прошедшие валидацию.
4. Откроем шаблон страницы правки объявления resources\views\bb_edit.blade.php
и добавим код, выводящий сообщения об ошибках ввода в поле title:

. . .

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

75


@error('title')

{{ $message }}

@enderror


Парная директива @error . . . @enderror шаблонизатора выводит находящийся
в ее теле фрагмент содержания только в том случае, если в серверной сессии
хранится сообщение об ошибке ввода в элемент управления с указанным именем. Внутри тела директивы можно использовать переменную message, хранящую сообщение об ошибке в виде текста.
Мы используем эту директиву, чтобы, во-первых, вывести сообщение об ошибке
под полем ввода и, во-вторых, привязать к полю ввода стилевой класс
is-invalid фреймворка Bootstrap, помечающий элемент управления как содержащий некорректное значение.
5. Добавим туда же код, помещающий в поле ввода ранее занесенное туда значение:


Функция old() извлекает из серверной сессии значение, занесенное в элемент
управления, чье наименование указано в первом параметре.
При первом выводе страницы на экран значение title в сессии отсутствует,
и функция old() вернет значение из второго параметра — изначальное название
товара, полученное контроллером из базы данных. Если валидация не увенчалась успехом, страница будет выведена повторно, в этом случае в сессии будет
храниться значение title, которое и занесется в поле ввода.
6. Внесем аналогичные исправления в код, создающий элементы управления
content и price.
7. Откроем шаблон страницы добавления объявления resources\views\bb_add.
blade.php и добавим аналогичный код, выводящий сообщения об ошибках ввода.
8. Добавим в тот же шаблон код, помещающий в элементы управления ранее занесенные туда значения (показаны исправления в коде поля ввода title — у остальных элементов правки будут аналогичными):


Указывать второй параметр у функции old() здесь не нужно — тогда в случае
отсутствия в серверной сессии заданного значения функция вернет null и поле
ввода будет выведено пустым.
Войдем на сайт от имени любого пользователя, попытаемся добавить объявление,
не указав какое-либо значение (например, название товара), нажмем кнопку Добавить и проверим, выводится ли сообщение об ошибке и заполняются ли остальные

76

Часть I. Основы Laravel на практическом примере

элементы управления ранее занесенными туда данными. После этого попробуем
ввести название товара длиной более 50 символов и нечисловое значение в качестве цены и посмотрим, что получится.
По идее, пользователь должен иметь возможность править и удалять лишь «свои»
объявления. Однако если мы выполним переход по интернет-адресу формата
/home//edit|delete/, то сможем исправить или удалить объявление с произвольным ключом вне зависимости от того, какой пользователь является
его автором. Это серьезная брешь в безопасности, и ее следует «заткнуть».

2.5. Разграничение доступа.
Посредники, политики и провайдеры
Теория
Разграничение доступа к данным сайта, написанного с применением Laravel, реализуется рядом посредников и политиками.
 Посредник (middleware) — программный модуль, выполняющий предваритель-

ную обработку запроса перед запуском контроллера и (или) окончательную
обработку ответа после его формирования контроллером и перед отправкой клиенту.
Посредник auth реализует разграничение доступа к сайту. Он проверяет, был ли
текущий запрос отправлен пользователем, выполнившим вход на сайт, и,
если это не так, выполняет перенаправление на страницу входа. Посредник
guest, напротив, проверяет, отправлен ли запрос гостем, в противном случае
производя перенаправление на раздел пользователя.
Большинство функционирующих в сайте посредников принадлежат самому
фреймворку, но часть непосредственно входит в состав сайта. Они генерируются
утилитой artisan при создании проекта и хранятся в папке app\Middleware, так что
программист при необходимости вмешаться в обработку запросов и ответов
может внести в их код нужные правки.
 Политика (policy) — программный модуль, реализующий разграничение досту-

па к определенной модели согласно заданным правилам.
В частности, политика может проверять, является ли текущий пользователь автором извлеченного из базы объявления. Если же это не так, будет выведена
страница с сообщением об ошибке 403.
Все политики, входящие в состав сайта, хранятся в папке app\Policies (изначально
отсутствующей) и регистрируются в провайдере AuthServiceProvider.
 Провайдер (provider) — программный модуль, задающий режим работы и некоторые параметры какой-либо из подсистем фреймворка.
Например, упоминавшийся ранее провайдер AuthServiceProvider инициализирует подсистему разграничения доступа и передает ей нужные для работы сведения, в частности перечень политик.

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

77

Бо́льшая часть входящих в состав сайта провайдеров принадлежат самому
Laravel. Остальные, в том числе и AuthServiceProvider, входят в состав самого
сайта, хранятся в папке app\Providers и могут быть исправлены программистом,
если он захочет изменить режим работы какой-либо подсистемы фреймворка.
Посредники, политики и провайдеры реализуются в виде классов.

Практика
Сначала удостоверимся, что раздел пользователя, страницы добавления, правки и
удаления объявления защищены посредником auth, «пускающим» к страницам
только пользователей, выполнивших вход. После чего создадим политику BbPolicy,
которая позволит править и удалять объявления только их автору.
1. Откроем контроллер app\Http\Controllers\HomeController.php и посмотрим на его конструктор:
public function __construct() {
$this->middleware('auth');
}

Метод middleware() указывает «пропустить» все запросы, приходящие на действия текущего контроллера, через указанный посредник. Отметим, что вызов
этого метода в конструктор класса вставила сама утилита artisan при создании
базовых средств разграничения доступа (см. разд. 2.2).
Как видим, все действия контроллера уже защищены от гостей посредником
auth. Займемся политикой.
2. В командной строке создадим политику BbPolicy:
php artisan make:policy BbPolicy

3. Откроем только что созданную политику app\Policies\BbPolicy.php и посмотрим на
ее код (листинг 2.3).
Листинг 2.3. Код «пустой» политики BbPolicy
namespace App\Policies;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class BbPolicy {
use HandlesAuthorization;
public function __construct() {
}
}

78

Часть I. Основы Laravel на практическом примере

Политика в Laravel — это класс, находящийся в пространстве имен App\Policies
и использующий трейт HandlesAuthorization. Изначально он содержит только
«пустой» конструктор, в принципе, ненужный.
4. Добавим в политику BbPolicy код, реализующий разграничение доступа:
use App\Models\Bb;
class BbPolicy {
. . .
public function update(User $user, Bb $bb) {
return $bb->user->id === $user->id;
}
public function destroy(User $user, Bb $bb) {
return $this->update($user, $bb);
}
}

Этот код записывается в виде набора методов, каждый из которых вызывается
при попытке выполнить определенную операцию (update() — правку,
destroy() — удаление и т. п.). Первым параметром эти методы принимают объект текущего пользователя. Остальные параметры могут быть произвольными — как правило, это объекты моделей, представляющие проверяемые записи.
Каждый метод должен возвращать в качестве результата логическую величину:
true — операция разрешена, false — запрещена.
Наши методы получают вторым параметром объект объявления. Метод update()
сравнивает ключ текущего пользователя с ключом автора объявления, и если
они равны (т. е. текущий пользователь является автором объявления), операция
правки разрешается. Метод destroy() для выполнения аналогичной проверки
просто вызывает метод update().
5. Откроем модуль app\Providers\AuthServiceProvider.php с кодом провайдера
AuthServiceProvider и добавим код, связывающий модель Bb с политикой
BbPolicy:
class AuthServiceProvider extends ServiceProvider {
. . .
protected $policies = [
'App\Models\Bb' => 'App\Policies\BbPolicy',
];
. . .
}

Такие связи записываются в ассоциативном массиве, присвоенном защищенному свойству policies класса провайдера. Один элемент массива описывает одну
связь между моделью (ее полное имя указывается в качестве ключа элемента)
и политикой (полное имя которой станет значением элемента).
6. Откроем список веб-маршрутов routes\web.php и добавим код, указывающий
применять политику BbPolicy при операциях правки и удаления объявления:

Глава 2. Доска объявлений 2.0: разграничение доступа, добавление, правка и удаление объявлений

79

Route::get('/home/{bb}/edit', . . . )
->name('bb.edit')->middleware('can:update,bb');
Route::patch('/home/{bb}', . . . )
->name('bb.update')->middleware('can:update,bb');
Route::get('/home/{bb}/delete', . . . )
->name('bb.delete')->middleware('can:destroy,bb');
Route::delete('/home/{bb}', . . . )
->name('bb.destroy')->middleware('can:destroy,bb');

Объект маршрута, возвращаемый методами get(), post(), patch() и delete(),
поддерживает знакомый нам метод middleware(), задающий посредник, который
станет обрабатывать все запросы по текущему маршруту. Мы указали «пропускать» запросы по этим четырем маршрутам через посредник can, реализующий
разграничение доступа к моделям посредством связанных с ними политик.
Посредник can требует указать два параметра, записываемых после двоеточия
через запятую:
• название выполняемой операции, которое должно совпадать с именем одного
из методов, объявленных в политике;
• имя URL-параметра, содержащего номер записи. Подсистема внедрения зависимости сама найдет запись по ее номеру и передаст методу политики
представляющий ее объект.
В нашем случае при попытке перейти на страницу правки записи Laravel определит, что с моделью Bb связана политика BbPolice, вызовет метод update() этой
политики и передаст ему объект исправляемого объявления. Метод update()
проверит, является ли текущий пользователь автором объявления, и разрешит
переход на страницу правки или, напротив, выведет сообщение об ошибке 403.
Выполним вход на сайт от имени любого пользователя и попробуем исправить какое-либо объявление. После чего, набрав интернет-адрес формата /home//edit/, попытаемся исправить «чужое» объявление и удостоверимся,
что сайт нас к нему не «пускает».

2.6. Получение сведений
о текущем пользователе
Осталось сделать так, чтобы гиперссылки на страницы, доступные лишь зарегистрированным пользователям, не показывались гостям, и наоборот. Также неплохо
было бы вывести в разделе пользователя его имя — с этого и начнем.
1. Откроем шаблон раздела пользователя resources\views\home.blade.php и вставим
код, выводящий заголовок с «адресным» приветствием:
@section('main')
Добро пожаловать, {{ Auth::user()->name }}!
Добавить объявление

80

Часть I. Основы Laravel на практическом примере

Здесь мы, вызвав метод user() фасада Auth, получаем объект текущего пользователя и обращаемся к свойству name этого объекта, чтобы извлечь имя пользователя.
2. Откроем базовый шаблон resources\views\layouts\base.blade.php и добавим код,
выводящий гиперссылки Регистрация и Вход только гостям, а гиперссылку
Мои объявления и форму с кнопкой Выход — только зарегистрированным
пользователям:
Главная
@guest
Регистрация
Вход
@endguest
@auth
Мои объявления

. . .

@endauth

Парная директива @quest . . . @endguest шаблонизатора выводит свое содержимое, если вход на сайт не был выполнен, а парная директива @auth . . .
@endauth — если вход, напротив, был выполнен.
Проверим, как все работает.
На этом разработка учебного сайта доски объявлений в основном закончена. Вы
можете доработать его самостоятельно в процессе изучения остального материала
книги.

ЧАСТЬ

II

Базовые инструменты
Глава 3.

Создание, настройка и отладка проекта

Глава 4.

Миграции и сидеры

Глава 5.

Модели: базовые инструменты

Глава 6.

Запись данных

Глава 7.

Выборка данных

Глава 8.

Маршрутизация

Глава 9.

Контроллеры и действия.
Обработка запросов и генерирование ответов

Глава 10.

Обработка введенных данных. Валидация

Глава 11.

Шаблоны: базовые инструменты

Глава 12.

Пагинация

Глава 13.

Разграничение доступа: базовые инструменты

Глава 14.

Обработка строк, массивов и функции-хелперы

Глава 15.

Коллекции Laravel

ГЛАВА

3

Создание, настройка
и отладка проекта
3.1. Подготовка платформы
Laravel для работы требует наличия программных платформ следующих (или более
новых) версий:
 PHP — 7.3.0 с расширениями BCMath, Ctype, Fileinfo, JSON, Mbstring, OpenSSL,
PDO, Tokenizer и XML;
 SQLite — 3.8.8;
 MySQL — 5.6;
 PostgreSQL — 9.4;
 Microsoft SQL Server — 2017.
Отдельный веб-сервер для отладки необязателен — можно использовать отладочный веб-сервер, встроенный в PHP.
Для подготовки программной платформы к разработке сайта на Laravel следует выполнить следующие шаги:
1. Установить платформу PHP (дистрибутив находится на сайте https://www.php.net/).
2. Установить утилиту Composer (https://getcomposer.org/).
В процессе установки потребуется указать путь к файлу php.exe — консольной
редакции PHP.
3. Установить клиентскую часть используемой серверной СУБД — при необходимости.
4. Установить утилиту laravel, набрав в командной строке команду:
composer global require laravel/installer

3.2. Создание проекта
Создать новый проект Laravel можно двумя способами:
 посредством утилиты laravel, набрав команду формата:
laravel new [--auth] [--dev] [--force]

84

Часть II. Базовые инструменты

Папка проекта с указанным именем создается в той папке, в которой была отдана
команда.
Поддерживаются следующие полезные ключи:
• --auth — дополнительно устанавливает библиотеку laravel/ui и добавляет
в проект базовые средства разграничения доступа, аналогичные описанным
в разд. 2.2 и 2.5.
Следует иметь в виду, что шаблоны созданных таким образом страниц регистрации, входа и другие используют CSS-фреймворк Bootstrap и клиентский
JavaScript-фреймворк Vue, которые также будут установлены. Если вы не
планируете использовать Bootstrap и Vue и хотите исключить их установку,
создавайте средства разграничения доступа способом, описанным в разд. 2.2;
• --dev — устанавливает самую последнюю версию Laravel, даже если она не
является стабильной (релизом);
• --force — принудительно создает проект, даже если папка с заданным именем
уже существует;
 с помощью утилиты Composer, набрав команду формата:
composer create-project --prefer-dist laravel/laravel

Ключ --prefer-dist указывает устанавливать только стабильные редакции (релизы) библиотек.

3.3. Папки и файлы проекта
В этом разделе описаны папки и файлы, составляющие проект и присутствующие
в его папке. Вложенность папок и файлов показана отступами. Имена папок набраны прописными буквами, имена файлов — строчными.
 APP — все программные PHP-модули, хранящие код сайта. Распределены по

разным папкам, которые мы рассмотрим в последующих главах;
 BOOTSTRAP — модули с инициализационным кодом:

• CACHE — инициализационные модули, сгенерированные фреймворком и
предназначенные для ускорения загрузки библиотечных модулей;
• app.php — основной инициализационный модуль. В частности, создает объект, представляющий Laravel-сайт;
 CONFIG — все модули с рабочими настройками проекта (будут рассмотрены да-

лее в этой и последующих главах);
 DATABASE — служебные модули, имеющие отношение к базам данных. В этой

папке можно сохранить базы данных формата SQLite;
• FACTORIES — фабрики записей (используются при автоматизированном тестировании, которое в этой книге не рассматривается);
• MIGRATIONS — миграции;

Глава 3. Создание, настройка и отладка проекта

85

• SCHEMA — схемы баз данных;
• SEEDERS — сидеры;
 PUBLIC — корневая папка сайта. В нее можно поместить необходимые статические файлы (изображения, таблицы стилей, веб-сценарии и др.):
• .htaccess — файл конфигурации веб-сервера Apache HTTP Server;
• web.config — файл конфигурации веб-сервера Microsoft Internet Information
services;
• favicon.ico — значок сайта;
• index.php — единая точка входа (или стартовый модуль). Выполняется при
получении любого клиентского запроса, создает объект, представляющий
сайт (запуская для этого модуль bootstrap\app.php), запускает обработку запроса, отправляет клиенту сгенерированный ответ и завершает работу сайта;
• robots.txt — список исключений для поисковых служб;
 RESOURCES — файлы с кодом сайта, написанные на языках, отличных от PHP:
• JS — внешние веб-сценарии, подлежащие преобразованию посредством
Laravel Mix (он будет описан в главе 17);
• LANG — языковые файлы, разнесенные по папкам, каждая из которых соответствует одному языку (подробности — в главе 28);
• SASS — внешние таблицы стилей, написанные на языке SCSS и подлежащие
преобразованию в CSS посредством Laravel Mix;
• VIEWS — шаблоны;
 ROUTES — модули со списками маршрутов;
 STORAGE — файлы, выгруженные на сайт посетителями (изображения, аудио-,
видеофайлы, архивы и др.) или созданные программно (например, аватары, сгенерированные на основе выгруженных посетителями изображений), и служебные файлы:
• APP — файлы, выгруженные на сайт посетителями или созданные программно и не предназначенные для вывода на страницах, — сохраняются непосредственно в этой папке:
 PUBLIC — файлы, выгруженные на сайт или созданные программно, напротив, выводящиеся на страницах. Обычно в корневой папке сайта
(public) создается символическая ссылка storage, указывающая на папку
storage\app\public (как это сделать, будет описано в главе 18);
• FRAMEWORK — служебные файлы, сгенерированные фреймворком:
 CACHE\DATA — файлы с кэшированными данными (если используется
файловый кэш). Подробнее о кэшировании данных будет рассказано в главе 29;
 SESSIONS — файлы с серверными сессиями, если в настройках указано
хранение сессий в файлах (подробности — в главе 26);

86

Часть II. Базовые инструменты

 TESTING — файлы, выгруженные посетителями (сохраняются здесь при
работе в тестовом режиме);
 VIEWS — откомпилированные шаблоны;
• LOGS — файлы журналов;
 TESTS — тестовые модули.
АВТОР НЕ ОПИСЫВАЕТ В ЭТОЙ КНИГЕ АВТОМАТИЗИРОВАННОЕ ТЕСТИРОВАНИЕ...
...поскольку не считает его сколь-нибудь полезным.

 VENDOR — сам фреймворк Laravel и его зависимости — другие библиотеки, ис-

пользуемые им в работе;
 .editorconfig — настройки для текстовых редакторов;
 .env — локальные настройки;
 .env.example — шаблон для генерирования файла .env. Отличается от последнего

лишь тем, что не содержит секретного ключа (о нем поговорим позже);
 .gitattributes — конфигурация Git;
 .styleci.yml — конфигурация StyleCI, службы, преобразующей исходный код к за-

данному стандарту написания;
 artisan — одноименная утилита, служащая для выполнения различных действий

над проектом;
 composer.json — конфигурация проекта в формате утилиты Composer;
 composer.lock — список установленных PHP-библиотек с указанием их версий;
 package.json — конфигурация проекта в формате утилиты npm;
 package-lock.json — список установленных JavaScript-библиотек с указанием их

версий;
 phpuint.xml — конфигурация тестовой подсистемы Laravel;
 readme.md — краткое описание проекта в формате Markdown;
 server.php — модуль единой точки входа, используемый отладочным веб-сер-

вером;
 webpack.mix.js — конфигурация Laravel Mix.

3.4. Настройки проекта
3.4.1. Две разновидности настроек проекта
3.4.1.1. Локальные настройки
Локальные настройки, записываемые в файле .env, описывают текущую программную платформу. К ним относятся параметры подключения к базе данных, почтовому серверу, параметры кэша, серверных сессий, службы рассылки сообщений и др.

Глава 3. Создание, настройка и отладка проекта

87

Настройки в файле .env записываются в виде строк формата:
=

Названия настроек традиционно набираются в верхнем регистре.
В качестве значения настройки может быть задана:
 строка:
APP_NAME=Объявления

Если строка содержит пробелы, ее следует заключить в одинарные или двойные
кавычки:
APP_NAME="Доска объявлений"

 логическая величина, записанная в виде true, (true), false или (false):
APP_DEBUG=true
SEND_MAIL=(false)

 значение null, записанное в виде null или (null):
MAIL_ENCRYPTION=null

 «пустая» строка, записанная в виде empty или (empty):
DB_PASSWORD=empty

 значение другой настройки с указанным именем, записанное в формате:
"${}"

Например, настройка MAIL_FROM_NAME получит значение, указанное у настройки
APP_NAME:
MAIL_FROM_NAME="${APP_NAME}"

 Если вообще не указать значение у настройки, она в качестве значения получит

«пустую» строку:
DB_PASSWORD=

Пример локальных настроек, задающих параметры доступа к базе данных MySQL:
DB_CONNECTION=mysql
DB_HOST=data-server.local
DB_DATABASE=bboard
DB_USERNAME=bboard
DB_PASSWORD=123456789

Любую настройку, записанную в файле .env, можно перекрыть, создав переменную
среды пользователя или системную переменную с тем же именем (например, если
создать переменную DB_DATABASE со значением bboard_new, Laravel будет использовать базу данных bboard_new, а не bboard, как записано в файле .env). При этом системные переменные имеют приоритет перед переменными уровня пользователя.

88

Часть II. Базовые инструменты

Начиная с Laravel 8, отладочный веб-сервер отслеживает изменение файла локальных настроек и загружает его повторно (в предыдущих версиях фреймворка этого
не происходило и отладочный сервер приходилось перезапускать вручную).
Локальные настройки не включаются в состав коммитов, создаваемых системой
управления версиями Git (в этом можно убедиться, если открыть файл .gitignore,
хранящийся в папке проекта). Это полезно, если над одним проектом работает
целая группа программистов. Каждый член группы сможет записать в файл .env
настройки своей платформы и впоследствии просто загружать «свежую» редакцию
проекта из Git-репозитория, не переконфигурируя ее каждый раз, чтобы запустить
у себя.
При инициализации сайта Laravel считывает значения локальных настроек и переносит их в соответствующие им рабочие настройки.

3.4.1.2. Рабочие настройки
Рабочие настройки затрагивают все аспекты функционирования проекта. Именно
из рабочих настроек Laravel получает все необходимые ему сведения о сайте.
Рабочие настройки хранятся в 14 модулях, находящихся в папке config. Каждый модуль содержит настройки одной из подсистем фреймворка. Так, модуль database.php
хранит настройки подсистемы, обеспечивающей работу с базами данных, модуль
session.php — настройки подсистемы серверных сессий, модуль view.php — шаблонизатора и пр. Особняком стоит модуль app.php, содержащий настройки сайта как
такового: название, режим работы, язык по умолчанию и др.
Настройки в каждом таком модуле организованы в виде ассоциативного массива,
который возвращается из модуля в качестве результата. Отдельный элемент такого
массива задает одну из настроек, которая может быть как элементарным значением,
так и массивом.
Ряд рабочих настроек получают свои значения из файла .env, в котором хранятся
локальные настройки. Таким образом, Laravel при инициализации выполняет объединение локальных и рабочих настроек для удобства программирования.
В качестве примера рассмотрим фрагмент модуля config\database.php:
return [
'default' => env('DB_CONNECTION', 'mysql'),
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DATABASE_URL'),
'database' => env('DB_DATABASE',
database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],
. . .

Глава 3. Создание, настройка и отладка проекта

89

],
. . .
];

Элемент connections возвращаемого массива содержит список баз данных (более
подробно о них мы поговорим очень скоро). А элемент default задает базу данных
по умолчанию, используемую, если база не была задана явно.
Элемент default получает свое значение из локальной настройки DB_CONNECTION,
записанной в файле .env. Для извлечения оттуда настройки с заданным именем используется функция env(), вызываемая в формате:
env([, =null])
Значение по умолчанию возвращается,

если настройка с заданным именем в файле .env

не найдена.
Также из локальных настроек получают свои значения рабочие настройки url,
database и foreign_key_constraints, записанные в массиве connections.sqlite.

3.4.2. Настройки проекта по категориям
Здесь мы рассмотрим только наиболее важные настройки самого проекта и используемых им баз данных. Остальные настройки, в том числе задающие режим работы
других подсистем фреймворка, будут описаны в последующих главах.

3.4.2.1. Базовые настройки проекта
Базовые настройки проекта записаны в файле config\app.php. Они включают название проекта, язык по умолчанию, интернет-адрес, по которому опубликован сайт,
и пр.:
 name — название проекта. Значение берется из локальной настройки APP_NAME.

По умолчанию: "Laravel";
 url — интернет-адрес хоста, на котором работает сайт. Используется утилитой

artisan при генерировании модулей. Значение берется из локальной настройки
APP_URL. По умолчанию: "http://localhost";
 asset_url — интернет-адрес хоста, на котором находятся статические файлы

сайта, или путь к содержащей их папке без конечного слеша. Используется
функцией asset() (см. главу 11) для формирования интернет-адресов статических файлов.
Значение настройки берется из локальной настройки ASSET_URL, изначально отсутствующей в файле .env. По умолчанию — null(указывает, что статические
файлы находятся непосредственно в папке public того же хоста, на котором развернут сайт). Пример:
// Файл .env
ASSET_URL=/assets

90

Часть II. Базовые инструменты
// Шаблон


-->

 timezone — обозначение временно́й зоны по умолчанию в виде строки (по умол-

чанию — "UTF", т. е. всемирное координированное время):
return [
. . .
'timezone' => 'Europe/Moscow',
. . .
];

 locale — обозначение языка, используемого по умолчанию, если иной язык не

задан, в виде строки (по умолчанию "en", т. е. английский). Пример установки
русского языка:
return [
. . .
'locale' => 'ru',
. . .
];

 fallback_locale — обозначение языка, используемого, если выбранный посети-

телем язык сайтом не поддерживается, в виде строки (по умолчанию — "en");
 faker_locale — обозначение языка, используемого фабриками записей (приме-

няются при автоматизированном тестировании, которое в этой книге не рассматривается). По умолчанию — "en_US" (американский английский).

3.4.2.2. Настройки режима работы веб-сайта
Эти настройки затрагивают особенности функционирования сайта и хранятся в модуле config\app.php.
 env — обозначение режима работы сайта в виде строки. Может быть произволь-

ным. Разработчики Laravel рекомендуют указывать следующие режимы:
• "local" — разработка;
• "production" — эксплуатация.
Значение настройки берется из локальной настройки APP_ENV. По умолчанию —
однако в настройке APP_ENV изначально указано "local";

"production",

 debug — если true, при возникновении ошибки будет выводиться страница

с подробным описанием проблемы, если false — будет выводиться стандартная
страница с сообщением об ошибке 503 (внутренняя ошибка сайта). Значение берется из локальной настройки APP_DEBUG. По умолчанию — false, однако в настройке APP_DEBUG изначально указано true.

Глава 3. Создание, настройка и отладка проекта

91

3.4.2.3. Настройки шифрования
Настройки шифрования также хранятся в модуле config\app.php.
 key — секретный ключ, используемый в операциях шифрования и подписыва-

ния данных, в виде строки. Значение берется из локальной настройки APP_KEY.
Секретный ключ генерируется и записывается в файл .env утилитой artisan при
создании нового проекта. При необходимости его можно сгенерировать повторно, отдав команду формата:
php artisan key:generate [--force]

Если сайт работает в эксплуатационном режиме (настройке env из модуля
config\app.php дано значение "production"), утилита artisan спросит, перезаписывать ли старый секретный ключ. Чтобы перезаписать его принудительно, без
выдачи запроса, следует указать ключ --force;
 cipher — обозначение алгоритма, применяемого для генерирования секретного

ключа, в виде строки. Поддерживаются алгоритмы "AES-256-CBC" (используется
по умолчанию) и "AES-128-CBC".

3.4.2.4. Настройки баз данных
Настройки подключения к базам данных, используемым сайтом, хранятся в модуле
config\database.php.
 connections — список используемых баз данных (в терминологии Laravel —

«подключений»), которых может быть произвольное количество, которые могут
быть разных форматов, располагаться в разных файлах и на разных хостах. Указывается в виде ассоциативного массива, каждый элемент которого описывает
одну базу данных. Ключ элемента этого массива задаст имя базы данных, используемое Laravel.
Изначально список содержит базы с именами sqlite, mysql, pgsql и sqlsrv форматов соответственно SQLite, MySQL, PostgreSQL и Microsoft SQL Server. Вы
можете удалить ненужные базы данных и добавить требуемые.
Настройки подключения к каждой базе данных также записываются в виде
ассоциативного массива (пример которого можно увидеть в разд. 3.4.1.2). Эти
настройки мы рассмотрим позже;
 default — имя используемой по умолчанию базы данных из указанных в списке
connections,

если в выражении, обращающемся к данным, база не задана явно.
Значение берется из локальной настройки DB_CONNECTION (по умолчанию —
"mysql");
 migrations — имя таблицы, создаваемой в базе данных для хранения перечня

выполненных к настоящему моменту миграций. Значение по умолчанию —
и менять его следует лишь в случае, если в базе уже есть таблица
с таким именем.
"migrations",

92

Часть II. Базовые инструменты

Настройки самих баз данных, указываемые в элементах массива connections:
 driver — обозначение драйвера PDO, используемого для доступа к базе данных

определенного формата, в виде строки: "sqlite" (SQLite), "mysql" (MySQL),
"pgsql" (PostgreSQL) и "sqlsrv" (Microsoft SQL Server);
 database — абсолютный путь к файлу базы данных (формата SQLite) или имя

базы данных (остальных форматов) в виде строки. Значение берется из локальной настройки DB_DATABASE. По умолчанию:
• у базы данных sqlite — абсолютный путь к файлу database\database.sqlite, изначально не существующему;
• у остальных баз данных — "forge";
 prefix — префикс, добавляемый в начале имен всех таблиц, в виде строки. Мо-

жет пригодиться, если база уже содержит таблицы, чьи имена могут совпасть
с именами таблиц с данными сайта. По умолчанию — «пустая» строка.
Следующие настройки указываются лишь у серверных СУБД:
 host — интернет-адрес хоста, на котором работает серверная СУБД, в виде

строки. Значение берется из локальной настройки DB_HOST. По умолчанию —
127.0.0.1 (локальный хост);
 port — номер TCP-порта, через который работает серверная СУБД, в виде стро-

ки. Значение берется из локальной настройки DB_PORT. По умолчанию:
• у базы данных mysql — "3306";
• у базы данных pgsql — "5432";
• у базы данных sqlsrv — "1433";
 username — имя пользователя для подключения к серверной СУБД в виде стро-

ки. Значение берется из локальной настройки DB_USERNAME. По умолчанию —
"forge";
 password — пароль для подключения к серверной СУБД в виде строки. Значение

берется из локальной настройки DB_PASSWORD. По умолчанию — «пустая» строка;
 charset — обозначение кодировки баз данных в виде строки. По умолчанию:
"utf8mb4" (MySQL)

или "utf8" (остальные);

 url — интернет-адрес базы данных, записанный в формате:
://:@ 
[:]/[?]

указывается в том случае, если серверная СУБД работает через нестандартный порт. Примеры:

TCP-порт

mysql://bboard:123456789@data-server.local/bboard?charset=UTF-8

Значение берется из локальной настройки DATABASE_URL, изначально не существующей.

Глава 3. Создание, настройка и отладка проекта

93

Далее приведены настройки, специфические для отдельных СУБД:
 prefix_indexes (только MySQL и PostgreSQL) — префикс, добавляемый в начале

имен всех создаваемых индексов, в виде строки (по умолчанию — «пустая»
строка);
 foreign_key_constraints (только SQLite) — если true, ссылочная целостность

будет поддерживаться непосредственно на уровне СУБД, если false — не будет
поддерживаться. Значение берется из локальной настройки DB_FOREIGN_KEYS, изначально не существующей. По умолчанию — true;
 unix_socket (только MySQL) — обозначение сокета, через который выполняется

подключение к серверной СУБД, в виде строки. Значение берется из локальной
настройки DB_SOCKET. По умолчанию — «пустая» строка;
 collation (только MySQL) — обозначение последовательности сортировки за-

писей в виде строки (по умолчанию — "utf8mb4_unicode_ci");
 strict (только MySQL) — если true, включится «строгий» (strict) режим, если
false —

СУБД будет работать в обычном режиме (по умолчанию — true);

 engine (только MySQL) — обозначение программного ядра, используемого для

работы с базой, в виде строки. По умолчанию — null (ядро InnoDB);
 options (только MySQL) — ассоциативный массив с дополнительными парамет-

рами подключения. По умолчанию содержит один элемент с ключом
PDO::MYSQL_ATTR_SSL_CA и значением, которое представляет собой путь к файлу
сертификата, извлеченный из локальной настройки MYSQL_ATTR_SSL_CA;
 schema (только PostgreSQL) — имя схемы базы данных, с которой будет осуще-

ствляться работа, в виде строки (по умолчанию — "public");
 sslmode (только PostgreSQL) — обозначение режима защищенного подключения

в виде строки (по умолчанию — "prefer").
Если для операций чтения и записи используются разные подключения, специфичные для них параметры записываются в настройках, соответственно: read и write.
В рассмотренном далее примере для чтения из базы данных bboard используется
подключение к хосту read.data.local от имени пользователя r_bboard с паролем
«123456789», а для записи в ту же базу — подключение к хосту write.data.local
через нестандартный TСP-порт 6603 от имени пользователя w_bboard с паролем
«987654321»:
'connections' => [
'mysql' => [
'driver' => 'mysql',
'read' => [
'host' => 'read.data.local',
'port' => '3306',
'username' => 'r_bboard',
'password' => '123456789',
],

94

Часть II. Базовые инструменты
'write' => [
'host' => 'write.data.local',
'port' => '6603',
'username' => 'w_bboard',
'password' => '987654321',
],
'database' => 'bboard',
. . .
],

],

Если параметру sticky дать значение true, после записи данных через подключение
«для записи» чтение также будет выполнено через это же подключение. Это может
повысить производительность (разумеется, если пользователь, соединившийся
через это подключение, имеет привилегии на чтение данных):
'connections' => [
'mysql' => [
. . .
'read' => [ . . . ],
'write' => [ . . . ],
. . .
'sticky' => true,
],
],

Если через подключение «для записи» невозможно чтение данных, параметру
sticky следует дать значение false или вообще удалить его.
ПОЛЕЗНО ЗНАТЬ
Laravel позволяет использовать несколько баз данных, просто записав их в настройках
проекта. Во фреймворке Django, напротив, для этого требуется дополнительное программирование.

3.4.3. Доступ к настройкам из программного кода
Для извлечения значения рабочей настройки с указанным путем применяется функция config():
config([, =null])
Путь записывается

в формате:

.

Если настройка с заданным путем отсутствует, возвращается значение по умолчанию.
Примеры:
// Получаем значение настройки name из модуля config\app.php
$project_name = config('app.name');

Глава 3. Создание, настройка и отладка проекта

95

// Получаем значение настройки connections.sqlite.database
// из модуля config\database.php
$sqlite_database_path = config('database.connections.sqlite.database');

Для программного указания новых значений настроек применяется та же функция
config(), но в другом формате вызова:
config()

Ключ элемента заданного массива укажет путь к нужной настройке, а значение элемента станет новым значением этой настройки. Пример:
// Задаем новое название сайта
config(['app.name' => 'ДО: Доска объявлений']);

Выяснить, в каком режиме работает сайт, позволит метод environment() фасада
Illuminate\Support\Facades\App (управляющего подсистемой, представляющей
сайт как таковой), который поддерживает четыре формата вызова:
 environment() (без параметров) — возвращает строку с наименованием режима

работы:
Сайт работает в режиме: {{ App::environment() }}

 environment() — возвращает true, если сайт работает в заданном режиме,

и false — в противном случае:
@if (App::environment('local'))
Сайт работает в режиме local
@endif

 environment(, . . . ) — возвращает true, если

сайт работает в одном из указанных режимов, и false — в противном случае:
@if (App::environment('local', 'testing', 'staging'))
Сайт работает в одном из тестовых режимов
@endif

 environment() — то же самое, что и предыдущий формат

вызова:
@if (App::environment(['local', 'testing', 'staging']))
Сайт работает в одном из тестовых режимов
@endif

3.4.4. Создание своих настроек
Ничто не мешает нам создать свои рабочие настройки, добавив их в один из модулей, хранящихся в папке config. Например, так можно создать в модуле config\app.php
настройку description, содержащую описание сайта:
return [
. . .
'description' => 'Электронная доска объявлений',
];

96

Часть II. Базовые инструменты

А потом — извлечь значение этой настройки:
{{ config('app.description') }}

Созданная таким образом основная настройка может брать значение из локальных
настроек (файла .env):
// Файл .env
APP_DESC="Электронная доска объявлений"
// Модуль config\app.php
return [
. . .
'description' => env('APP_DESC'),
];

Также можно создать в папке config новый модуль, предназначенный для хранения
вновь добавленных настроек: