Вы находитесь на странице: 1из 249

ОДО "Центр Обучающих Технологий "БелХард"

Михалькевич Александр Викторович

PHP PRO

2-ое издание

Минск 2016
2
УДК 004.415.53
ББК 32.973.2-018.2я7

Рецензент:

Об авторе:
Михалькевич Александр Викторович, программист, преподаватель
учебных курсов по PHP. Автор книг по “JavaScript”, “PHP базовый”, “PHP
Pro”, “HTML5”.

Михалькевич Александр Викторович


Разработка программного обеспечения и оказание услуг в этой области.
http://mikhalkevich.colony.by

ОДО "Центр Обучающих Технологий "БелХард" - Мн., 2016. – 242с.

ISBN

 В процессе разработки web-приложения на web-лаборатории


рассматриваются следующие темы: общие принципы ООП, MVC и
HMVC, применение данных технологий на практике. Используется
самый продвинутый на сегодняшний день php-фрэймворк –Laravel.
Быстрый способ создания админки. Вспомогательные инструменты
и технологии, которые необходимо знать каждому web-мастеру. В
приложении рассматриваются такие темы, как система контроля
версий, phpShtorm, разработка express-приложения.

Пособие рекомендовано к использованию слушателям курсов ОДО "Центр


Обучающих Технологий "БелХард"

УДК 004.415.53
ББК 32.973.2-018.2я7

© http://mikhalkevich.colony.by, 2016
© ОДО "Центр Обучающих Технологий
"БелХард", 2016
3
Оглавление

I. Инструментарий и проектирование
1. Программное обеспечение ----------------------------------------------------------------------------5
2. Верстка ----------------------------------------------------------------------------------------------------7
3. Schema.org ----------------------------------------------------------------------------------------------16
4. Шаблонизация -----------------------------------------------------------------------------------------20
5. MVC и HMVC -----------------------------------------------------------------------------------------24
II. Основы PHP ------------------------------------------------------------------------------------------27
1. Строки и числа ---- ------------------------------------------------------------------------------------27
2. Массивы ------------------------------------------------------------------------------------------------30
3. Классы объекты ---------------------------------------------------------------------------------------34
4. PDO ------------------------------------------------------------------------------------------------------52
III. Laravel ------------------------------------------------------------------------------------------------55
1. Перед установкой ------------------------------------------------------------------------------------55
2. Установка Laravel ------------------------------------------------------------------------------------57
3. Структура и жизненный цикл фрэймворка -----------------------------------------------------60
4. Удаление public из url проекта --------------------------------------------------------------------64
5. Маршрутизация --------------------------------------------------------------------------------------67
6. Artisan --------------------------------------------------------------------------------------------------71
7. Конфигурирование ----------------------------------------------------------------------------------76
8. Middleware --------------------------------------------------------------------------------------------81
9. Контроллеры -----------------------------------------------------------------------------------------83
10. Шаблоны --------------------------------------------------------------------------------------------88
11. База данных MySQL, миграции (migrate), первоначальная загрузка данных (seeder),
запросы ---------------------------------------------------------------------------------------------------93
12. Модели ----------------------------------------------------------------------------------------------101
13. Управление данными через Artisan tinker ----------------------------------------------------121
14. Request -----------------------------------------------------------------------------------------------123
15. Response ---------------------------------------------------------------------------------------------128
16. Модуль авторизации -----------------------------------------------------------------------------132
17. Постраничная навигация ------------------------------------------------------------------------135
18. Модуль обработки изображений --------------------------------------------------------------136
19. ProfilerDebugbar -----------------------------------------------------------------------------------137
20. Вспомогательные классы -----------------------------------------------------------------------138
21. Понимание IOC -----------------------------------------------------------------------------------139
22. ServiceProviders -----------------------------------------------------------------------------------146
23. ServiceConteiner ----------------------------------------------------------------------------------149
24. Фасады----------------------------------------------------------------------------------------------156
25. Контракты -----------------------------------------------------------------------------------------159
26. События ---------------------------------------------------------------------------------------------162
27. Кэш -------------------------------------------------------------------------------------------------171
28. Хелперы -------------------------------------------------------------------------------------------175
29. Elixir ------------------------------------------------------------------------------------------------184
30. Разработка пакетов -------------------------------------------------------------------------------186
31. Laravel Homestead --------------------------------------------------------------------------------195
32. Обработка файлов .csv --------------------------------------------------------------------------198
33. Парсинг --------------------------------------------------------------------------------------------200
IV. Ресурсы Laravel ------------------------------------------------------------------------------------201
1. Real-time приложение с широковещательными событиями --------------------------------201
2. QuickAdminс ролевым доступом ----------------------------------------------------------------207
3. Админка FrozenNode -------------------------------------------------------------------------------209
4
VI. Приложение ---------------------------------------------------------------------------------------215
1. PHPShtorm --------------------------------------------------------------------------------------------215
2. Модальное окно на ajax ---------------------------------------------------------------------------220
3. Node.js ------------------------------------------------------------------------------------------------227
4. Express-приложение -------------------------------------------------------------------------------229
5. Обзор рынка IT -------------------------------------------------------------------------------------236
5

I. Инструментарий и проектирование

1. Программное обеспечение

IDE:NetBeans, PHPShtorm, WebShtorm, – или любая другая


интегрированная среда разработки.

Nodepad++- Блокнот.

OpenServer – Локальный сервер (Содержит встроенные технологии:PHP,


Apache, MySQL и системы управления MySQL).

Git – система контроля версий

Composer – локальный менеджер зависимостей.

Firefox – Браузер.

Firebug – Отладчик кода на стороне клиента

FireFTP – или любой другой FTP-клиент.

Node.js – программная платформа

Удаленные репозитории Github.com и Bitbacket.org


6
Сайт github.com позиционируется как веб-сервис хостинга проектов с
использованием системы контроля версий git, а также как социальная сеть
для разработчиков. Пользователи могут создавать неограниченное число
репозиториев, для каждого из которых предоставляется wiki, система issue
tracking-а, есть возможность проводить code review и многое другое. GitHub
на данный момент самый популярный сервис такого рода, обогнав
Sourceforge и Google Code.

Для open-source проектов использование сайта бесплатно. При


необходимости иметь приватные репозитории, есть возможность перейти на
платный тарифный план:

После регистрации на сайте github.com создаем новый репозиторий. Жмем


New Repository, заполняем поле названия и, если необходимо, описание
проекта.

GitHub позволяет работать с репозиториями тремя способами: SSH, HTTP и


Git Read-Only, соответственно предоставляя ссылки трех видов для нашего
репозитория:

1. git@github.com:userlogin/Hello-world.git

2. userlogin@github.com/habrauser/Hello-world.git

3. https://github.com/userlogin/Hello-world.git

Для того, чтобы просто забрать репозиторий на локальную машину,


достаточно внутреннего протокола git (третья ссылка). Это наиболее
быстрый и эффективный способ, который обеспечивает анонимный доступ
только для чтения.

Отличительные особенности репозитория bitbacket.org заключаются в том,


что на bitbacket мы можем создавать как private, так и open source
репозитории. Также bitbacket понимает две системы контроля версий: git и
mercurial. Но ограничения бесплатного пользования есть и на bitbacket.org: к
репозиториям бесплатно можно подключить не более пяти пользователей.

При работе с удаленными репозиториями можно сперва создать удаленный


репозиторий, потом клонировать его на свой локальный компьютер.

Клонирование репозитория. Листинг 1.1


git clone https://github.com/userlogin/Hello-world.git

Можно поступить другим путем. Создать локальный и удаленный


репозитории. Затем их связать, например, с помощью команды remote.
7
Часто используемые команды при работе с github.com или bitbacket.org

Команды git. Листинг 1.2


Git init
Git add *
Git commit –m “сообщение”
Git remote add project http://.....
Git push project master
8

2. Верстка

Сайты бывают следующих типов:

 сайт визитка
 портфолио
 корпоративный
 интернет-магазин
 социальная сеть
 CRM, блоки управления и прочее.

В зависимости от типа разрабатываемого сайта, используются различные


способа проектирования. Несмотря на то, что любым шаблоном
проектирования можно построить сайт любого типа и сложности, но
использовать простой шаблон для разработки интернет-магазина, соц.сети
или корпоративного сайта не рационально.

Сайт начинают разрабатывать с главной страницы. HTML-код главной


страницы можно получить одним из двух способов:

 сверстать с нуля. Для этого необходимы навыки работы с HTML, CSS,


bootstrap, умение работать с селекторами.
 либо по шаблону. В качестве шаблона можно использовать любой
существующий сайт, сохранив страницу “как html”.

Можно выделить основные три области любой страницы сайта, которые


также необходимо учитывать при проектировании:

 предметика (как правило, верх сайта: шапка + верхнее меню)


 конкретика (центральная часть сайта)
 для пользователя (реклама, банеры)

Область предметика должна объяснить пользователю о чем данный сайт и


направить его в область конкретика. Если уже пользователь заинтересовался
конкретикой и отвел взгляд ниже, тогда рационально показывать область
для пользователя.

Способы верстки:

Табличная – способ верстки с применением тэга <table>. В чистом виде не


применяется.

Блочная
9
1. Фиксированная блочная верстка

CSS и HTML код фиксированной верстки. Листинг 2.1


<div style="width:500px; text-align:center; margin:0 auto;
padding:10px">
<p>Этот <code>блок</code> имеет фиксированную ширину 500
пикселей.
</div>

2. Резиновая блочная верстка

CSS и HTML код резиновой блочной верстки. Листинг 2.2


<style>
body
{
background-image: url(media/images/body.jpg);
margin:0px auto;
width: 100%;
}

#hedImg
{
position: absolute;
top: 50px;
left: 141px;
width: 78%;
float: left;
}

#logo
{
position: absolute;
top: 87px;
left: 210px;
}

#logoLbl
{
font-size: 25px;
font-family: Verdana, Tahoma, Sans-Serif;
}

#rap
{
background-color: #FFFFFF;
padding: 15px;
margin: 0 auto;
height: 500px;
width: 78%;
}

#menu
{
position: absolute;
top: 157px;
left: 141px;
width: 78%;
10
float: left;
}
</style>

<div id = "rap">
<div id = "hed">
<img src = "_mod_files/ce_images/top.png" id = "hedImg"
alt = "Шапка сайта">
</div>
<div id = "logo">
<p id = "logoLbl">C#, Delphi, Visual Basic</p>
</div>
<div id = "menu">
<img src = "_mod_files/ce_images/menu.png" width = 100%
height = "39" >
<ul>
<li><a id = "menuLink1" href =
"http://programer.web-box.ru/">ГЛАВНАЯ</a></li>
<li><a id = "menuLink2" href =
"http://programer.web-box.ru/">О САЙТЕ</a></li>
<li><a id = "menuLink3" href =
"http://programer.web-box.ru/">ИСХОДНИКИ</a></li>
<li><a id = "menuLink4" href =
"http://programer.web-box.ru/">ПРОГРАММЫ</a></li>
<li><a id = "menuLink5" href =
"http://programer.web-box.ru/">УРОКИ</a></li>
</ul>
</div>
</div>

Предыдущие два вида верстки относятся к традиционной блочной


верстке.

Гибкая блочная верстка

Гибкая блочная верстка пришла на замену традиционной блочной модели.


Свойство стилей display:flex указывает на то, что вложенные элементы
будут гибкими (flexable), т.е. подстраиваться под родительский.

Гибкая блочная верстка. Листинг 2.3


<nav class="topmenu">
<a href="#"> Box 1</a>
<a href="#"> Box 2</a>
<a href="#"> Box 3</a>
<a href="#"> Box 4</a>
</nav>
<style>
.topmenu {
display: flex;
display: -moz-box;
display: -webkit-flex;
11
text-align:center;
flex-direction: row;
width:100%;
margin:0 auto;
}
.topmenu a{
display:block;
-moz-box-flex:1;
-webkit-flex:1;
flex:1;
border:solid 1px black;
padding:10px;
}
</style>

Обратите внимание на свойство flex:1. Это говорит о том, что все гибкие
блоки будут равны между собой. Единица ширины гибкого блока
высчитвается по следующей формуле:

E = W÷X

Где:

E – Единица ширины

X – Сумма значений всех атрибутов flex

W – Ширина внешнего родительского блока, для которого прописано


свойтсво display:flex.

После определения единицы ширины, чтобы определить ширину гибкого


блока, значение единицы ширины нужно умножить на значение свойства
flex. В нашем случае, все блоки равны flex:1. Значит:

ширина блока = E×1;

Адаптивная верстка

Даже идеальное браузерное отображение не делает никакой разницы


между десктопными браузерами и принтерами или между мобильными
устройствами и голосовым браузером. Чтобы решить эту проблему, W3C
создала список медиатипов для классификации каждого браузера или
устройства по медиакатегориям. Медиатипы могут принимать значения:
all, braille, embossed, handheld, print, projection, screen, speech, tty и tv.

Все эти медиатипы созданы с одной целью: чтобы мы могли лучше


проектировать дизайн для каждого типа браузера или устройства,
просто загружая нужный CSS.
12
Следовательно, устройство с экраном будет игнорировать CSS, созданный
для медиатипа print, и наоборот. А для стилевых правил, которые
применимы ко всем устройствам, в спецификации создана супергруппа
all. На практике это означает правку media-атрибута ссылки:

Медиа-атрибуты ссылок. Листинг 2.4


<link rel="stylesheet" href="global.css" media="all" />
<link rel="stylesheet" href="main.css" media="screen" />
<link rel="stylesheet" href="paper.css" media="print" />

Также имеется возможность создавать media-блоки в самих стилях:

Медиа-запросы в файлах стилей. Листинг 2.5


@media screen {
body {
font-size: 100 %;
}
}
@media print {
body {
font-size: 15 pt;
}
}

Кроме того, имеется возможность адаптироваться не под само медиа-


устройство, а под размер экрана любого медиа-устройства.

Учитываем ширину эранана в медиа-запросах. Листинг 2.6


#page {
margin: 36px auto;
width: 90 %;
}
@media screen and (max-width: 768px) {
#page {
position: relative;
margin: 20px;
width: auto;
}
}

Разметка страницы с помощью bootstrap

Простое центрирование контента страницы, включая это содержимое в


.container. Контейнер установлен в width на различных контрольных точках
медиа запросов для соответствия с нашей системой разметки.

Следует отметить, что, благодаря padding и фиксированной ширине,


контейнеры не вложены по умолчанию.

Контейнер с классом container. Листинг 2.7


<div class="container">
...
13
</div>

Превратить любую фиксированную ширину сетки макет в полную ширину


макета, можно, изменив последний .container на .container-fluid.

Пример резинового макета. Листинг 2.8


<div class="container-fluid">
<div class="row">
...
</div>
</div>

Используя единичный набор .col-md-* классовой разметки, вы можете


создать базовую систему разметки, которая запускается одновременно на
мобильные устройства и планшетные устройства (от экстра маленьких до
малых диапазонов) до того, как выстроится горизонталь на рабочем столе
(средних) устройств. Разместите столбцы разметки в любом .row.

Использование col-md-*. Листинг 2.9


<div class="row">
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
<div class="col-md-1">.col-md-1</div>
</div>
<div class="row">
<div class="col-md-8">.col-md-8</div>
<div class="col-md-4">.col-md-4</div>
</div>
<div class="row">
<div class="col-md-4">.col-md-4</div>
<div class="col-md-4">.col-md-4</div>
<div class="col-md-4">.col-md-4</div>
</div>
<div class="row">
<div class="col-md-6">.col-md-6</div>
<div class="col-md-6">.col-md-6</div>
</div>

Элементы распределятся следующим образом:


14

На устройствах с небольшим размером экрана увидим следующее:

Не хотите, чтобы ваши колонки просто складывались на небольших


устройствах? Используйте очень маленькие xs или средние md классы
разметки устройства, добавляя .col-xs-* .col-md-* к вашим столбцам.
Смотрите пример ниже для лучшего понимания как это работает.

Использование col-xs-*. Листинг 2.10


<div class="row">
<div class="col-xs-12 col-md-8">.col-xs-12 .col-md-8</div>
<div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
15
</div>
<div class="row">
<div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
<div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
<div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
</div>
<div class="row">
<div class="col-xs-6">.col-xs-6</div>
<div class="col-xs-6">.col-xs-6</div>
</div>

Смещенные колонки

Переместить колонки направо с помощью .col-md-offset-* класса. Эти классы


увеличивают отступ слева столбца * колонки. Например, .col-md-offset-4
сдвигает .col-md-4 , пропуская один такой же столбец

Смещенные колонки. Листинг 2.11


<div class="row">
<div class="col-md-4">.col-md-4</div>
<div class="col-md-4 col-md-offset-4">.col-md-4 .col-md-
offset-4</div>
</div>
<div class="row">
<div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-
offset-3</div>
<div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-
offset-3</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3">.col-md-6 .col-md-
offset-3</div>
</div>

Вложенные столбцы

Чтобы вложить ваше содержание в разметку, необходимо добавить новый


.row и набор .col-md-* столбцов в существующую .col-md-* колонку.
Вложенные строки должны включать в себя набор столбцов, которые
добавляются до 12 или менее.

Вложенные столбцы. Листинг 2.12


<div class="row">
<div class="col-md-9">
Level 1: .col-md-9
16
<div class="row">
<div class="col-md-6">
Level 2: .col-md-6
</div>
<div class="col-md-6">
Level 2: .col-md-6
</div>
</div>
</div>
</div>
17

3. Schema.org

Schema.org – это стандарт семантической разметки данных в сети,


объявленный поисковыми системами Google, Bing и Yahoo! летом 2011 года.
Цель семантической разметки – сделать интернет более понятным,
структурированным и облегчить поисковым системам и специальным
программам извлечение и обработку информации для удобного её
представления в результатах поиска. Яндекс с осени 2011 года понимает этот
формат и поддерживает его в некоторых партнерских программах. Разметка
происходит непосредственно в HTML-коде страниц с помощью специальных
атрибутов и не требует создания отдельных экспортных файлов.

Микроданные (HTML microdata) — это международный стандарт


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

Что такое Schema.org?

Это сайт, который содержит коллекцию схем (html-тегов), которые


вебмастера могут использовать для разметки своих страниц способами,
признанными такими крупнейшими поисковыми системами, как Bing,
Google, Yahoo! и Яндекс, которые полагаются на эту разметку для
улучшения отображения результатов поиска, делая процесс поиска
правильных веб-страниц проще для людей.

Многие сайты генерируются из структурированных данных, которые чаще


всего хранятся в БД. Когда данные переводятся в HTML, становится очень
сложно восстановить первоначальные структурированные данные. Многие
приложения, особенно поисковые системы, могут получить значительные
преимущества, имея прямой доступ к структурированным данным. Разметка
страниц позволяет поисковым системам понимать информацию на веб-
страницах и предоставлять пользователю именно ту информацию, которую
он ищет. Разметка также может задействовать новые инструменты и
приложения, которые используют эту структуру.

Этот общий словарь облегчит вебмастерам выбор правильной схемы


разметки и поможет получить максимум выгоды из приложенных усилий.
Так, воодушевленные sitemaps.org, Bing, Google and Yahoo! собрались
вместе, чтобы создать для вебмастеров эту коллекцию схем.

Как использовать?
18
Начнем с конкретного примера. Представим, что у нас есть страница о
фильме «Аватар» со ссылкой на трейлер, информацией о режиссере и т. п.
HTML-код может выглядеть примерно так:

Пример html-страницы. Листинг 3.1


<div>
<h1>Аватар</h1>
<span>Режиссер: Джеймс Кэмерон (род. 16 августа 1954 г.)</span>
<span>Фантастика</span>
<a href="/../movies/avatar-theatrical-trailer.html">Трейлер</a>
</div>

В первую очередь необходимо указать, какая часть страницы посвящена


непосредственно фильму «Аватар». Для этого добавим атрибут itemscope к
HTML-тегу, в который заключена эта информация:

Атрибут itemscope. Листинг 3.2


<div itemscope>
<h1>Аватар</h1>
<span>Режиссер: Джеймс Кэмерон (род. 16 августа 1954 г.)
</span>
<span>Фантастика</span>
<a href="/../movies/avatar-theatrical-
trailer.html">Трейлер</a>
</div>

Добавляя itemscope, мы тем самым обозначаем, что HTML-код,


содержащийся в блоке <div>...</div>, описывает некоторую сущность.

Пока мы только объявили, что речь идет о какой-то сущности, но не


сообщили, что это за сущность. Чтобы указать тип сущности, добавим
атрибут itemtype сразу после itemscope.

Атрибут itemtype. Листинг 3.3


<div itemscope itemtype="http://schema.org/Movie">
<h1>Аватар</h1>
<span>Режиссер: Джеймс Кэмерон (род. 16 августа 1954
г.)</span>
<span>Фантастика</span>
<a href="/../movies/avatar-theatrical-
trailer.html">Трейлер</a>
</div>

Тем самым мы уточняем, что сущность, описание которой заключено в теге


<div>, представляет собой фильм (тип Movie в иерархии типов schema.org).
Названия типов имеют вид URL, в нашем случае http://schema.org/Movie.

Какую дополнительную информацию о фильме «Аватар» можно


предоставить поисковым системам? О фильме можно сообщить множество
интересных сведений: актерский состав, режиссер, рейтинг. Чтобы отметить
свойства сущности, используется атрибут itemprop. Например, чтобы указать
19
режиссера фильма, добавим атрибут itemprop="director" к HTML-тегу,
содержащему имя режиссера. (Полный список свойств, которые можно
задать для фильма, приведен на странице http://schema.org/Movie.)

Атрибут itemprop. Листинг 3.4


<div itemscope itemtype="http://schema.org/Movie">
<h1 itemprop="name">Аватар</h1>
<span>Режиссер: <span itemprop="director">Джеймс
Кэмерон</span> (род. 16 августа 1954 г.)</span>
<span itemprop="genre">Фантастика</span>
<a href="/../movies/avatar-theatrical-trailer.html"
itemprop="trailer">Трейлер</a>
</div>

Обратите внимание, что мы добавили дополнительный тег <span>...</span>,


чтобы привязать атрибут itemprop к соответствующему тексту на странице.
Тег <span> не влияет на отображение страницы в браузере, поэтому его
удобно использовать вместе с itemprop.

Теперь поисковые системы смогут понять не только то, что


http://www.avatarmovie.com — это ссылка, но и то, что это ссылка на трейлер
фантастического фильма «Аватар» режиссера Джеймса Кэмерона.

Иногда значение свойства может само являться сущностью, с собственным


набором свойств. Например, режиссер фильма может быть описан как
сущность с типом Person, у которой есть свойства name (имя) и birthDate
(дата рождения). Чтобы указать, что значение свойства представляет собой
сущность, необходимо добавить атрибут itemscope сразу после
соответствующего itemprop.

Вложенные сущности. Листинг 3.5


<div itemscope itemtype="http://schema.org/Movie">
<h1 itemprop="name">Аватар</h1>
<div itemprop="director" itemscope
itemtype="http://schema.org/Person">
Режиссер: <span itemprop="name">Джеймс Кэмерон</span> (род.
<span itemprop="birthDate">16 августа 1954 г.</span>)
</div>
<span itemprop="genre">Фантастика</span>
<a href="/../movies/avatar-theatrical-trailer.html"
itemprop="trailer">Трейлер</a>
</div

Типы и свойства schema.org

Кроме типов Movie и Person, ранее упомянутых, schema.org описывает


множество разнообразных типов сущностей, для каждого из которых
определен набор свойств.
20
Наиболее обобщенный тип сущности — это Thing (нечто), у которого есть
четыре свойства: name (название), description (описание), url (ссылка) и image
(картинка). Более специализированные, частные типы имеют общие свойства
с более универсальными. Например, Place (место) — частный случай Thing, а
LocalBusiness (местная фирма) — частный случай Place. Частные типы
наследуют свойства родительского типа. (Более того, тип LocalBusiness
является частным случаем Place и частным случаем Organization, поэтому
наследует свойства обоих родительских типов.)

Вот список некоторых популярных типов сущностей:

Творческие произведения: CreativeWork (творческое произведение), Book


(книга), Movie (фильм), MusicRecording (музыкальная запись), Recipe
(рецепт), TVSeries (телесериал)...

Встроенные нетекстовые объекты: AudioObject (аудио), ImageObject


(изображение), VideoObject (видео)

Event (событие)

Organization (организация)

Person (человек)

Place (место), LocalBusiness (местная фирма), Restaurant (ресторан)...

Product (продукт), Offer (предложение), AggregateOffer (сводное


предложение)

Review (отзыв), AggregateRating (сводный рейтинг).


21

4. Шаблонизация

Шаблонов проектирования, или способов организации кода огромное


количество. Каждый новый фрэймворк предлагает свою структуру папок
(классов, готовых библиотек, модулей и иных файлов). Но все их можно
сгруппировать в несколько категорий:

 Простой паттерн
 Шаблонная функция и метод буферизации.
 MVC и HMVC (модель, вид и контроллер и иерархически связанные
контроллеры, вид и модель).
 MV-VM (данные передаются сразу в шаблон, минуя логику
контроллеров, и пользователь из шаблона может сразу менять данные).
Данный способ организации кода используется в фрэймворках
JavaScript.

Простой паттерн

Используетя для простых проектов. Данный паттерн, как правило, не


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

В данном паттерне базовый шаблон (html-файл страницы) делится на top.php,


bottom.php и центральную часть (по-умолчанию index.php).Top.php и
bottom.php подключаются на всех страницах.

Плюсы:

• простота

Минусы:

• разрезан на 2 части

• видимость переменных

• проблемы со вложенной структурой.

• не предназначен для последующего расширения.

Пример использования:
22
Простой шаблон в действии.Листинг 4.1
<?php
// Установка переменных шаблона.

$title = 'Добро пожаловать на сайт';

// Header.

include 'v_header.php';

// Содержание.

echo “Привет мир!”;

// Footer.

include 'v_footer.php';

Пример использования с GET-параметрами и SQL-запросами

Простой шаблон с GET-параметрами иSQL-запросами. Листинг 4.2


<?php
require_once ("templates/top.php");
if (!$_GET['url'])
{
$file = $_GET['url'];
}else
{
$file = "index";
}
$query = "SELECT * FROM $tbl_news WHERE url = '".$file."' AND
hide='show'";
$adr = mysql_query($query);
if (!$adr) exit($query);
$tbl_users = mysql_fetch_array($adr);
echo "<h1>".$tbl_users['name']."</h1>";
echo $tbl_users['body'];
require_once ("templates/bottom.php");

В файле top.php будет подключение файла config.php, в котором прописано


подключение к базе данных. В файлах templates/top.php и
templates/bottom.php находятся верхний и нижний части шаблона.

Шаблонная функция

Определение в вызываемом php-файле (например, index.php) специальной


функции вывода содержимого, которую вызывает базовый шаблон.

Пример использования:

Файл index.php. Листинг 2.1


<?php
23
function content()
{
<a href="index.php">Наглавную</a>
<hr />
<div><?=$text?></div>
}

// Установка переменных основного шаблона.

$title = 'Главная';

// Генерация HTML всей страницы.

include 'v_main.php';

Файл шаблона v_main.php

Файлv_main.php. Листинг 4.3


<html>
<head>
<title><?=$title?></title>
</head>
<body>
<h1><?=$title?></h1>
<? content(); ?>
<p><small>Все права защищены. Адрес. Телефон.<small></p>
</body>
</html>

Плюсы:

• целостность

• ограничение видимости переменных

Минусы:

• большинство минусов совпадает с минусами простого шаблона.

• представление вызывает функцию контроллера

Метод буферизации

Занесение шаблона в буфер.

Плюсы:

• вложенность шаблонов

• независимость представления от контроллера


24
• целостность шаблона

• возможность кэширования

Минусы:

• видимость переменных

• громоздкость кода

• проблеммы с вложенностью файлов

Весь код, который находится между функциямиob_start() и ob_get_clean(), на


экран не выводится, а заносится в переменную.

Пример использования:

Файл index.php. Листинг 4.4


// Генерация HTML по внутреннему шаблону в $content.

ob_start();
<ahref="index.php">Наглавную</a>
<hr />
<div><?=$text?></div>
$content = ob_get_clean();

// Установка переменных основного шаблона.

$title = 'Главная';

// Генерация HTML всей страницы.

include 'v_main.php';

Файл шаблона v_main.php

Файлv_main.php. Листинг 4.5


<html>
<head>
<title><?=$title?></title>
</head>
<body>
<h1><?=$title?></h1>
<?=$content?>
<div>Все права защищены. Адрес. Телефон.</div>
</body>
</html>

MVC
25
Паттерн трехуровневого разделения логики, на модель, шаблон и
контролллер.
26

6. MVC и HMVC

Основной паттерн для создания фреймворков и других web-приложений.

Фреймворки в PHP зачастую используют для больших проектов. Основное


преимущество - это, конечно же, предоставление возможности строить
проект при помощи паттерна MVC (Model-View-Controller)

Плюсы:

• вложенность шаблонов

• независимость представления от контроллера

• целостность шаблона

• возможность кэширования

• видимость переменных

• лаконичность кода

Расшифруем само понятие MVC:

Model - модели данных, которые многие и без того используют без


фреймфорков. Фактически обычные классы для работы с разными данными.

View - представления. Это шаблонизатор, например, SMARTY либо


собственный. Представления - это вид, в котором отображаются данные.

Controller – основной вызываемый класс, содержащий базовую логику


приложения.
27

Для понимания модели HMVC необходимо также иметь представление о


роутинге (или маршрутизации) запросов. Главное отличие от MVC-паттерна
- возможность передачи запроса по контроллерам.

По такому принципу построены почти все современные web-фреймворки (за


исключением клиентских, таких как Angular, Backbone и др.)

В HMVC фактически процесс не меняется, т.к. последовательность действий


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

HMVC позволяет легко собирать воедино и легко управлять большими


кусками кода. А фреймворк вносит существенную долю автоматизации и
простоты управления. Фреймворк - это склад различных классов и
библиотек, которые позволяют отказаться от изобретения велосипедов и
начать использовать готовые решения, тем самым увеличив скорость
разработки. Любой разработчик, если он занимается профессиональной
разработкой, со временем приходит к созданию собственной библиотеке
классов, основанной, как правило, на уже готовых классах.

Современные фрэймворки не только предлагают для использования готовые


классы, но и свою структуру папок.

У каждого из фрэймворков есть свои преимущества и свои недостатки.


28
HMVC

По концепции MVC, когда мы делаем запрос, мы сперва попадаем в


контроллер (Controller). Затем в конроллере может происходить вызов
модели (Model) (т.е. получение данных из модели),а затем передача этих
данных в шаблон представления (View). Все очень просто, но это не всегда
бывает удобно, хотя бы потому, что часто приходится вносить изменения в
контроллеры либо дублировать контроллеры из-за того, что в них вносятся
незначительные изменения.

В связи с этим придумали концепцию HMVC, т.е. иерархическая MVC. По


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

Концепцию HMVC помогают понять следующие технологии:

 Наследование классов,
 Использование переменных-шаблонов.
29

II. Основы PHP

1. Строки и числа

Строки

Размер строк в PHP ограничивается только объемом памяти доступной


интерпретатору php.

4 способа вывода строк: одинарные ковычки (‘), двойные кавычки (“),


heredoc и nowdoc.

Рассмотрим работу одинарных кавычек.

Одинарные ковычки. Листинг 1.1


$string1 = ‘hello world’;
$string2 = ‘hello ’ .$name;
$string3 = 'hello ' .$name. ’!’;
$string4 = ‘I\’ve gone to the store’;
$price = ‘$250’ // знак $ читается как знак доллара

В двойных ковычках знак доллара читается как знак переменной.


Экранированные двойные кавычки не распознаются. В двойных кавычках
проверяется интерполяция переменных и служебные комбинации.

Двойные кавычки. Листинг 1.2


$string1 = “hello world”;
$string2 = “hello “ .$name;
$string3 = “hello “ .$name. “!”;
$price = “$var” // знак $ читается как символ переменной.

Строки в формате heredoc поддерживают все интерполяции и служебные


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

Формат heredoc. Листинг 1.3


print <<< END
Это строки, в которой могут быть любые ковычки. И двойные (“) и
одинарные (‘).
А также любое количество абзацев.
$var - переменная
END;

Конкатенация строк в формате heredoc

Конкатенация строк в формате heredoc. Листинг 1.4


$html = <<< END
30
<div>
END
. $listen .
‘</div>’;
Print $html

Рассмотрим еще один пример интерполяции строк формата heredoc

Интерполяция строк в формате heredoc. Листинг 1.5


print <<< END
Сегодня
END
. strftime(‘%c’) . <<< END
Завтра
END
. strftime(‘%c’,time()+86400);

Формат nowdoc похож на heredoc, но не поддерживает интерполяцию строк.


Таким образом, формат heredoc связан с форматом nowdoc так же, как строки
в одиночных кавычках со строками в двойных кавычках.

Формат nowdoc. Листинг 1.6


$js = <<<’JS’
$.ajax({
‘url’:’ajax.php’
})
JS;
print $js;

Функции, работы со строками

strpos – поиск подстроки

substr – выделение подстрок

substr_replace – замена подстрок

strstr – поиск первого вхождения строки

strlen – возвращает количество байт в строке

strrev – обратная перестановка строки по словам или байтам

str_rand – генерирование случайной строки

ucfirst – преобразование первого символа строки в верхний регистр

ucwords – преобразование первой буквы каждого слова в верхний регистр


31
trim – удаление начальных или конечных пропусков в строке

fputcsv – формирование строки значений, разделенных запятыми

fopen – если данные хранятся в файле или доступны по url, откройте файл
функцией fopen, и прочитайте данные функцией fgetcsv.

explode – разбиение строк на фрагменты (элементы массива)

implode – обратное действие функции explode, склейка фрагментов в строку.

wordwrap – перенос текста по заданной длине строки.

is_numeric – для проверки, является ли значение переменной числом.


Функция работает как с целыми, так и с дробными числами (разделителем
должна быть точка . )

floor – округление дробного числа в меньшую сторону

ceil – округление числа в большую сторону.


32

2. Массивы

Массив в PHP - это упорядоченное отображение, которое устанавливает


соответствие между значением и ключом.

Массив определяется с помощью ключевого слова array либо, начиная с


версии PHP 5.4, квадратными скобками.

Определение массива. Листинг 2.1


$array = array(
"foo" => "bar",
"bar" => "foo",
);

// Начиная с PHP 5.4


$array = [
"foo" => "bar",
"bar" => "foo",
];

Ключом массива назвается левая часть определения (то, что находится слева
от символов “=>”). Значением массива назвают правую часть определения.

Ключом может быть либо строка, либо число. Если ключом является число,
массив называют индексированным. Если ключом является строка, массив
ассоциативный. Впрочем, PHP не делает разницы между индексированным и
ассоциативным массивом.

Значения элементов массива могут содержать любые типы данных, в том


числе и другой массив, в таком случае массив называют многомерным
(двойным, тройным и т.д.)

Индексированные массивы можно определять без ключа:

Определение индексированного массива без ключа. Листинг 2.2


$arr = array("foo", "bar", "hello", "world");

Для вывода информации о массиве можно воспользоваться функциями


print_r() или var_dump(), а также html-тэгом <pre></pre>, который нужен для
удобного вывода массива (с отступами):

Вывод информации о массиве. Листинг 2.3


// использование print_r()
echo “<pre>”
33
print_r($arr);
echo “</pre>”
// использование var_dump()
echo “<pre>”
var_dump($arr);
echo “</pre>”

Для перебора элементов массива можно воспользоваться оператором


foreach()

Получение всех значений массива. Листинг 2.4


foreach($arr as $one){
echo $one;
echo “<br />”;
}

Для получения ключей и значений массива воспользуемся тем же foreach():

Получение всех значений и ключей массива. Листинг 2.5


foreach($arr as $key => $value){
echo $key . “ => ” . $value;
echo “<br />”;
}

Для удаления элементов массива можно воспользоваться функцией unset().

Удаление элементов массива. Листинг 2.6


unset($arr[0]);
unset($arr[1], $arr[5]);

Чтобы удалить несколько смежных элементов массива, воспользуемся


функцией array_splice()

Удаление нескольких смежных элементов массива. Листинг 2.7


array_splice($arr, $offcet, $length)

Узнать количество элементов массива можно с помощью функции count().


Эту же функцию часто применяют для проверки массива на существование.

Использование функции count(). Листинг 2.8


if(count($arr)>0){
//массив $arr не пустой.
}

Слияние массивов.

Использование функции array_merge. Листинг 2.9


array_merge($arr, $arr2);
34
array_merge($arr, array(‘more’));
array_unique(array_merge($arr, $arr2));

Для проверки ключа на существование можно воспользоваться функцией


array_key_exists().

Проверка ключа на существование. Листинг 2.10


if(array_key_exists(‘key’, $arr)){
//$arr[‘key’] существует
}

Для проверки значения на существование, воспользуемя функцией in_array().

Проверка значение на существование. Листинг 2.11


if(in_array($value, $arr)){
// В массиве $arr имеется элемент со значением $value.
}

Существует множество встроенных функций по сортировке массива:

sort() – сортировка массива по возрастанию,

rsort() – сортировка массива по убыванию,

asort() – сортировка ассоциативного массива по возрастанию,

arsort() – сортировка ассоциативного массива по убыванию,

ksort() – сортировка массива по возрастанию ключей,

krsort() – сортировка массива по убыванию ключей,

array_reverse() – расстановка элементов массива в обратном порядке,

shuffle() – перемешивание элементов массива случайным образом,

natsort() – выполняет "естественную" сортировку массива.

Пример использования

Сортировка массива по возрастанию. Листинг 2.12


$arr = array("2", "1", "4", "3","5");
sort($arr);
for($i=0; $i < count($arr); $i++)
{
echo ("$i:$arr[$i] ");
}
// выводит "0:1 1:2 2:3 3:4 4:5"
35
Чтобы применить функцию к каждому элементу массива, можно
воспользоваться функцией array_walk().

Применение функции для каждого элемента массива. Листинг 2.13


array_walk($arr, function(&$value, $key){

});
36

3. Классы и объекты

Класс – контейнер для логически связанных данных и функций. Фактически


класс – это тип данных.

Пример объявления класса:

Объявление класса. Листинг 3.1


class Article
{
// тело класса
}

Имена классов в PHP принято писать с большой буквы.

Объект – совокупность конкретных данных и функций для их обработки.


Фактически объект – это переменная, тип данных которой задается
соответствующим классом.

Для объявления объекта используется ключевое слово new.

Пример объявления объекта:

Объявление объекта. Листинг 3.2


$a = new Article();

Класс может содержать методы и свойства. Методы – это функции класса.


Свойства – это переменные класса. Значением свойств может быть пустое
значение, число, строка или массив.

Объявление свойств и методов класса. Листинг 3.3


class Article
{
var $id;
var $myint = 3;
var $mystr = ‘str’;
var $myarr = array(‘Jesse’);
publid function content(){
// содержимое метода
}
}

При попытке присвоить любое другое значение происходит аварийное


завершение PHP.
37
Чтобы присвоить переменной неконстантное значение, нужно выполнить
присвоение внутри метода класса.
Присвоение свойству неконстантного значения. Листинг 3.4
class Article{
var $str;
public function update($id){
$this->str = ‘My id is ’.$id;
}
}

$this – специальная переменная, содержащая ссылку на объект текущего


класса.
Символ -> служит для обращения к методам и свойствам класса через
объект.
Обращение к свойствам и методам класса. Листинг 3.5
$a = new Article();
echo $a->id;
echo $a->content()

Три ключевых понятия ООП

ООП – объектно-ориентированное программирование. Это одна из самых


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

Инкапсуляция

Инкапсуляция — свойство языка программирования, позволяющее


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

Наследование

Наследование — один из четырёх важнейших механизмов объектно-


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

При наследовании используется ключевое слово extents:


38
Пример наследования. Листинг 3.6
class Foo
{
public function printItem($string)
{
echo 'Foo: ' . $string . PHP_EOL;
}

public function printPHP()


{
echo 'PHP is great.' . PHP_EOL;
}
}

class Bar extends Foo


{
public function printItem($string)
{
echo 'Bar: ' . $string . PHP_EOL;
}
}

$foo = new Foo();


$bar = new Bar();
$foo->printItem('baz'); // Выведет: 'Foo: baz'
$foo->printPHP(); // Выведет: 'PHP is great'
$bar->printItem('baz'); // Выведет: 'Bar: baz'
$bar->printPHP(); // Выведет: 'PHP is great'

Полиморфизм

Полиморфизм — взаимозаменяемость объектов с одинаковым интерфейсом.


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

Рассмотрим свойство полиморфности классов на основе следующего


примера:

Полиморфность классов. Листинг 3.7


class A {
// Выводит, функция какого класса была вызвана
function Test() { echo "Test from A\n"; }
// Тестовая функция — просто переадресует на Test()
function Call() { Test(); }
}
class B extends A {
// Функция Test() для класса B
function Test() { echo "Test from B\n"; }
}
$a=new A();
$b=new B();
39
Пример использования

Реализация полиморфности. Листинг 3.8


$a->Call(); // выводит "Test from A"
$b->Test(); // выводит "Test from B"
$b->Call(); // Внимание! Выводит "Test from B"!

Запрет наследования
Дла запрета наследования классов используется ключевое слово final. Для
этого необходимо пометить все нужные методы или весь класс ключевым
словом final.
Ключевое слово final. Листинг 3.9
final class Mysql{}
final public function connect(){}

Спецификаторы (модификаторы) доступа

Модификатор public позволяет обращаться к свойствам и методам отовсюду.


Модификатор private позволяет обращаться к свойствам и методам только
внутри текущего класса. Модификатор protected позволяет обращаться к
свойствам и методам только текущего класса и класса, который наследует
свойства и методы родительского класса.

Использование ключевых слов public, protected, private. Листинг 3.10


class Person {
public $name;
protected $age;
private $salary;
public function __construct(){ }
protected function set_age(){ }
private function set_salary(){ }
}

Статические методы и свойства


Помимо символа ->, для обращения к методам или свойствам можно
использовать символ :: (двойное двоеточие). Этот синтаксис предназначен
для обращения к статическим методам класса. Статические методы работают
независимо друг от друга и от других методов и свойств. В статическом
методе не может использоваться ссылка $this.
Объявление статического метода. Листинг 3.11
class Article{
public static function getuser(){
// содержимое метода.
}
}
40
Обращаться к статическому методу класса можно без создания объекта.

Обращение к статическому методу. Листинг 3.12


echo Article::getuser();

Внутри класса, в котором определен статический метод, для обращения к


нему используется обозначение self::
Обращение к статическому методу внутри класса. Листинг 3.13
class Article{
public static function getuser(){
// содержимое метода.
}
public function moreuser(){
return self::getuser();
}
}

Константы класса
Константы определяются как свойства класса, но с использованием метки
const.
Определение констант класса. Листинг 3.14
class Math{
const pi = 3.14159;
}
// math::pi

К константам, как и к статическим свойствам, можно обращаться без


предварительного создания объекта, при этом используется синтаксис двух
двоеточий(::).
Для обращения к константе внутри метода класса, используется префикс
self::
Префикс self::. Листинг 3.15
class Math{
const pi = 3.14159;
protected $radius;
public function cirle(){
return self::pi * $this->radius;
}
}

Магические или предопределенные свойства


Предопределённые константы используются для получения информации о
вашем коде. Имя такой константы пишется заглавными буквами между
сдвоенными подчеркиваниями, например, __LINE__ и __FILE__ . Вот
несколько полезных предопределённых констант, доступных в PHP:
41
__LINE__ возвращает номер строки в исходном файле, где используется
константа:
Константа __LINE__. Листинг 3.16
<?php
echo&nbsp;"Номер строки: " . __LINE__;&nbsp;// Номер строки: 2
echo&nbsp;"Номер строки: " . __LINE__;&nbsp;// Номер строки: 3
echo&nbsp;"Номер строки: " . __LINE__;&nbsp;// Номер строки: 4

__FILE__ представляет имя файла, включая полный путь:

Константа __FILE__. Листинг 3.17


<?php
echo&nbsp;"Имя файла: " . __FILE__;
// Имя файла: C:\wamp\www\index.php

__DIR__ представляет только путь к файлу:

Константа __DIR__. Листинг 3.18


<?php
echo&nbsp;"Путь к файлу: " . __DIR__;
// Путь к файлу: C:\wamp\www

__CLASS__ представляет имя текущего класса:

Константа __CLASS__. Листинг 3.19


<?php
class Sample
{
public function __construct() {
echo __CLASS__;
}
}
$obj = new Sample(); // Sample

__FUNCTION__ представляет имя текущей функции:

Константа __FUNCTION__. Листинг 3.20


<?php
function mySampleFunc() {
echo "Имя функции: " . __FUNCTION__;
}
mySampleFunc(); //Имя функции: mySampleFunc

__METHOD__ представляет имя текущего метода:

Константа __METHOD__. Листинг 3.21


<?php
class Sample
{
public static function myMethod() {
42
echo "Имя метода: " . __METHOD__;
}
}
Sample::myMethod(); // Имя метода: myMethod

__NAMESPACE__ представляет имя текущего пространства имен:

Константа __NAMESPACE__. Листинг 3.22


<?php
namespace MySampleNS;
echo "Пространство имен: " . __NAMESPACE__;
// Пространство имен: MySampleNS

Магические методы

Имена методов __construct(), __destruct(), __call(), __callStatic(), __get(),


__set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(),
__set_state(), __clone() и __debugInfo() зарезервированы для "магических"
методов в PHP. Не стоит называть свои методы этими именами, если вы не
хотите использовать их "магическую" функциональность.

PHP оставляет за собой право все методы, начинающиеся с __, считать


"магическими". Не рекомендуется использовать имена методов с __ в PHP,
если вы не желаете использовать соответствующий "магический"
функционал.

Конструктор

Метод с именем __construct() используется для построения объекта.

Использование конструктора. Листинг 3.23


class user{
public $user;
public function __construct($username){
$this->user = $username;
}
}

Теперь при создании объекта, нужно в конструктор передать значение


входящего параметра $username.
Если неконстантное значение должно присваиватся переменной при
создании объекта, оно присваивается в конструкторе (__construct) класса.
Использование втроенного конструтора. Листинг 3.24
$user = new user(‘Alex’);
43
Деструктор

__destruct()- магический метод, который вызывается, когда объект


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

Использование деструктора. Листинг 3.25


<?php
class MySample
{
public function __destruct() {
echo__CLASS__ . " вызвал деструктор.";
}
}
$obj = new MySample; // MySample вызвал деструктор

Методы __call(), __callStatic()

Иногда возникает необходимость либо выполнить какой-то код при


отсутствии нужного нам метода, либо узнать какой метод пытались вызвать,
либо использовать другое API для вызова нужного нам метода. С этой целью
и существуют методы __call() и __callStatic() — они перехватывают
обращение к несуществующему методу в контексте объекта и в статическом
контексте, соответственно.

Каждый из этих волшебных методов принимает два параметра: первый —


имя метода, который мы пытаемся вызвать, второй — список, содержащий
параметры вызываемого метода. Ключ — номер параметра вызываемого
метода, значение — собственно сам параметр:

Методы __call() и __callStatic(). Листинг 3.26


<?php
class OurClass
{
public function __call($name,array $params)
{
echo 'Вы хотели вызвать $Object->'.$name.', но его не
существует, и сейчас выполняется '.__METHOD__.'()<br>'
.PHP_EOL;
return;
}

public static function __callStatic($name,array $params)


{
echo 'Вы хотели вызвать '.__CLASS__.'::'.$name.', но
его не существует, и сейчас выполняется '.__METHOD__.'()';
return;
}
44
}

$Object=new OurClass;
#Вы хотели вызвать $Object->DynamicMethod, но его не существует, и
сейчас выполняется OurClass::__call()
$Object->DynamicMethod();
#Вы хотели вызвать OurClass::StaticMethod, но его не существует, и
сейчас выполняется OurClass::__callStatic()
OurClass::StaticMethod();
?>

Этот прием лучше всего использовать при создании объектно-реляционных


отношений (ORM). Предположим, мы хотим, чтобы приложение могло
возвращать пользователя по различным критериям поиска: id, email, phone,
login и т.д. Для этого можно создать по одному методу на каждый критерий.
Однако, код этих методов получится в основном идентичен. Для решения
такой задачи воспользуемся магическим методом __callStatic()
Реализация ORM-отношений с использованием метода __callStatic(). Листинг
3.27
class Users{
static function find($args){
// Здесь реализуется логика запроса:
// SELECT * FROM users WHERE $args[‘field’] = $args[‘value’]
}
static function __callStatic($method, $args){
if(preg_match(‘/^findBy(.+)$/’, $method, $matches)){
return static::find(array(‘field’=>$matches[1],
‘value’=>$args[0]));
}
}
}
$user = User::findById($id);
$user = User::findByEmail($email)

При вызове findById() PHP передает запрос __callStatic(). Внутри метода


результат ищет запросы, начинающиеся с findBy, и извлекает остальные
символы. Полученное значение и аргумент функции передается методу
Users::find(), который, используя полученные данные, выполняет запрос.
Строковое представление объекта
Метод __toString() определяет строковое представление объекта.
Реализация метода __toString(). Листинг 3.28
class Person{
public function __toString(){
return $this->message;
}
}

Копирование и клонирование объектов


Чтобы присвоить один объект другому, можно воспользоваться оператором
=.
45
Присвоение. Листинг 3.29
$tom = new user;
$tom->load_info(‘tom’);
$jhon = $tom

Таким образом, модификация одного объекта приводит к изменению


другого.
Копирование объектов по назначению осуществляется ключевым словом
clone. При этом создается незвивисмый объект с тем же содержимым
(методы и свойства). Изменение одного объекта не приводит к изменению
другого.
Клонирование объектов. Листинг 3.30
$ra = clone $zevs;

Для управления процессом клонирования можно использовать волшебный


метод __clone().
Если метод __clone() не существует, PHP автоматически предоставляет
поверхностную копию переменной, хранящейся в $this.
Управление процессом клонирования. Листинг 3.31
class Person{
public function __clone(){
$this->name = clone $this->name;
}
}

Переопределение обращений к свойствам


Для перехвата обращений к свойствам используются методы __get() и __set().
Предположим, класc User хранит переменные в массиве $data.
Использование геттеров и сеттеров. Листинг 3.32
class User{
private $data = array();
public function __get($prop){
if(isset($this->data[$prop])){
return $this->data[$prop];
}else{
return false;
}
}
public function __set($prop, $value){
$this->data[$prop] = $value;
}
}

Применение этих методов и массива для хранения данных упрощает


инкопсуляцию данных объекта. Вместо того, чтобы писать пару методов
доступа для каждого свойства класса, достаточно прописать методы __set() и
46
__get(). Дело в том, что операции чтения и записи не выполняются с
переменными напрямую, а проходят через методы доступа.
Пример использования:
Определение свойств через объект. Листинг 3.33
$jhon = new User;
$jhon->email = ‘jhon@mail.com’;
echo $jhon->email;

Кроме сокращения количества методов, специальные методы __set() и __get()


упрощают реализацию централизованной проверки входных и выходных
данных. Так можно ограничить использование свойств класса зараниее
определенным списком:
Ограничение свойств класса. Листинг 3.34
class User{
private $data = array(‘email’=>false, ‘login’=>false);
public function __get($prop){
if(isset($this->data[$prop])){
return $this->data[$prop];
}else{
return false;
}
}
public function __set($prop, $value){
if(isset($this->data[$prop])){
$this->data[$prop] = $value;
}else{
return false;
}
}
}

Сериализация

Объект, как и массив, можно сохранить в файле, базе данных, а также


передать в другой php-файл или web-службу для дальнейшего
использования. Для этого можно воспользоваться функциями serialize() и
unserialize().

Использование функций serialize() и unserialize() для сохранения данных


объекта. Листинг 3.35
// classa.inc:

class A {
public $one = 1;

public function show_one() {


echo $this->one;
}
}

// page1.php:
47

include("classa.inc");

$a = new A;
$s = serialize($a);
// сохраняем $s где-нибудь, откуда page2.php сможет его получить.
file_put_contents('store', $s);

// page2.php:

// это нужно для того, чтобы функция unserialize работала


правильно.
include("classa.inc");

$s = file_get_contents('store');
$a = unserialize($s);

// теперь можно использовать метод show_one() объекта $a.


$a->show_one();

Процессом сериализации и десериализации объекта можно управлять с


помощью специальных методов __sleep() и __wakeUp().
Функция serialize() проверяет наличие метода с магическим именем __sleep().
С другой стороны, функция unserialize() проверяет наличие метода с
магическим именем __wakeup(). Если такой имеется, то он может воссоздать
все ресурсы объекта, принадлежавшие ему.
Управление процессом сериализации объекта. Листинг 3.36
class Connection
{
protected $link;
private $dsn, $username, $password;

public function __construct($dsn, $username, $password)


{
$this->dsn = $dsn;
$this->username = $username;
$this->password = $password;
$this->connect();
}

private function connect()


{
$this->link = new PDO($this->dsn, $this->username, $this-
>password);
}

public function __sleep()


{
return array('dsn', 'username', 'password');
}

public function __wakeup()


{
$this->connect();
}
48
Интерфейсы
Результат сходного поведения в разных классах
Определение интерфейса . Листинг 3.37
interface NameInterface{
public function getName();
public function setName();
}
class Book implements NameInterface{
private $name;
public function getName(){
return $this->name;
}
public function setName($name){
return $this->name = $name;
}
}

Если нужно проверить, реализует ли класс конкретный интерфейс, можно


воспользоваться функцией class_implements().
Функция class_implements(). Листинг 3.38
class Book implemets NameInterface{ }

$interface = class_implements('Book');
if(isset($interface['NameInterface'])){
// Book реализует инрефейс NameINterface
}

Так же возможно воспользоваться классами Reflection

Использование класса Reflection. Листинг 3.39


class Book implemets NameInterface{ }

$rc = new ReflectionClass('Book');


if($rc->implementsInterface('NameInteface')){
// Book реализует интерфейс
}

Типаж (trait)

Если вы захотите включить код, реализующий интерфейс в другой класс,


определите типаж (trait) и в другом классе объявите его использование.
Применяются для совместного использования кода.

Trait. Листинг 3.40


trait NameTrait{
private $name;
public function getName(){

}
public function setName(){
return $this→name = $name;
49
}
}

class Book{
use NameTrait;
}
class Picture{
use NameTrait;
}
$book = new Book;
$book→setName('Практика разработки сайтов');
echo $book->getName();

Типаж из листинга определяет и реализует два метода, необходимые для


присваивания имени объекту. В классах Book и Picture будут доступны
методы из типажа.
Сочетания интерфейсов и типажей
Класс может реализовывать несколько интерфейсов и типажей. Совместное
использование типажей и интерфейсов открывает большие возможности по
реализации структуры проекта.
Совместное использование типажей и интерфейсов. Листинг 3.41
class Book implements NameInteface, PictureInterface{
use NameTrait, PictureTrait;
}

Абстрактные классы и методы

Абстрактным называется класс, от которого нельзя создать объекты.

Абстрактный класс. Листинг 3.42


abstract class Database{
abstract public function connect(){}
}

Абстрактные классы должны содержать, как минимум, один метод с


ключевым словом abstract(). Другие методы могут быть и не абстрактными.
Если класс содержит абстрактный метод, то и сам класс должен быть
объявлен абстрактным.
Абстрактные методы реализуются не внутри абстрактного класса, а внутри
произвольного класса, расширяющего абстрактного родителя.
Если произвольный класс не реализует все абстрактные методы
родительского класса, он тоже является абстрактным. Поэтому другой класс
должен осуществить дальнейшее субклассирование.
50
К абстрактным методам предъявляются следующие требования: они не могут
быть закрытыми (private), потому что они должны использоваться при
наследовании; абстрактные методы не могут использоваться совместно с
ключевым словом final, потому что они должны переопределяться.
Окончательные (final) методы и классы
PHP 5 представляет ключевое слово final, разместив которое перед
объявлениями методов класса, можно предотвратить их переопределение в
дочерних классах. Если же сам класс определяется с этим ключевым словом,
то он не сможет быть унаследован.
Пример использования окончательного метода. Листинг 3.43
class BaseClass {
public function test() {
echo "Вызван метод BaseClass::test()\n";
}

final public function moreTesting() {


echo "Вызван метод BaseClass::moreTesting()\n";
}
}

Попытка переопределения метода moreTesting() закончится фатальной


ошибкой: метод BaseClass::moretesting() не может быть переопределён.
Окончательным может быть также класс:
Пример использования окончательного класса. Листинг 3.44
final class BaseClass {
public function test() {
echo "Вызван метод BaseClass::test()\n";
}

// В данном случае неважно, укажете ли вы этот метод как final


или нет
final public function moreTesting() {
echo "BaseClass::moreTesting() called\n";
}
}

Попытка наследования от этого класса закончится фатальной ошибкой: класс


ChildClass не может быть унаследован от окончательного класса (BaseClass).
Вызов метода при создании объекта
Возможен вызов метода или обращение к свойству непосредственно при
создании объекта.
Вызов метода или свойства при создании объекта. Листинг 3.45
$str = (new Article(‘Alex’))->last_visit;
$func = (new Article(‘Alex’))->getlast();
51
Множественный вызов методов объектом
В PHP возможен последовательный вызов методов объекта. Чтобы цепочки
вызовов работали, следует возвращать $this из каждого сцепляемого метода.
Класс, реализующий цепочку методов. Листинг 3.46
class Mail {
protected $data;
public function from($from){
$data[‘from’] = $from;
return $this;
}
public function send(){
print_r($this->data);
return true;
}
}

Пример использования:
Объект множественного вызова методов. Листинг 3.47
$kolya = new Mail;
$kolya->from(‘kolya@mail.com’)->send();

Интроспекция

Чтобы получить краткую сводку о методах и свойствах класса, можно


воспользоваться встроенным классом Reflection.

Краткая сводка методов и свойств класса. Листинг 3.48


Reflection::export(new ReflectionClass(‘car’));

Проверить конкретные компоненты класса можно через объект


ReflectionClass().
Проверка метода на существование. Листинг 3.49
$car = new ReflectionClass(‘car’);
if($car->hasMethod(‘refractTop’)){
// содержит метод refractTop
}

Чтобы проверить объект на принадлежность к определенному классу, можно


воспользоваться оператором instanceof.
Проверка объекта на принадлежность к классу. Листинг 3.50
if($media instanceof Book){
//$media является объектом класса Book.
}

Если полное имя класса, объект которого необходимо создать, неизвестно, то


можно воспользоваться функцией class_exists() для проверки класса на
существование.
Проверка класса на существование. Листинг 3.51
52
if(class_exists($class)){
//$class – класс с таким именем существует.
}
53

4. PDO

PHP Data Object – это класс подключения и взаимодействия с базой данных.


Рассмотрим его в действии.

Создадим файл config.php, в котором будем хранить константы подключения


к базе данных.

Файл с настройками подключения к базе данных. Листинг 4.1


// Define configuration
define("DB_HOST", "localhost");
define("DB_USER", "root");
define("DB_PASS", "");
define("DB_NAME", "database");

Далее создадим файл database.php со следующим классом

Класс Database. Листинг 4.2


class Database{
private $host = DB_HOST;
private $user = DB_USER;
private $pass = DB_PASS;
private $dbname = DB_NAME;

private $dbh;
private $error;

public function __construct(){


// Set DSN
$dsn = 'mysql:host=' . $this->host . ';dbname=' . $this-
>dbname;
// Set options
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
// Create a new PDO instanace
try{
$this->dbh = new PDO($dsn, $this->user, $this->pass,
$options);
}
// Catch any errors
catch(PDOException $e){
$this->error = $e->getMessage();
}
}
}

Далее необходимо сделать метод, выполняющий query-запросы. Но сперва


создадим свойство $stmt, которое будет держать запросы.
54
Свойство $stmt. Листинг 4.3
private $stmt;

Используем метод prepare, чтобы обезопасить данные от sql-инъекции.

Защита от sql-инъекций с использованием метода prepare(). Листинг 4.4


public function query($query){
$this->stmt = $this->dbh->prepare($query);
}

Добавим метод bind, который к запросу будет добавлять данные.

Метод bind(). Листинг 4.5


public function bind($param, $value, $type = null){
if (is_null($type)) {
switch (true) {
case is_int($value):
$type = PDO::PARAM_INT;
break;
case is_bool($value):
$type = PDO::PARAM_BOOL;
break;
case is_null($value):
$type = PDO::PARAM_NULL;
break;
default:
$type = PDO::PARAM_STR;
}
}
$this->stmt->bindValue($param, $value, $type);
}

И наконец, метод execute.

Метод execute. Листинг 4.6


public function execute(){
return $this->stmt->execute();
}

Для выдачи результатов создадим свои методы.

Методы извлечения данных. Листинг 4.7


//извлечь всё
public function resultset(){
$this->execute();
return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
}

// извлечь один параметр


public function single(){
$this->execute();
return $this->stmt->fetch(PDO::FETCH_ASSOC);
55
}

//извлечь кол-во строк


public function rowCount(){
return $this->stmt->rowCount();
}

// узнать id
public function lastInsertId(){
return $this->dbh->lastInsertId();
}

Для отладки ошибок создадим метод debugDumpParams.

Метод debugDumpParams. Листинг 4.8


public function debugDumpParams(){
return $this->stmt->debugDumpParams();
}

Рассмотрим пример вставки данных.

Вставка данных. Листинг 4.9


$database = new Database();

$database->query('INSERT INTO mytable (FName, LName, Age, Gender)


VALUES (:fname, :lname, :age, :gender)');

$database->bind(':fname', 'John');
$database->bind(':lname', 'Smith');
$database->bind(':age', '24');
$database->bind(':gender', 'male');

$database->execute();

Еще пример извлечения данных.

Извлечение данных. Листинг 4.10


$database = new Database();

$database->query(“SELECT * FROM users WHERE id = $id”);

$database->single();


56

V. Laravel
1. Перед установкой

Изучать новый незнакомый фреймворк - нелегкое занятие, но учить новое


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

 Изучаем вспомогательные технологии. Если это php-фрэймворк,


предварительно нужно изучить PHP и ООП. Фрэймворк использует
HMVC, поэтому необходимо понимать основные принципы
использования данного паттерна. Устанавливается через Composer
(уметь работать с этим менеджером зависимостей).
 Установка и Настройка
 Routing или маршрутизация
 Controller
 Views или шаблонизация
 Requests & Input
 Responses
 База данных, взаимодействие с базой данных
 Модели

После изучения этих документов вы будете иметь представление о том, как


во фреймворке происходит обработка цикла запроса, а также создать простой
сайт-визитку.

Затем можно почитать про настройку соединения и построение запросов к


базе данных, а также про встроенный Eloquent ORM, облегчающий работу с
БД.

Затем, когда вы получите знания основ и приобретете некоторый опыт


использования фрэймворка, можно приступать к углубленному изучению:
использование и разработка модулей, библиотек, хелперов, работа с
готовыми ресурсами Laravel, разработка realtime-приложений и др.

Важно, чтобы изучение теории сопровождалось с практикой. Фрэймворк


Laravel позволяет быстро, а, главное, грамотно создать web-приложение
любой сложности (от сайта-визитки до порталов, чатов, магазинов…).
57
Философия Laravel

Laravel - фреймворк для построения веб-приложений с выразительным и


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

Laravel - попытка сгладить все острые и неприятные моменты в работе php-


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

Laravel стремится сделать процесс разработки приятным для разработчика


без ущерба для функциональности приложений. Для этого мы попытались
объединить все самое лучшее из того, что мы видели в других фреймворках, -
RubyOnRails, ASP.NET и Синатра, Kohana, Yii. Превосходный IoCcontainer,
встроенные миграции и интегрированная поддержка юнит-тестов дают вам
мощные инструменты для того, чтобы сделать именно тот функционал,
который вам нужен.

Требования к установке

Официальный сайт laravel – http://laravel.com

У Laravel всего несколько требований к вашему серверу:

PHP >= 5.5

Mcrypt PHP Extension

OpenSSL PHP Extension

MbstringPHPExtension

В некоторых операционных системах может понадобиться ручная установка


PHP JSON extension.
58

2. Установка Laravel

Laravel не установится без менеджера зависимостей Composer, поэтому


сперва рассмотрим Composer.

Composer

Laravel использует менеджер зависимостей Сomposer. Если в качестве


сервера мы используем OpenServer, то composer поставлен совместно с
пакетомпрограмм OpenServer-а.Отдельно его скачивать и устанавливать не
нужно.

Прежде чем устанавливать Laravel, давайте убедимся, что Composer работает.


Для этого запустим встроенную консоль OpenServer-а.
59

В консоли наберем команду “Composer”:

Команда composer. Листинг 2.1


Composer

Такой ответ консоли свидетельствует о том, что Composer установлен и готов


к работе.
60

Если с момента последнего запуска composer прошло более 30 дней, то


необходимо обновить composer.

Обновление composer. Листинг 2.2


Composer selfupdate

Установка Laravel через консоль

Через встроенную консоль OpenServer-а перейдем в корневую дирректорию


сервера либо в ту дирректорию, в которой хотим создать папку с проектом.

Выполним следующую команду в консоли:

Установка laravel. Листинг 2.3


Composer create-project laravel/laravel -–prefer-dist

Либо эту же команду, но с именем папки проекта

Установка laravel в определенную папку. Листинг 2.4


Composer create-project laravel/laravel your-project-name --prefer-
dist
61
Composer создаст папку laravel, куда установится проект laravel, со
следующей структурой:

Установка через скачивание

Сперва необходимо скачать сам архив по ссылке


https://github.com/laravel/laravel/archive/master.zip и извлечь архив в папку на
сервере. Дальше в корне приложения выполнить команду composer-а для
установки всех зависимостей библиотеки.

Установка зависимостей. Листинг 2.5


composer install

Запуск проекта

Перейдем в папку с проектом: laraverl/public


62

3. Структура и жизненный цикл фрэймворка

Все рабочие файлы, за исключением файлов шаблонов, живут в папке app/.

app/

Главная рабочая папка всего фрэймворка. По умолчанию, это папка в


пространстве имен App. Пространство имен можно изменить, используя
artisan команду app:name.

Изменение пространства имен с помощью команды artisan. Листинг 3.1


php artisan app:name SocialNet

Папка app содержит папки Console, Events, Exceptions, Http, Jobs, Listeners,
Providers.

Каталог Console содержит artisan-команды.

Каталог Events предназначен для классов событий. События могут быть


использованы для того, чтобы определить, что какое-то действие произошло.

Каталог Exceptions содержит классы обработчики исключений.

Каталог Http содержит все фильтры, контроллеры, а также запросы.

Каталог Jobs содержит файлы, управляющие синхронностью загрузки


приложения.

Каталог Listeners содержит классы прослушивателей событий. Слушатели


тесно связаны с событиями. Например, событие UserRegistered должно быть
обработано слушателем в SendWelcomeEmail.

Каталог Providers содержит сервис-провайдеры приложения.

bootstrap/

Папка для конфигурационных файлов автозагрузки.

config/

Папка конфигурационных файлов.


63
database/

В папке database находятся папка migrations (для файлов миграций баз


данных), seeds (предворительные данные таблиц базы данных) и factories
(настройки для моделей).

public/

Корневая папка проекта. В этой папке находится файл index.php и .htaccess,


которые загружаются первыми, а также папкимедиафайлов (css, js,
изображения и др.)

resources/

Папка для шаблонов.

storage/

Папка для хранения временных файлов, создаваемых фрэймворком.

Папки внутри storage должны быть доступны веб-серверу для записи. Если
вы устанавливаете фреймворк на Linux или MacOS, открыть папки на запись
можно командой chmod -R 777 storage.

tests/

Папка содержит файлы автоматических тестов.

vendor/

Папка содержит composer – зависимости.

Жизненный цикл фрэймворка

Точка входа для всех запросов фрэймворка начинается с файла index.php.


Данный файл не содержит много кода. Основная его задача – загрузка
необходимых файлов: autoload.php и app.php. А также дальнейшее
перенаправление запроса.

Далее входящий запрос, в зависимости от типа запроса, отправляется либо в


модуль, обрабатывающий консольные команды (консольное ядро), либо в
ядро фрэймворка, обрабатывающее http-запросы.
64
Сосредоточимся на ядре HTTP, который находится по адресу
app/Http/Kernel.php. Данный файл является расширением класса
Illuminate\Foundation\Http\Kernel (ядра фрэймворка). Ядро фрэймворка можно
представить в виде большого черного ящика, который обрабатывает запросы,
а также определяет перечень промежуточного программного обеспечения
(специальных классов наследуемых от класса middleware), которые должен
пройти фрэймворк прежде чем выдаст ответ.

Далее загружаются поставшики услуг для приложения (servise providers).


Все поставщики услуг по применению настроены в массиве провайдеров
конфигурационного файла config/app.php. Далее метод register() определяет
необхоимые классы поставщиков услуг. Если некоторые поставщики услуг
необходимо запустить сразу, то это можно выполнить в методе boot().

Сервис-провайдеры несут ответственность за все компоненты по


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

И только после того, как все поставщики услуг были зарегистированы (или
сразу выполнены методом boot()), запрос попадает в маршрутизатор.
Маршрутизатор, как правило, отправляет запрос в какой-либо из
контроллеров.

На этом запрос обработан, и в контроллере должен быть прописан ответ


фрэймворка. Кстати, если ответ ожидается коротким, то можно обойтись без
контроллера: ответы фрэймворка можно писать сразу в маршрутизаторе.

Есть много отклонений и различных вариантов путей запроса, но неизменно


путь проходит три опорные точки, на которые надо обратить внимание:

1. Роуты - app/routes.php

2. Контроллеры - app/controllers/

3. Отображения (виды) - app/views/


65
66

4. Удаление public из url проекта

Как правило, на рабочем проекте необходимо избавиться от папки public,


которая должна стать корневой папкой вашего проекта. Это можно сделать
несколькими способами.

1 способ

Чтобы к laravel-проекту можно было обращаться без вызова папки public,


необходимо сделать следующие действия:

1. Создать в корневой дирректории проекта папку local (или с любым


другим именем).
2. Перенести все файлы и папки проекта, за исключением папки public, в
созданную папку local. Содержимое папки local получится примерно
таким:

3. Содержимое папки public перенести в корень проекта. После чего саму


папку public можно удалить. Корневая дирректория проекта будет
выглядеть примерно так:
67

4. Сейчас необходимо изменить несколько путей. В корневом файле


index.php изменим пути до файлов autoload.php и start.php.

Новые пути до файлов autoload.phpи start.php. Файл index.php. Листинг 4.1


require __DIR__.'/local/bootstrap/autoload.php';

$app = require_once __DIR__.'/local/bootstrap/start.php';

2 способ

Если вы пользуетесь OpenServer-ом, то можно в настройках открыть вкладку


domens и там связать любое имя домена с путем до папки public laravel-
проекта.
68

3 способ

В корне проекта создать файл .htaccess со следующим содержимым.

Перенаправление в папку public, файл .htaccess. Листинг 4.2


<IfModule mod_rewrite.c>
RewriteEngine On

RewriteRule ^(.*)$ public/$1 [L]


</IfModule>

Для обновление publish путей найдем файл application.php, в котором есть


метод publicPath (из папки vendor), и обновим его:

Обновление publish_path. Листинг 4.3


public function publicPath()
{
return $this->basePath.DIRECTORY_SEPARATOR.’../’;
}
69

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

Запрос из адресной строки попадает в так называемый обработчик


маршрутов, или маршрутизатор, или роутер (routes). Маршрутизатор
определяет, какой контроллер необходимо вызывать.

Маршруты определяются в файле app/routes.php

Простейший get-маршрут. Листинг 5.1


Route::get('/', function () {
return 'Hello World';
});

Для перехвата POST-данных можно воспользоваться методомRoute::post

Простейший post-маршрут. Листинг 5.2


Route::post('foo/bar', function () {
return 'Hello World';
});

Метод Route::any перехватывает и POSTи GET данные.

Маршрут любого http-запроса. Листинг 5.3


Route::any('foo', function () {
return 'Hello World';
});

Для перехвата маршрутов только по протоколу HTTPS, вторым входящим


параметром можно передать не функцию, а массив, первым элементом
которого является тип протокола, а вторым – функция.

Маршрут любого https-запроса. Листинг 5.4


Route::get('foo', array('https', function() {
return 'Must be over HTTPS';
}));

Для генерации URL к какому-нибудь маршруту можно воспользоваться


методом URL::to(). Это может пригодится для генерации путей к ссылкам
(значение атрибута href), картинкам (src), стилям (href), скриптам(src),
обработчику форм (action) либо при переадресации.

Генерация URL к маршруту. Листинг 5.5


$url = URL::to('foo');

Для этих же целей можно использовать хелпер url

Использование хелпера url. Листинг 5.6


70
$url = url('foo');

Параметры маршрутов

Добавление к маршруту обязательного параметра id.

Добавление к маршруту обязательного параметра id. Листинг 5.7


Route::get('user/{id}', function ($id) {
return 'User '.$id;
});

Добавление к маршруту необязательного параметра name.

Добавление к маршруту необязательного параметра name. Листинг 5.8


Route::get('user/{name?}', function ($name = null) {
return $name;
});

Вместо $name = nullможно использовать любое значение по умолчанию.

Добавление к маршруту необязательного параметра name. Листинг 5.9


Route::get('user/{name?}', function ($name =’Jhon’) {
return $name;
});

Использование регулярных выражений в маршрутах.

Маршруты с соответствием пути регулярному выражению. Листинг 5.10


Route::get('user/{name}', function ($name) {
})->where('name', '[A-Za-z]+');

Route::get('user/{id}', function ($id) {


})->where('id', '[0-9]+');

Вместо последовательного вызова метода where, можно передать массив


ограничений.

Использование массива в регулярных вырражениях. Листинг 5.11


Route::get('user/{id}/{name}', function ($name) {
})->where(['name'=>'[A-Za-z]+', ‘id’=>'[0-9]+']);

Если какие-то регулярные выражения нужно связать со всеми параметрами,


то можно использовать метод pattern:

Использование шаблона регулярного выражения. Листинг 5.12


public function boot(Router $router)
{
$router->pattern('id', '[0-9]+');
71
parent::boot($router);
}

Таким образом, будет осуществляться проверка всех параметров с именем id.

Именованные маршруты

Задать имя маршруту можно следующим способом:

Назначение имени текущего исполняемого маршрута. Листинг 5.13


Route::get('user/profile', array('as' => 'profile', function () {
//
}));

Также можно задать контроллер и его экшн, который будет выполняется по


данному маршруту.

Назначение контроллера и экшна. Листинг 5.14


Route::get('user/profile', array('as' => 'profile',
'uses' => 'UserController@showProfile'));

Теперь можно использовать имя маршрута при генерации URL либо при
перенаправлении.

Генерация URL. Листинг 5.15


$url = URL::route('profile');
$redirect = Redirect::route('profile');

Получить имя текущего выполняемого маршрута можно методом


currentRouteName():

Получить имя текущего исполняемого маршрута. Листинг 5.16


$name = Route::currentRouteName();

Кроме того, существуют два волшебных метода controller и controllers.

Использование методов controllerи controllers. Листинг 5.17


Route::controllers([
'cabinet' => 'CabinetController',
'user' => 'UserController',
]);
Route::controller(‘auth’=>’AuthController’);

Обратите внимание на то, что мы не указываем, какой экшн будет


использоваться, и не указываем имена передаваемых параметров. По
умолчанию используется экшн getIndex (для get-запросов) и postIndex (для
post-запросов). Т.е. в зависимости от типа запроса, один из этих экшнов
должен присутствовать в контроллере.
72
Вызываемый экшн также может принимать параметр $id.

Регистрация всех экшнов контроллера. Листинг 1.


Route::controller(Controller::detect());
73

6. Artisan

Artisan - название интерфейса командной строки, входящей в состав Laravel.


Он предоставляет полезные команды для использования во время разработки
вашего приложения. Работает на основе мощного компонента
SymfonyConsole.

Чтобы вывести все доступные команды Artisan, используйте list команду:

Вывод всех команд Artisan. Листинг 6.1


php artisan list

Доступные команды:

make:command — создаёт новый класс команды

make:console — создаёт новую команду Artisan

make:controller — создаёт новый класс контроллера ресурса

make:event — создаёт новый класс события

make:middleware — создаёт новый класс промежуточного ПО

make:migration — создаёт новый файл миграции

make:model — создаёт новый класс модели Eloquent и миграцию

make:provider — создаёт новый класс поставщика услуг

make:request — создаёт новый класс запроса формы

event:generate — генерирует пропущенные события и обработчики

Каждая команда также включает и инструкцию, которая отображает и


описывает доступные аргументы и опции для команды. Чтобы её вывести,
необходимо добавить слово help перед командой:

Просмотр подсказок для текущей команды. Листинг 6.2


php artisan help migrate

Вы также можете указать среду, в которой будет выполнена команда при


помощи опции --env:
74
Использование среды. Листинг 6.3
php artisan migrate --env=local

Чтобы определить текущую версию Laravel, можно воспользоваться опцией--


version

Определение текущей версии Laravel. Листинг 6.4


php artisan --version

Разработка artisan-команд

В дополнение к командам, предоставляемых Artisan-ом, вы также можете


создавать свои собственные команды для работы с вашим приложением.
Свои команды можно хранить как в директории app/Console, так и
самостоятельно выбирать место для хранения, прежде убедившись, что
команды будут автоматически загружены, основываясь на настройках
composer.json.

Для создания новой команды вы можете воспользоваться командой Artisan-а


make:console, которая сгенерирует макет класса:

Создание нового класса команды. Листинг 6.5


php artisan make:console FooCommand

Команда выше сгенерирует класс app/Console/FooCommand.php.

Создавая команду, опция --command может быть использована для


назначения имени команды в консоли:

Использование опции --command. Листинг 6.6


php artisan make:console AssignUsers --command=users:assign

Как только команда будет сгенерирована, необходимо заполнить свойства


класса name и description, которые будут использованы при отображении
команды в списке.

Метод fire будет вызван как только команда будет запущена. Вы можете
поместить в этот метод любую логику.

В методах getArguments и getOptions можно определить любые аргументы


или опции, которые будет принимать команда. Оба этих метода возвращают
массив команд, описываемых списком полей массива.
75
Массив, определяющий аргументы, выглядит так:

Массив определяющий аргументы. Листинг 6.7


array($name, $mode, $description, $defaultValue)

Аргумент mode может принимать одно из следующих значений:


InputArgument::REQUIRED (обязательный) или InputArgument::OPTIONAL
(необязательный).

Массив, определяющий опции, выглядит следующим образом:

Массив, определяющий аргументы. Листинг 6.8


array($name, $shortcut, $mode, $description, $defaultValue)

Для опций аргумент mode может быть: InputOption::VALUE_REQUIRED


(значение обязательно), InputOption::VALUE_OPTIONAL (значение
необязательно), InputOption::VALUE_IS_ARRAY (значение - это массив),
InputOption::VALUE_NONE (нет значения).

Режим VALUE_IS_ARRAY обозначает, что этот переключатель может быть


использован несколько раз при вызове команды:

Вызов команды с использованием режима VALUE_IS_ARRAY. Листинг 6.9


php artisan foo --option=bar --option=baz

Значение VALUE_NONE означает, что опция просто используется как


"переключатель":

Вызов команды с использованием опции переключателя. Листинг 6.10


php artisan foo --option

Во время выполнения команды, очевидно, потребуется получать значения


переданных аргументов и опций. Для этого можно воспользоваться методами
argument и option:

Вывод переданных значений аргументов и опций. Листинг 6.11


//Получение значения аргумента команды
$value = $this->argument('name');
//Получение всех аргументов
$arguments = $this->argument();
//Получение значения опции команды
$value = $this->option('name');
//Получениевсехопций
$options = $this->option();
76
Для вывода данных в консоль вы можете использовать методы info
(информация), comment (комментарий), question (вопрос) и error (ошибка).
Каждый из этих методов будет использовать цвет по стандарту ANSI,
соответствующий смыслу метода.

Вывод команды. Листинг 6.12


//Выводинформациивконсоль
$this->info('Display this on the screen');
//Вывод сообщений об ошибке в консоль
$this->error('Something went wrong!');

Для обеспечения пользовательского ввода, можно воспользоваться методами


ask иconfirm.

Пользовательский ввод. Листинг 6.13


//Попросить пользователя ввести данные:
$name = $this->ask('What is your name?');
//Попросить пользователя ввести секретные данные:
$password = $this->secret('Whatisthepassword?');
Попросить пользователя подтвердить что-то:
if ($this->confirm('Doyouwishtocontinue? [yes|no]'))
{
//
}

Также можно указать ответ по умолчанию для метода confirm. Это должно
быть true или false:

Ответ по умолчанию для метода confirm. Листинг 6.14


$this->confirm($question, true);

Как только команда будет готова, нужно зарегистрировать её в Artisan-е,


чтобы она была доступна для использования. Обычно это делается в файле
app/Providers/ArtisanServiceProvider.php. В этом же файле команду можно
зарегестрировать в контейнере IoC. Для регистрации можно воспользоваться
методом commands. По умолчанию, образец регистрации команды включен в
сервис-провайдер. Например:

Пример регистрации команды. Листинг 6.15


$this->app->bindShared('commands.inspire', function()
{
return new InspireCommand;
});

Как только команда зарегистрирована в IoC контейнере, вы можете


воспользоваться методом commands в вашем сервис-провайдере, чтобы
сделать её доступной в Artisan-е. Вам необходимо передать название,
использованное при регистрации команды в IoC контейнере:
77
Передача названия, использованное при регистрации команды. Листинг 6.16
$this->commands('commands.inspire');

Иногда может потребоваться вызвать другую команду из вашей команды.


Это можно сделать, используя метод call:

Вызов другой команды из текущей команды. Листинг 6.17


$this->call('command:name', array('argument' => 'foo', '--option'
=> 'bar'));
78

7. Конфигурирование

Основные файлы настроек хранятся в app/config/. Каждой группе настроек


отведен свой файл либо папка.

После установки фреймворка, можно придумать имя своему будущему


приложению. Это имя будет фигурировать в качестве корневого имени в
неймспейсе классов, которые будут использоваться в вашем приложении. По
умолчанию, ваше приложение находится в папке app, имеет имя App и
автозагружается при помощи Composer, согласно стандарту PSR-4. Вы
можете изменить его при помощи artisan-команды app:name.

Например, чтобы изменить имя приложения на Horsefly, выполните эту


команду в корне установленного фреймворка:

Определение имени приложению. Листинг 7.1


php artisan app:name Horsefly

Laravel "из коробки" практически не требует дополнительного


конфигурирования - вы можете сразу начать писать код.

Максимум, вам может быть нужно изменить настройки доступа к базе


данных в config/database.php и, возможно, изменить параметры timezone и
locale в config/app.php.

Далее, вам нужно определиться с названием среды выполнения, в которой


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

Поэтому вы определяете, что на данной машине у вас среда выполнения local


и в config/local/app.php ставите параметр debug в true.

Примечание: Никогда не ставьте app.debug в true в продакшне, т.е. на


хостинге.

Права на запись

Папки внутри storage должны быть доступны веб-серверу для записи. Если
вы устанавливаете фреймворк на Linux или MacOs - открыть папки на запись
можно командой chmod -R 777 storage

Пути
79
Расположение некоторых папок, используемых фреймворком, можно
изменить. Пути до таких папок задаются в bootstrap/paths.php.

Настройка среды выполнения (theenvironment)

Часто необходимо иметь разные значения для разных настроек в зависимости


от среды, в которой выполняется приложение. Например, вы можете захотеть
использовать разные драйвера кэша на локальном и производственном
(продакшн) серверах. Это легко достигается использованием настроек,
зависящих от среды.

Просто создайте новую папку внутри папки config, название которой


совпадает с именем вашей среды, таким как local. Затем создайте файлы
настроек, которые вы хотите переопределить и укажите в них значения для
этой среды.

Например, вы можете переопределить драйвер кэша для локальной среды,


создав файл cache.php внутри config/local с таким содержимым:

Переопределение драйвера кэша. Листинг 7.2


<?php
return [
'driver' => 'file',
];

Заметьте, что вам не нужно указывать каждую настройку, которая есть в


исходном конфигурационном файле, кроме настроек, которые вы хотите
переопределить. Настройки среды будут наложены на эти базовые файлы.
Теперь нам нужно сообщить Laravel, как определить, в какой среде он
работает. Начальная среда всегда production.

Однако, вы можете настроить и другие среды в файле


bootstrap/environment.php, который находится в корне установки Laravel. В
этом файле вы найдёте вызов метода $app->detectEnvironment. Функция,
которая ему передаётся, используется для определения текущей среды. В
этой функции вы можете определять среду выполнения по имени машины,
вызываемому домену или переменной окружения.

Определение среды выполнения по имени машины. Листинг 7.3


<?php
$env = $app->detectEnvironment(function()
{
return getenv('APP_ENV');
});

В этом примере название среды выполнения у нас находится в переменной


окружения APP_ENV. Занести туда значения вы можете в файле .htaccess,
правилах nginx или при помощи файла .env, который должен находиться в
80
корне фреймворка.

Получение текущей среды

Вы можете получить текущую среду с помощью метода environment объекта


Application:

Получение текущей среды. Листинг 7.4


$environment = $app->environment();

Передача аргументов в этот метод, чтобы проверить, совпадает ли среда с


переданным значением:

Использование параметров метода environment. Листинг 7.5


if ($app->environment('local'))
{
// Среда - local
}
if ($app->environment('local', 'staging'))
{
// Среда - local ИЛИ staging
}

Режим обслуживания

Когда ваше приложение находится в режиме обслуживания (maintenance


mode), специальный шаблон будет отображаться вместо всех ваших
маршрутов. Это позволяет "отключать" ваше приложение во время
обновления или обслуживания. Проверка на режим обслуживания уже
включена в стандартный фильтр App::before в файле
app/Http/Filters/MaintenanceFilter.php. Ответ от этой проверки будет
возвращен пользователю, когда приложение находится в режиме
обслуживания.

Для включения этого режима просто выполните команду Artisan-а down:

Режим включения обслуживания. Листинг 7.6


php artsan down

Чтобы выйти из режима обслуживания выполните команду up:

Режим включения обслуживания. Листинг 7.7


php artsan up

Настройка сервис-провайдеров
81
При использовании среды, вы, возможно, захотите "добавить"
дополнительные сервис-провайдеры к исходным в файле app. Однако, если
вы это сделаете, вы заметите, что сервис-провайдеры среды переопределяют
сервис-провайдеры в вашем исходном файле настроек app. Чтобы заставить
сервис-провайдеры не переопределять, а добавляться к существующим,
используйте вспомогательный метод append_config в вашем файле настроек
среды app:

Добавление сервис-провайдеров. Листинг 7.8


'providers' => append_config(array(
'LocalOnlyServiceProvider',
))

Язык

Язык по умолчанию устанавливается в файле app.php.

Язык можно изменить в любом файле проекта:

Изменение языка по умолчанию. Листинг 7.9


App::setLocale('es');

Вы также можете установить резервный язык локализации в случае, если для


основного языка нет вариантов перевода, будет браться строка из резервного
файла локализации. Обычно это английский язык, но вы можете это
поменять. Настройка находится в файле app/config/app.php:

Изменение резервного языка. Листинг 7.10


'fallback_locale' => 'en',

Получение строк из языкового файла.

Обращение к элементам массива языкового файла. Листинг 7.11


echo Lang::get('messages.welcome');

Языковые строки могут быть использованы при валидации данных из форм.


Подробнее об этом в теме Request-запросы.

E-mail

Laravel предоставляет простой интерфейс к популярной библиотеке


SwiftMailer. Главный файл настроек - app/config/mail.php - содержит
всевозможные параметры, позволяющие вам менять SMTP-сервер, порт,
логин, пароль, а также устанавливать глобальный адрес from для исходящих
82
сообщений. Вы можете использовать любой SMTP-сервер либо стандартную
функцию PHPmail - для этого установите параметр driver в значение mail.
Кроме того, доступен драйвер sendmail.

Отправка E-mail. Листинг 7.12


Mail::send('emails.welcome', $data, function($message)
{
$message->to('foo@example.com', 'Джон Смит')->subject('Привет!');
});
83

8. Middleware

HTTP Middleware (посредники) - это фильтры обработки HTTP-запроса. Так,


например, в Laravel включены middlewares для проверки аутентификации
пользователя. Если пользователь не залогинен, middleware перенаправляет
его на страницу логина. Если же залогинен - middleware не вмешивается в
прохождение запроса, передавая его дальше по цепочке middleware-
посредников к собственно приложению.

Middlewares можно использовать в качестве фильтров для роутов.

Конечно, проверка авторизации - не единственная задача, которую способны


выполнять middlewares. Это также добавление особых заголовков (например,
CORShttp-ответ вашего приложения) или логирование всех http-запросов.

В Laravel есть несколько дефолтных middleware, которые находятся в папке


app/Http/Middleware. Это middlewares для реализации режима обслуживания
сайта ("сайт временно не работает, зайдите позже"), проверки авторизации,
CSRF-защиты и т.п.

Создание middleware

Для создания middleware можно воспользоваться командой make:middleware:

Создание middleware. Листинг 8.1


php artisan make:middleware Admin

Давайте создадим middlewareAdmin, который будет пропускать только


админов, у которых в таблице users в базе данных выставлен параметр
isAdmin = 1.

MiddlewareAdmin сделаем по примеру Authenticate.

В папке app/Http/Middleware будет создан файл с классом Admin и методом


handle(), к которому добавим следующую логику:

Логика метода handle(). Листинг 8.2

if(Auth::user()->isAdmin != 1){
return redirect()->guest('auth/login');
}

Чтобы пропустить запрос дальше, нужно вызвать функцию-замыкание $next


с параметром $request.
84
Лучше всего представлять middlewares как набор уровней, которые HTTP-
запрос должен пройти, прежде чем дойдёт до вашего приложения. На каждом
уровне запрос может быть проверен по различным критериям и, если нужно,
полностью отклонён.

Далее необходимо добавить middleware в свойство routeMiddleware класса


app/Http/Kernel.php, назначив ему некоторое имя, например, admin, которое
будет ключем массива:

Назначение имени классуmiddleware. Листинг 8.3


protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'admin' => 'App\Http\Middleware\Admin',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
];

Теперь мы можем закрывать контроллеры от неавторизированных


пользователей:

Закрытие контроллеров от авторизированных пользователей. Листинг 8.4


public function __construct()
{
parent::__construct();
$this->middleware('admin');
}

Если нужно, чтобы через middleware проходили все HTTP-запросы, то просто


добавьте его в свойство $middleware класса app/Http/Kernel.php:

Свойство $middleware класса app/Http/Kernel.php. Листинг 8.5


protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'App\Http\Middleware\VerifyCsrfToken',
];

Начиная с версии 5.2 Laravel поставляется со встроенным web-middleware,


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

Классы web-middleware. Листинг 8.6


'web' => [

\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\App\Http\Middleware\MenuMiddleware::class,
\App\Http\Middleware\BreadMiddleware::class
85
],
86

9. Контроллеры

Контроллеры хранятся в папке app/controllers/. Этот путь, в свою очередь


определен в файле composer.json в настройке classmap.

Все контроллеры должны наследовать класс BaseController. Этот класс также


может хранится в папке app/controllers, и в него можно поместить общую
логику для других контроллеров.BaseController расширяет базовый класс
Controller.

Создаем контроллер

Создание контроллера с помощью artisan-команды. Листинг 9.1


php artisan make:controller NewsController

Есть несколько способов определения маршрута для контроллера.

Вфайлеapp/routes.php.

Определение маршрута для контроллера с помощью методаget. Листинг 9.2


Route::get('static', 'StaticController@index');

С помощью метода cotroller

Определение маршрута для контроллера с помощью метода controller. Листинг


9.3
Route::controller(
'cabinet' => 'CabinetController',
);

А также с помощью метода controllers

Определение маршрута для контроллера с помощью метода controllers. Листинг


9.4
Route::controllers([
'cabinet' => 'CabinetController',
'user' => 'UserController',
'works' => 'WorksController',
'portfolio' => 'PortfolioController',
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);

А вот так будет выглядеть сам контроллер:

Простейший контроллер StaticController. Листинг 9.5


namespace App\Http\Controllers;
class CabinetController extends BaseController {
public function getIndex()
87
{
echo 'Ok';
}
}

Обратите внимание на namespace (пространство имен) в начале файла.

Теперь, если в адресной строке браузера набираем cabinet, получаем


ответсервера Ok

Создание RESTfull-контроллера через artisan

Сперва в консоли перейдем в папку с файлом artisan. Находясь в этой папке,


выполним следующую консольную команду.

Можно также создавать RESTfull-контроллеры:

Создание RESTfull-контроллеров. Листинг 9.6


php artisan make:controller NewsController --resource

Получим такой контроллер:

RESTfull-контроллер. Листинг 9.7


<?php

class NewsController extends \BaseController {

public function index()


{
//
}

public function create()


{
//
}

public function store()


{
//
}

public function show($id)


{
//
}

public function edit($id)


{
//
}
88
public function update($id)
{
//
}

public function destroy($id)


{
//
}

Также необходимо зарегистрировать роут к этому контроллеру.

Регистрация роута в файле app/routes.php. Листинг 9.8


Route::resource('news', 'NewsController');

Такая регитрация дает множество маршрутов для обработки RESTfull экшнов


контроллера News. Сам сгенерированный контроллер уже имеет методы-
заглушки для каждого из этих маршрутов с комментариями, которые
напоминают вам о том, какие типы запросов они обрабатывают.

Тип Путь Действие Имя маршрута


GET /resource index resource.index
GET /resource/create create resource.create
POST /resource store resource.store
GET /resource/{id} show resource.show
GET /resource/{id}/edit edit resource.edit
PUT/PATCH /resource/{id} update resource.update
DELETE /resource/{id} destroy resource.destroy

Где resource – имя контроллера.

Чтобы создать только часть из возможных экшнов, можно воспользоваться


настройками –only или --except :

Ключевые слова only и except можно указать при регистрации маршрута:

Создание маршрутов для группы экшнов. Листинг 9.9


Route::resource('news', 'PhotoController',
array('only' => array('index', 'show')));
// либо:
Route::resource('news', 'PhotoController',
array('except' => array('create', 'store',
'update', 'delete')));

Обработка неопределенных методов

Можно определить «catch-all» метод, который будет вызываться для


обработки запроса, когда в контроллере нет соответствующего метода. Он
89
должен называться missingMethod и принимать массив параметров запроса в
виде единственного своего аргумента.

Создание части RESTfull-экшнов. Листинг 9.10


public function missingMethod($parameters)
{
//
}

Узнать текущий выполняемый контроллер и экшн

Узнать текущий контроллер можно с помощью следующей команды


Route::currentRouteAction()

Получаем имя контроллера. Листинг 9.11


substr(class_basename(Route::currentRouteAction()), 0,
(strpos(class_basename(Route::currentRouteAction()), '@') -0) )

Получаем имя экшна:

Получаем имя экшна. Листинг 9.12


{{ substr(class_basename(Route::currentRouteAction()),
(strpos(class_basename(Route::currentRouteAction()), '@') + 1)) }}

Вложенная папка для контроллеров

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

app/controllers

app/controllers/auth

app/controllers/adminka

...

В самих контроллерах ничего не меняем. Из корневой папки проекта


открываем консоль и выполняем следующую команду composer:

Перестройка автозагрузки контроллеров. Листинг 9.13


composer dump-autoload

Возьмите на вооружение эту команду: если выскакивает ошибка


автозагрузки классов (ошибка класса ClassLoader.php), то иногда достаточно
выполнить команду dump-autoload. Laravel перезагрузит все классы, и,
возможно, ошибка пропадет. Вот пример ошибки, которую может испрвить
команда dump-autoload:
90
91

10. Шаблоны

По умолчанию, laravel работает с шаблонизатором blade. Шаблоны


создаются в папке app/views и имеют расширение blade.php. Шаблоны
подключаются в экшне через хелпер view(), входящим параметром в который
передается имя шаблона без расширения blade.php.

Сперва создадим в папке view папку layouts для хранения базовых шаблонов.
В папке layouts создадим файл defaults.blade.php

Базовый шаблон defaults.blade.php. Листинг 10.1


@include('layouts.header')
<div class="container">

<div class="masthead">
<h3 class="text-muted">Project name</h3>
<ul class="nav nav-justified">
<li class="active"><a href="#">Home</a></li>
--блок ссылок --
</ul>
</div>

@yield('content')

<!-- Site footer -->


<div class="footer">
<p>&copy; Company 2014</p>
</div>

</div><!-- /container -->


@include('layouts.footer')

В файлах layouts/header.blade.php и layouts/footer.blade.php находится


обычный html-код для шапки и футера сайта. Эти 3 файла – это неизменная
часть шаблона.

@yield(‘content’) – вывод переменной content. Саму переменную определим в


меняющейся части шаблона.

Меняющуюся часть шаблона вынесем в отдельный файл index.blade.php.

Меняющаяся часть шаблона. Листинг 10.2


@extends('layouts.default')
@section('content')
<h1>Добро пожаловать на сайт</h1>
<div>Текст на страницу</div>
@stop

Если необходимо создать часть кода в шаблоне, которая в последствии будет


либо заменена, либо добавлена, то можно воспользоваться дирректировой
@show
92
Объявление переменной с диррективой @show в базовом шаблоне. Листинг 10.3
@section('styles')
<link href="{{ asset('/css/bootstrap.css') }}">
@show

Далее к данному стилю можно добавить другие стили в файлах подшаблона.


В подшаблоне обращаемся к диррективе @parent переменной styles.

Объявление переменной с диррективой @show в базовом шаблоне. Листинг 10.4


@extends('public')
@section('styles')
@parent
<linkhref="{{ asset('/js/fancybox/jquery.fancybox.css') }}">
@stop

Обратите внимание на @extends в начале кода. В шаблонизаторе blade из


контроллера мы обращаемся к подшаблону, а подшаблон с помощью
диррективы @extends сам себя вставляет в базовый шаблон public.

Подключение подшаблона index.blade.php осуществляется в экшне


контроллера.

Подключение подшаблона index.blade.php. Листинг 10.5


return view('index');

Передача массива в шаблон:

Передача массива в шаблон. Листинг 10.6


$posts = array(1=>’One’, 2=>’Two’);
return view('index', $posts);

Имеется также ещё один способ передачи переменных в шаблон, через метод
with():

Передача переменной в шаблон с помощью метода with. Листинг 10.7


$posts = array(1=>’One’, 2=>’Two’);
return view(‘index’)->with(‘posts’, $posts)

Если вы не уверены в существовании передаваемой переменной, то нужно


использовать with(), тогда не будет выводиться ошибка.

И ещё один способ передачи переменных:

Магический метод передачи данных в шаблон. Листинг 10.8


$view = view('greeting')->withName('Victoria');

Для вывода переменных в шаблоне можно воспользоваться диррективой {{}}


93
Вывод переменных шаблона. Листинг 10.9
{{$name}} // простой вывод переменной
{{isset($name)?$name:'Default'}} // вывод либо переменной либо значения
по умолчанию.
{{ $name or 'Default' }}// еще один способ вывода значения по умолчанию

Для предотвращения XSS-атак дирректива {{}} экранирует html-тэги. Если


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

Дирректива {!! !!}. Листинг 10.10


Hello,{!!$name!!}.

Вывод всех элементов массива на экран

Вывод элементов массива на экран. Листинг 10.11


@if($posts->count())
@foreach($posts as $post)
<div>{{$post }}</div>
@endforeach
@endif

Проверка шаблона на существование

Проверить существует ли шаблон по заданному пути, можно с помощью


хелпера view() и метода exists(), входящим параметром в который передается
путь к шаблону.

Проверка шаблона на существование. Листинг 1.


if (view()->exists('templates.base')) {
// шаблон существует
}else{
// шаблон не существует
}

Передача данных во все шаблоны

Для передачи переменных во все шаблоны, можно воспользоваться либо


хелпером view():

Передача данных во все шаблоны с помощью хелперов. Листинг 10.12


view()->share('data', [1, 2, 3]);

либо фасадом:

Передача данных во все шаблоны с помощью фасадов. Листинг 10.13


94
View::share('data', [1, 2, 3]);

Этот код можно положить в метод boot() сервис-провайдера общего сервис-


провайдера приложения AppServiceProvider или своего собственного.

View composer

Композеры (view composers) - функции-замыкания или методы класса,


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

Регистрировать композеры можно внутри сервис-провайдера. Мы будем


использовать фасад View для того, чтобы получить доступ к имплементации
контракта Illuminate\Contracts\View\Factory:

Определение композера. Листинг 10.14


<?php namespace App\Providers;

use View;
use Illuminate\Support\ServiceProvider;
use App\Http\ViewComposers\SiteComposer;

class ComposerServiceProvider extends ServiceProvider {

public function boot()


{
View::composer('*', 'App\Http\ViewComposers\SiteComposer');
}

public function register()


{
//
}

Файл компосера необходимо зарегистрировать в config/app.php

Регистрация файла композера. Листинг 10.15


'App\Providers\ComposerServiceProvider',

Определение класса SiteComposer:

Класс SiteComposer. Листинг 10.16


<?php

namespace App\Http\ViewComposers;
95
use Illuminate\Contracts\View\View;

class SiteComposer
{

public function compose(View $view)


{

$view->with('menu_items',’TEST’);
}

Метод compose должен получать в качестве аргумента инстанс


Illuminate\Contracts\View\View. Для передачи переменных в шаблон
используйте метод with().

Назначение композера для нескольких шаблонов

Вместо имени шаблона можно использовать массив имен.

Назначение композера для нескольких шаблонов. Листинг 10.17


View::composer(['profile', 'dashboard'],
'App\Http\ViewComposers\MyViewComposer');

Композер для всех шаблонов

А вот так можно назначить композер для всех шаблонов:

Назначение композера для всех шаблонов. Листинг 10.18


View::composer('*', function()
{
//
});

Регистрация нескольких шаблонов

Можно использовать метод composers, чтобы зарегистрировать несколько


композеров одновременно:

Регистрация нескольких шаблонов одновременно. Листинг 10.19


View::composers([
'App\Http\ViewComposers\AdminComposer' =>
['admin.index', 'admin.profile'],
'App\Http\ViewComposers\UserComposer' => 'user',
'App\Http\ViewComposers\ProductComposer' => 'product'
]);

ViewCreator
96
Создатели шаблонов работают почти так же, как композеры, но вызываются
сразу после создания объекта шаблона, а не во время его рендеринга в
строку. Для регистрации используйте метод creator:

Регистрация view creator. Листинг 10.20


View::creator('profile', 'App\Http\ViewCreators\ProfileCreator');
97

11. База данных MySQL, миграции (migrate), первоначальная загрузка


данных (seeder), запросы

Подключение и настройка базы данных осуществляется в файле


application/config/database.php. Для подключения нужной базы данных
необходимо прописать настройки сервера баз данных в массив mysql:

Настройка базы данных, элемент массива mysql. Листинг 11.1


'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'project',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),

Либо с помощью функции env:

Использование функции env. Листинг 11.2


'mysql' => [
'driver' => 'mysql',
'host' =>env('DB_HOST', 'localhost'),
'database' =>env(‘DB_DATABASE’),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],

В данном случае, сервер берет настройки подключения из файла .env,


который находится в корне проекта.Значит все необходимые для
подключения данные должны быть определены в этом файле.

Рассмотрим часть содержимого файла .env, в котором определяются данные


для подключения.

Настройки подключение файла .env. Листинг 11.3



DB_HOST=localhost
DB_DATABASE=quickadmin
DB_USERNAME=root
DB_PASSWORD=

Миграции
98
Миграции базы данных являются весьма полезны для любого проекта,
особенно для проектов с несколькими разработчиками, позволяя иметь
последнюю версию базы данных у всех разработчиков. В Laravel для этого
достатчно выполнить одну команду в командной строке.

Для создания новой миграции нам понадобится интерфейс командной строки


Laravel — «Artisan».

Итак, откроем консоль командной строки из папки, где расположен файл


artisan. В консоли введем следующую команду:

Создание файла миграции. Листинг 11.4


php artisan make:migration create_categories

Консоль должна ответить нам следующей фразой:

“Great! New migration created!”

Если получили такой ответ, то перейдем в папку application/migrations. Там


должен находится файл 2014_04_20_210359_create_users.php (к имени
миграции artisan добавляет текущую дату). Откроем данный файл. Увидим
следующее:

Файл миграции. Листинг 11.5


Class Create_Categories {
/**
* Внести изменения в базу данных.
*
* @return void
*/
public function up()
{
//
}

/**
* Отменить изменения базы данных.
*
* @returnvoid
*/
public function down()
{
//
}
}

Класс миграции содержит два метода up() для внесения изменений в таблицу
базы данных и down() для отмены действий метода up(). Например, если мы
создаем таблицу в up(), то в down() ее нужно удалить.

Допишем действия up() и down()


99
Создание таблицы и удаление в методах up() и down() файла миграции. Листинг
11.6
public function up(){
Schema::create('categories', function ($table) {
// auto incremental id (PK)
$table->increments('id');
// varchar 32
$table->string('name', 32);
$table->enum('showhide', array('show','hide'));
// created_at | updated_at DATETIME
$table->timestamps();
});
}
public function down(){
Schema::drop('categories');
}

Внутри функции мы можем использовать следующие красивые методы для


определения структуры таблицы:

 increments() — добавить автоинкрементируемое поле — его будет


иметь бо?льшая часть ваших таблиц
 string() — создать поле VARCHAR — правда, «строка» куда более
описательное имя, чем в стандарте SQL?
 integer() — добавить целочисленное поле
 float() — поле с дробным числом (число с плавающей точкой)
 boolean() — логическое («булево») поле — истина (true) или ложь
(false)
 date() — поле даты
 timestamp() — поле «отпечатка времени», так называемый «Unix
timestamp»
 text() — текстовое поле без ограничения по длине
 blob() — большой двоичный объект (BLOB)

Перед тем как на основе существующих миграций создавать таблицы,


давайте создадим таблицу migrations, в которой laravel будет хранить данные
о самих миграциях:

Создание таблицы миграций. Листинг 11.7


// ВНИМАНИЕ!Данну команду необходимо выполнять, если у нас еще не создана
таблица migrations
php artisan migrate:install

В результате мы должны увидеть

“Migration table created successfully.”

Теперь, когда таблица создана, мы можем выполнить саму миграцию:

Выполнение миграций. Листинг 11.8


100
php artisan migrate

Чтобы удалить таблицу, можно выполнить команду rollback

Выполнение миграций. Листинг 11.9


php artisan migrate:rollback

Загрузка начальных данных в БД

Для загрузки первоначальных данных имеется artisan-команда

Создание класса загрузчика первоначальных данных. Листинг 11.10


php artisan make:seeder UserTableSeeder

Seeder-класс содержит только один метод по умолчанию – run().

Подготовка первоначальных данных в методе run(). Листинг 11.11


use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;

class DatabaseSeeder extends Seeder


{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('users')->insert([
'name' => str_random(10),
'email' => str_random(10).'@gmail.com',
'password' => bcrypt('secret'),
]);
}
}

После того, как данные для загрузки подготовлены, нужно выполнить artisan-
команду db:seed

Загрузка первоначальных данных. Листинг 11.12


php artisan db:seed// все классы

php artisan db:seed --class=UserTableSeeder // только указанный


класс

Откатить данные можно с помощью команды –rolback

Откат загруженных данных. Листинг 11.13


101
php artisan migrate:refresh--seed

Можно использовать вспомогательные классы, в которых будет прописана


логика загруки данных, загружаются они с помощью метода call().

Использование метода call() для вызова вспомогательных классов. Листинг


11.14
publicfunctionrun()
{
Model::unguard();

$this->call(UserTableSeeder::class);
$this->call(PostsTableSeeder::class);
$this->call(CommentsTableSeeder::class);
}

Запросы к базе данных, CRUD-операции

SELECT

Для создания SELECT-запросов, сперва необходимо создать объект


классаDB и статического метода table(), входящим параметром в который
необходимо передать имя таблицы.

Создание объекта запроса. Листинг 11.15


$cat = DB::table(‘categories’);

Для получения результата можно воспользоваться следующими методами:

 get() его мы только что использовали, он возвращает массив объектов


— результирующих строк с полями;
 first() этот метод вернёт один объект-результат, который подошёл под
критерии запроса;
 find($id) метод находит запись по её ID; это краткая форма для записи
where('id', '=', $id); возвращает один объект-результат;
 only($fieldname) возвращает значение одного поля, подходящего под
запрос;
 get(array(…))мы можем передать методу get() массив полей, чтобы
получить только их.

Получить все поля таблицы можно, используя метод get().

Создание объекта запроса. Листинг 11.16


$cat = DB::table(‘categories’)->get();
102
Уточнение запроса осуществляется с помощью метода where()

Применение метода where(). Листинг 11.17


$cat = DB::table(‘categories’)->where(‘showhide’, ‘=’, ‘show’)->get();

Конструктор запросов может быть использован для выборки данных из


нескольких таблиц через JOIN.

Выборка данных из нескольких таблиц черезJoin. Листинг 11.18


DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.id', 'contacts.phone', 'orders.price');

Если необходимо сделать сложную фильтацию, то в значение методов


where()или orWhere() можно добавлять функуию с дополнительным query-
запросом:

Сложная фильтрация. Листинг 11.19


DB::table('users')
->where('name', '=', 'Джон')
->orWhere(function ($query) {
$query->where('votes', '>', 100)
->where('title', '<>', 'Админ');
})->get();

INSERT

Вставка данных . Листинг 11.20


DB::table('users')->insert(
array('email' => 'john@example.com', 'votes' => 0)
);

Если необходимо вставить данные и при этом тут же получить id, можно
воспользоваться методом insertGetId.

Вставка данных и получение id. Листинг 11.21


$id = DB::table('users')->insertGetId(
array('email' => 'john@example.com', 'votes' => 0)
);

Laravel также поддерживает множественную вставку данных:

Множественная вставка данных. Листинг 11.22


DB::table('users')->insert(array(
array('email' => 'taylor@example.com', 'votes' => 0),
array('email' => 'dayle@example.com', 'votes' => 0),
));

UPDATE
103
Обновление данных. Листинг 11.23
DB::table('users')
->where('id', 1)
->update(array('votes' => 1));

DELETE

Удаление. Листинг 11.24


DB::table('users')->where('votes', '<', 100)->delete();
DB::update('update users set votes = 100 where name = ?',
array('John'));

Выполнение запросов другого типа

Удаление таблицы. Листинг 11.25


DB::statement('drop table users');

Транзакции

Для выполнения запросов внутри одной транзакции, можно воспользоваться


методом transaction.

Пример транзакции. Листинг 11.26


DB::transaction(function()
{
DB::table('users')->update(array('votes' => 1));
DB::table('posts')->delete();
});

Доступ к соединениям

При использовании нескольких подключений к БД, вы можете получить к


ним доступ через метод DB::connection:

Доступ к другому соединению. Листинг 11.27


$users = DB::connection('foo')->select(...);
$pdo = DB::connection()->getPdo();
// получение объектаPDO указанног соединения.
DB::reconnect('foo');
// переподключение к базе данных

Если вам нужно отключиться от БД - например, чтобы не превысить лимит


max_connections в БД, вы можете воспользоваться методом disconnect

Отключение от базы данных. Листинг 11.28


DB::disconnect('foo');

По умолчанию, Laravel записывает все SQL-запросы в памяти, выполненные


104
в рамках текущего HTTP-запроса.

Однако, в некоторых случаях, как, например, при вставке большого


количества записей, это может быть слишком ресурсозатратно. Для
отключения журнала вы можете использовать метод disableQueryLog:

Отключение журнала логирования запросов. Листинг 11.29


DB::connection()->disableQueryLog();

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


getQueryLog:

Массив выполненных запросов. Листинг 11.30


$queries = DB::getQueryLog();
105

12. Модели

Модели создаем в папке models. Имя файла и класса модели laravel


ассоциирует с именем таблицы, причем из таблицы убирается окончание “s”.
Таким образом, мы создали модель для таблицы products.

Простейшая модель. Листинг 12.1


<?php
class Product extends Eloquent
{

Eloquent - класс ORM для модели, который используется в Laravel. Он


обеспечивает работу с объектами базы данных.

Свойство $timestamps получает значение true, что указывает на


автоматическое обновление полей created_at и updated_at при создании или
обновлении записи соответственно.

Внимание: Eloquent предполагает, что каждая таблица имеет первичный


ключ с именем id. Вы можете определить свойство $primary Key для
изменения этого имени. Аналогичным образом, вы можете определить
свойство $connection для задания имени подключения к БД, которое должно
использоваться при работе с данной моделью.

Кроме свойства $primaryKey,Laravel предопределяет следующие свойства


класса модели:

Предопределенные свойства модели. Листинг 12.2


<?php
class Product extends Eloquent
{
protected $table = 'my_users';
public static $timestamps = true;
protected$fillable = array('username, 'email'); //указание на то,
какие поля могут массово назначатся
protected$guarded = array('id', 'password'); // запрет на массовое
назначение перечисленных полей.
protected $hidden = array('password');//позволяет скрыть
определенные столбцы при возвращении экземпляра модели
}

Массовое заполнение

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


Эти атрибуты затем присваиваются модели через массовое заполнение. Это
удобно, но в то же время представляет серьёзную проблему с безопасностью,
когда вы передаёте ввод от клиента в модель без проверок — в этом случае
106
пользователь может изменить любое и каждое поле вашей модели. По этой
причине, по умолчанию, Eloquent защищает вас от массового заполнения.

Пример массового заполнения. Листинг 12.3


$user = User::create(array('name' => 'Джон'));

Методы возврата данных

Модель включает три простых метода для возврата конкретных элементов


данных из неё:

Методы возврата данных. Листинг 12.4


public function getAuthIdentifier()
{
return $this->getKey(); // вернетid пользователя
}
public function getAuthPassword()
{
return $this->password; // вернетпарольпользователя
}
public function getReminderEmail()
{
return $this->email; // вернетemailдляотправкинапоминанияопароле
}
protected function getDateFormat()
{
return 'U'; // использованиесобственногоформатавремени
}

Извлечение данных

Как только модель определена, у вас всё готово для того, чтобы можно было
выбирать и создавать записи. Обратите внимание, что вам нужно создать в
этой таблице поля updated_at и created_at. Если вы не хотите, чтобы они были
автоматически используемы, установите свойство $timestamps класса модели
в false.

Получение всех записей модели. Листинг 12.5


$users = User::all();

По первичному ключу записи можно получить так:

Получение записи по первичному ключу. Листинг 12.6


$user = User::find(1);
var_dump($user->name);

Метод find может принимать входящим параметром массив


107
Использование массива ключей в методе find. Листинг 1.
$user = User::find([1,2,4]);

Для извлечения данных по другим полям можно воспользоваться методом


where.

Использование метода where. Листинг 1.


$user = User::where(‘email’,$email)->get()

При этом конечный метод get() возвращает массив объектов, для вывода
которых в последствии необходимо использовать foreach.

Метод where() может использоваться с тремя входящими параметрами, тогда


вторым входящим параметром передается ключевой символ =, !=, >, <, <>
или ключевое слово LIKE.

Метод where с тремя входящими параметрами. Листинг 1.


$user = User::where(‘name’,’=’,$name)->get()

Метод where можно использовать в запросе несколько раз подряд, тогда


последующие where работают как AND WHERE.

Множественный вызов метода where. Листинг 1.


$user = Tovar::where(‘cat_id’,1)->where(‘showhide’,’show’)->get()

Существует еще один полезный метод, связанный с подзапросом where – это


whereIn. WhereIn вторым (или трейтим) входящим параметром принимает
массив значений, по которым необходимо формировать запрос.

Использование whereIn. Листинг 1.


$models = Model::whereIn('id', [1, 2, 3])->get();

Все методы, доступные в конструкторе запросов, также доступны в запросах


с использованием моделей.

Иногда вам нужно возбудить исключение, если определённое значение не


найдено, что позволит вам его отловить в обработчике App::error() и вывести
страницу 404 («Не найдено»).

Получение данных по первичному ключу с возбуждением исключения. Листинг


12.7
108
$model = User::findOrFail(1);

$model = User::where('votes', '>', 100)->firstOrFail();

Для регистрации обработчика ошибки необходимо подписаться на событие


ModelNotFoundException:

Подписка на событие ModelNotFoundException. Листинг 12.8


use Illuminate\Database\Eloquent\ModelNotFoundException;

App::error(function (ModelNotFoundException $e) {


return Response::make('Not Found', 404);
});

Вставка

Данные через модель вставляются следующим образом:

Вставка данных. Листинг 12.9


$user = new User; // создаем объект

$user->name = 'Джон'; // данные

$user->save(); // сохранение

Внимание: обычно модели Eloquent содержат автоматические числовые


ключи (autoincrementing). Однако, если вы хотите использовать собственные
ключи, установите свойство $incrementing класса модели в значение false.

Можно также использовать метод create() для создания и сохранения модели


одной строкой. Метод вернёт добавленную модель. Однако перед этим
нужно определить либо свойство $fillable, либо $guarded в классе модели, так
как изначально все модели Eloquent защищены от массового заполнения.

Всавка данных путем массового заполнения . Листинг 12.10


$user = User::create(array('name' => 'Джон'));

Обновление

Обновление данных по id. Листинг 16.11


$user = User::find(1);
$user->email = 'alex@ya.ru';
$user->save();

Удаление

Удаление данных по id. Листинг 12.11


109
$user = User::find(1);
$user->delete();

Мягкое удаление

Когда вы "мягко" удаляете модель, она на самом деле остаётся в базе данных,
однако устанавливается её поле deleted_at. Для включения мягких удалений
на модели, определите свойство её softDelete:

Включение мягкого удаления в модели. Листинг 12.12


class User extends Eloquent {
protected $softDelete = true;
}

Для добавления поля deleted_at, можно воспользоваться методом softDeletes()


из миграции.

Добавление поля deleted_at в файл-миграцию. Листинг 12.13


$table->softDeletes();

Теперь, когда вы вызовите метод delete, поле deleted_at будет установлено в


значение текущего времени. При запросе моделей, используйте их мягкое
удаление, "удалённые" модели не будут включены в результат запроса. Для
отображения всех моделей, в том числе удалённых, используйте метод
withTrashed.

Запросы с удаленными строками. Листинг 12.14


// включение в запрос удаленных строк
$users = User::withTrashed()->where('account_id', 1)->get();
//Если вы хотите получить только удалённые модели, вызовите метод
onlyTrashed:
$users = User::onlyTrashed()->where('account_id', 1)->get();
//Для восстановления мягко удалённой модели в активное
состояниеиспользуется метод restore:
$user->restore();
//Вы также можете использовать его в запросе:
User::withTrashed()->where('account_id', 1)->restore();
//Метод restore можно использовать и в отношениях:
$user->posts()->restore();
//Если вы хотите полностью удалить модель из БД, используйте метод
forceDelete:
$user->forceDelete();
//Он также работает с отношениями:
$user->posts()->forceDelete();
Для того, чтобы узнать, удалена ли модель, можно использовать метод
trashed:
if ($user->trashed())
{
//
}
110
Поля времени

По умолчанию, Eloquent автоматически поддерживает поля created_at и


updated_at . Просто добавьте эти timestamp-поля к таблице и Eloquent
позаботится об остальном. Если вы не хотите, чтобы он поддерживал их,
добавьте свойство timestamps к классу модели.

Отключение автоматических полей времени. Листинг 12.15


class User extends Eloquent {
protected $table = 'users';
public $timestamps = false;
}

Для настройки форматов времени можно воспользоваться функцией


getDateFormat().

Использование собственного формата времени. Листинг 12.16


class User extends Eloquent {
protected function getDateFormat()
{
return 'U';
}
}

Заготовки запросов

Заготовки позволяют повторно использовать логику запросов в моделях. Для


создания заготовки просто начните имя метода со scope:

Создание заготовок запроса. Листинг 12.17


class User extends Eloquent {

public function scopePopular($query)


{
return $query->where('votes', '>', 100);
}

public function scopeWomen($query)


{
return $query->whereGender('W');
}

Использование заготовок запросов

Использование заготовок запросов. Листинг 12.18


$users = User::popular()->women()->orderBy('created_at')->get();

Можно также использовать заготовки запросов с входящими параметрами:


111
Входящие параметры в заготовках запросов. Листинг 12.19
class User extends Eloquent {

public function scopeOfType($query, $type)


{
return $query->whereType($type);
}

Вызов заготовок запросов с входящими параметрами:

Входящие параметры в заготовках запросов. Листинг 12.20


$users = User::ofType('member')->get();

Связи в моделях (отношения)

Логика связей в Laravel осуществляется с помощью класса Eloquent. Eloquent


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

 Один к одному
 Один ко многим
 Многие ко многим
 Ко многим через
 Полиморфические связи
 Полиморфические связи многие ко многим

Один к одному / hasOne

Связь вида «один к одному» является очень простой. К примеру, модель User
может иметь один Phone. Мы можем определить такое отношение в Eloquent:

Связь hasOne. Листинг 12.21


class User extends Eloquent {

public function phone()


{
return $this->hasOne('Phone');
}

loquent считает, что поле в таблице называется по имени модели плюс _id. В
данном случае предполагается, что это user_id. Если мы хотим перекрыть
стандартное имя, необходимо передать второй параметр методу hasOne().
Кроме того, мы можем передать в метод третий аргумент, чтобы указать,
какие локальные столбцы следует использовать для объединения:
112
Передача дополнительных аргументов связи hasOne. Листинг 12.22
return $this->hasOne('Phone', 'foreign_key');

return $this->hasOne('Phone', 'foreign_key', 'local_key');

Принадлежность / belongsTo

Для создания обратного отношения в модели имеется специальный метод


belongsTo() («принадлежит к»):

Связь belongsTo. Листинг 12.23


class Phone extends Eloquent {

public function user()


{
return $this->belongsTo('User');
}

В примере выше Eloquent будет искать поле user_id в таблице phones. Если
вы хотите назвать внешний ключ по другому, передайте это имя вторым
параметром к методу belongsTo():

Передача аргумента внешнего ключа связи belongsTo. 12.24


class Phone extends Eloquent {

public function user()


{
return $this->belongsTo('App\User', 'local_key');
}

Кроме того, можно передать третий аргумент, который определит имя


связного столбца в родительской таблице.

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


Листинг 12.25
class Phone extends Eloquent {

public function user()


{
return $this->belongsTo('App\User', 'local_key', 'parent_key');
}

Один ко многим / hasMany


113
Примером отношения «один ко многим» является статья в блоге, которая
имеет «много» комментариев. Мы можем смоделировать это отношение
таким образом:

Связь hasMany. Листинг 12.26


class Post extends Eloquent {

public function comments()


{
return $this->hasMany('Comment');
}

Теперь мы можем получить все комментарии с помощью динамического


свойства:

Получение данных из связующей модели. Листинг 12.27


$comments = Post::find(1)->comments;

Если нужно добавить ограничения на получаемые комментарии, можно


вызвать метод comments() и продолжить добавлять условия:

Добавление условий к связующей таблице. Листинг 12.28


$comments = Post::find(1)->comments()->where('title', '=', 'foo')-
>first();

Многие ко многим / belongsToMany

Отношения типа «многие ко многим» — более сложные, чем остальные виды


отношений. Примером может служить пользователь, имеющий много ролей,
где роли также относятся ко многим пользователям. Например, один
пользователь может иметь роль admin. Нужны три таблицы для этой связи:
users, roles и role_user. Название таблицы role_user происходит от
упорядоченных по алфавиту имён связанных моделей, и она должна иметь
поля user_id и role_id.

Связь belongsToMany. Листинг 12.29


class User extends Eloquent {

public function roles()


{
return $this->belongsToMany('Role');
}

Теперь мы можем получить роли пользователей следующим образом:


114
Получение ролей пользователя. Листинг 12.30
$roles = User::find(1)->roles;

Полиморфические отношения

Полиморфические отношения позволяют модели быть связанной с более, чем


одной моделью. Например, может быть модель Photo, содержащая записи,
принадлежащие к моделям Staff и Order. Мы можем создать такое отношение
таким образом:

Создание полиморфических отношений. Листинг 12.31


class Photo extends Eloquent {
public function imageable()
{
return $this->morphTo();
}
}
class Staff extends Eloquent {
public function photos()
{
return $this->morphMany('Photo', 'imageable');
}
}
class Order extends Eloquent {
public function photos()
{
return $this->morphMany('Photo', 'imageable');
}
}

Теперь мы можем получить фотографии и для персонала, и для заказа.

Чтение полиморфической связи. Листинг 12.32


$staff = Staff::find(1);
foreach ($staff->photos as $photo)
{
//
}

Однако истинная "магия" полиморфизма происходит при чтении связи на


модели Photo:

Чтение связи на владельце полиморфического отношения. Листинг 12.33


$photo = Photo::find(1);
$imageable = $photo->imageable;

Отношение imageable модели Photo вернёт либо объект Staff, либо объект
Order, в зависимости от типа модели, к которой принадлежит фотография.
115
Чтобы понять, как это работает, давайте изучим структуру БД для
полиморфического отношения.

Структура таблиц полиморфической связи:

staff

id - integer

name - string

orders

id - integer

price - integer

photos

id - integer

path - string

imageable_id - integer

imageable_type - string

Главные поля, на которые нужно обратить внимание: imageable_id и


imageable_type в таблице photos. Первое содержит ID владельца, в нашем
случае - заказа или персонала, а второе - имя класса-модели владельца. Это
позволяет ORM определить, какой класс модели должен быть возвращёт при
использовании отношения imageable.

Запросы к отношениям

При чтении отношений модели вам может быть нужно ограничить


результаты в зависимости от существования связи. Например, вы хотите
получить все статьи в блоге, имеющие хотя бы один комментарий. Для этого
можно использовать метод has:

Проверка связей при выборке. Листинг 12.34


$posts = Post::has('comments')->get();
//Вы также можете указать оператор и число:
$posts = Post::has('comments', '>=', 3)->get();

Eloquent позволяет вам читать отношения через динамические свойства.


Eloquent автоматически определит используемую связь и даже вызовет get
для связей "один ко многим" и first - для связей "один к одному". К примеру,
116
для следующей модели $phone:

Модель phone. Листинг 12.35


class Phone extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
}

Чтение свойств связующей модели

Чтение свойств связующей модели. Листинг 12.36


$phone = Phone::find(1);
//Вместо того, чтобы получить e-mail пользователя так:
echo $phone->user()->first()->email;
//Вызов может быть сокращён до такого:
echo $phone->user->email;

Активная загрузка

Активная загрузка (eager loading) призвана устранить проблему запросов


N+1. Например, представьте, что у нас есть модель Book со связью к модели
Author. Отношение определено как:

Определение связи. Листинг 12.37


class Book extends Eloquent {
public function author()
{
return $this->belongsTo('Author');
}
}

Теперь у нас есть такой код:

Получаем всех авторов книг по связи. Листинг 12.38


foreach (Book::all() as $book)
{
echo $book->author->name;
}

Цикл выполнит один запрос для получения всех книг в таблице, а затем
будет выполнять по одному запросу на каждую книгу для получения автора.
Таким образом, если у нас 25 книг, то потребуется 26 запросов.

К счастью, мы можем использовать активную загрузку для кардинального


уменьшения числа запросов. Отношение будет активно загружено, если оно
было указано при вызове метода with:

Использование активной загрузки с помощью метода with. Листинг 12.39


117
foreach (Book::with('author')->get() as $book)
{
echo $book->author->name;
}

В цикле выше будут выполнены всего два запроса:

select * from books

select * from authors where id in (1, 2, 3, 4, 5, ...)

Разумное использование активной загрузки поможет сильно повысить


производительность вашего приложения.

Конечно, вы можете загрузить несколько отношений одновременно:

Загрузка нескольких отношений одновременно. Листинг 12.40


$books = Book::with('author', 'publisher')->get();

Вы даже можете загрузить вложенные отношения:

Загрузка вложенных отношений. Листинг 12.41


$books = Book::with('author.contacts')->get();

В этом примере связь author будет активно загружена вместе со связью


contacts модели автора.

Иногда вам может быть нужно не только активно загрузить отношение, но


также указать условие для его загрузки:

Использование условий в активной загрузке. Листинг 12.42


$users = User::with(array('posts' => function($query)
{
$query->where('title', 'like', '%первое%');
}))->get();

Вставка связных моделей

Часто вам нужно будет добавить связанную модель. Например, вы можете


создать новый комментарий к сообщению.

Вместо явного указания значения для поля post_id, вы можете вставить


модель через её владельца - модели Post:

Создание связной модели. Листинг 12.43


$comment = new Comment(array('message' => 'Новый комментарий.'));
$post = Post::find(1);
118
$comment = $post->comments()->save($comment);

В этом примере поле post_id вставленного комментария автоматически


получит значение ID своей статьи.

При обновлении связей belongsTo ("принадлежит к") вы можете использовать


метод associate. Он установит внешний ключ на связанной модели:

Связывание моделей (Belongs To). Листинг 12.44


$account = Account::find(10);
$user->account()->associate($account);
$user->save();

Вы также можете вставлять связанные модели при работе с отношениями


многие ко многим. Продолжим использовать наши модели User и Role в
качестве примеров. Вы можем легко привязать новые роли к пользователю
методом attach.

Связывание моделей многие ко многим. Листинг 12.45


$user = User::find(1);
$user->roles()->attach(1);

Вы также можете передать массив атрибутов, которые должны быть


сохранены в связующей (pivot) таблице для этого отношения:

Передача массива атрибутов. Листинг 12.46


$user->roles()->attach(1, array('expires' => $expires));

Также существует противоположность attach – метод detach

Использование метода detach. Листинг 12.47


$user->roles()->detach(1);

Вы также можете использовать метод sync для привязки связанных моделей.


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

Использование Sync для привязки моделей "многие ко многим". Листинг 12.48


$user->roles()->sync(array(1, 2, 3));

Вы также можете связать другие связующие таблицы с нужными ID.

Добавление данных для связующий таблицы при синхронизации. Листинг 12.49


119
$user->roles()->sync(array(1 => array('expires' => true)));

Иногда вам может быть нужно создать новую связанную модель и добавить
её одной командой. Для этого вы можете использовать метод save:

Создание и связывание модели одной командой. Листинг 12.50


$role = new Role(array('name' => 'Editor'));
User::find(1)->roles()->save($role);

В этом примере новая модель Role будет сохранена и привязана к модели


User. Вы можете также передать массив атрибутов для помещения в
связующую таблицу:

Передача массива атрибутов для помещения в связующую таблицу. Листинг


12.51
User::find(1)->roles()->save($role, array('expires' => $expires));

Обновление времени владельца

Когда модель принадлежит к другой посредством belongsTo - например,


Comment, принадлежащий Post- иногда нужно обновить время изменения
владельца при обновлении связанной модели. Например, при изменении
модели Comment вы можете обновлять поле updated_at её модели Post.
Eloquent делает этот процесс простым: добавьте свойство touches,
содержащее имена всех отношений с моделями-потомками.

Обновление времени владельца с помощью метода touches. Листинг 16.53


class Comment extends Eloquent {
protected $touches = array('post');
public function post()
{
return $this->belongsTo('Post');
}
}

Теперь при обновлении Comment владелец Post также обновит своё поле
updated_at:

Обновление. Листинг 16.54

$comment = Comment::find(1);

$comment->text = 'Изменениеэтогокомментария.';

$comment->save();

Работа со связующими таблицами


120
Работа отношения многие ко многим требует наличия промежуточной
таблицы. Например, предположим, что наш объект User имеет множество
связанных объектов Role. После чтения отношения мы можем прочитать
таблицу pivot на обоих моделях:

Чтение связующей таблицы на обоих моделях. Листинг 12.52


$user = User::find(1);
foreach ($user->roles as $role)
{
echo $role->pivot->created_at;
}

Заметьте, что каждая модель Role автоматически получила атрибут pivot.


Этот атрибут содержит модель, представляющую промежуточную таблицу, и
она может быть использована как любая другая модель Eloquent.

По умолчанию, только ключи будут представлены в объекте pivot. Если ваша


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

Указание полей в связующей таблице. Листинг 12.53


return $this->belongsToMany('Role')->withPivot('foo', 'bar');

Теперь атрибуты foo и bar будут также доступны на объекте pivot модели
Role.

Если вы хотите автоматически поддерживать поля created_at и updated_at


актуальными, используйте метод withTimestamps при создании отношения:

Использование метода withTimestamps(). Листинг 12.54


return $this->belongsToMany('Role')->withTimestamps();

Для удаления всех записей в связующей таблице можно использовать метод


detach:

Удаление всех записей в связующей таблице. Листинг 12.55


User::find(1)->roles()->detach();

Заметьте, что эта операция не удаляет записи из таблицы roles, а только из


связующей таблицы.

Коллекции

Все методы Eloquent, возвращающие набор моделей - либо через get, либо
121
через отношения - возвращают объект-коллекцию. Этот объект реализует
стандартный интерфейс PHP IteratorAggregate, что позволяет ему быть
использованным в циклах наподобие массива. Однако этот объект также
имеет набор других полезных методов для работы с результатом запроса.

Например, мы можем выяснить, содержит ли результат запись с


определённым первичным ключом методом contains.

Проверка на существование ключа в коллекции. Листинг 12.56


$roles = User::find(1)->roles;
if ($roles->contains(2))
{
//
}

Коллекции могут быть преобразованы в массив или в строку JSON.

Преобразование коллекций в массив и в JSON. Листинг 12.57


$roles = User::find(1)->roles->toArray();
$roles = User::find(1)->roles->toJson();

Если коллекция преобразуется в строку, результатом будет JSON-выражение:

Преобразование коллекции в строку. Листинг 12.58


$roles = (string) User::find(1)->roles;

Коллекции Eloquent имеют несколько полезных методов для прохода и


фильтрации содержащихся в них элементов.

Проход и фильтрация элементов коллекции. Листинг 12.59


$roles = $user->roles->each(function($role)
{
});
$roles = $user->roles->filter(function($role)
{
});

Функции обратного вызова

Применение функций обратного вызова. Листинг 12.60


$roles = User::find(1)->roles;
$roles->each(function($role)
{
//
});

Сортировка коллекции по назначению.

Сортировка. Листинг 12.61


122
$roles = $roles->sortBy(function($role)
{
return $role->created_at;
});

Иногда вам может быть нужно получить собственный объект Collection со


своими методами. Вы можете указать его при определении модели Eloquent,
перекрыв метод newCollection.

Использование произвольного класса коллекции. Листинг 12.62


class User extends Eloquent {
public function newCollection(array $models = array())
{
return new CustomCollection($models);
}
}

Читатели и преобразователи

Eloquent содержит мощный механизм для преобразования атрибутов модели


при их чтении и записи. Просто объявите в её классе метод getFooAttribute.
Помните, что имя метода должно следовать соглашению camelCase, даже
если поля таблицы используют соглашение snake-case (он же - "стиль Си", с
подчёркиваниями -прим. пер.).

Объявление читателя. Листинг 12.63


class User extends Eloquent {
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
}

В примере выше поле first_name теперь имеет читателя (accessor). Заметьте,


что оригинальное значение атрибута передаётся методу в виде параметра.

Преобразователи (mutators) объявляются подобным образом.

Объявление преобразователя. Листинг 12.64


class User extends Eloquent {
public function setFirstNameAttribute($value)
{
$this->attributes['first_name'] = strtolower($value);
}
}

Преобразователи дат

По умолчанию, Eloquent преобразует поля created_at, updated_at и deleted_at в


объекты Carbon, которые предоставляют множество полезных методов,
расширяя стандартный класс PHP DateTime.
123
Вы можете указать, какие поля будут автоматически преобразованы, и даже
полностью отключить преобразование, перекрыв метод getDates класса
модели.

Метод getDates(). Листинг 12.65


public function getDates()
{
return array('created_at');
}

Когда поле является датой, вы можете установить его в число-оттиск


времени формата Unix (timestamp), строку даты формата(Y-m-d), строку
даты-времени и, конечно, экземпляр объекта DateTime или Carbon.

Чтобы полностью отключить преобразование дат, просто верните пустой


массив из метода getDates.

Отключение преобразователя дат. Листинг 12.66


public function getDates()
{
return array();
}

События моделей

Модели Eloquent инициируют несколько событий, что позволяет вам


добавить к ним свои обработчики с помощью следующих методов: creating,
created, updating, updated, saving, saved, deleting, deleted, restoring, restored.

Когда первый раз сохраняется новая модель, возникают события creating и


created. Если модель уже существовала на момент вызова метода save,
вызываются события updating и updated. В обоих случаях также возникнут
события saving и saved.

Если обработчики creating, updating, saving или deleting вернут значение false,
то действие будет отменено.

Отмена сохранения через события. Листинг 12.67


User::creating(function($user)
{
if ( ! $user->isValid()) return false;
});

Модели Eloquent также содержат статический метод boot, который может


быть хорошим местом для регистрации ваших обработчиков событий.

Использование метода boot. Листинг 12.68


class User extends Eloquent {
public static function boot()
124
{
parent::boot();
// Регистрация ваших обработчиков...
}
}

Наблюдатели моделей

Для того, чтобы держать всех обработчиков событий моделей вместе, вы


можете зарегистрировать наблюдателя (observer). Объект-наблюдатель
может содержать методы, соответствующие различным событиям моделей.
Например, методы creating, updating и saving, а также любые другие методы,
соответствующие именам событий.

К примеру, класс наблюдателя может выглядеть так:

Класс наблюдателя. Листинг 12.69


class UserObserver {
public function saving($model)
{
//
}
public function saved($model)
{
//
}
}

Зарегистрировать его можно, используя метод observe:

Регистрация наблюдателя методом observe(). Листинг 12.70


User::observe(new UserObserver);
125

13. Управление данными через Artisan tinker

Для управления данными через Artisan tinker необходима модель. Сперва


создадим пустую модель service, которая свяжется с таблицей services.

Запускаем консоль tinker.

Запуск консоли tinker. Листинг 13.1


php artisan tinker

Консоль должна ответить вот таким приглашением для ввода следующих


команд:

Далее создадим объект и добавим данные.

Создание объекта и заполнение данными. Листинг 13.2


$serv = new App\Models\Service
$serv->name = ‘Web-sites’

Проверить объект можно следующим образом:

Вызов объекта. Листинг 13.3


$serv
$serv->toArray()

Сохранение данных

Сохранение данных с помощью метода save(). Листинг 13.4


$serv->save()

С помощью всё той же модели, мы можем вывести все данные из таблицы:

Получение всех записей. Листинг 13.5


App\Models\Service::all()
App\Models\Service::all()->toArray()
$one = App\Models\Service::find(1)
$one->name
126
Чтобы обновить данные, можем воспользоваться тем же объектом:

Обновление данных. Листинг 13.6


$serv->name ‘create web-sites’
$serv->save()

Множественная вставка

Сперва добавим свойство fillable модели Service

Открываем атрибуты для множественной вставки в модели. Листинг 13.7


class Service extends Model {

protected $fillable = [
'name'
];

Выполняем команду множественной вставки:

Множественная вставка. Листинг 13.8


App\Models\Service::create([‘name’=>’new work’])
127

14. Request

Request – это объект запроса сервера, это те данные, которые мы можем


получить от клиента (браузера).

Получить объект запроса можно двумя способами.

1 способ. С помощью фасада Request

Фасад Request дает доступ к объекту HTTP-запроса:

Получение объекта запроса при помощи фасадаRequest. Листинг 14.1


$name = Request::input('name');

При этом фасад Request необходимо включить в начале контроллера,


используя диррективу use.

Подключение фасада Request. Листинг 14.2


use Request;

2 способ. Через внедрение зависимостей.

Можно получить объект HTTP-запроса при помощи DI (dependency injection,


внедрение зависимости). Способ заключается в том, что в аргументы
конструктора контроллера помещается (type-hint) объект, который нам
нужен, и Laravel, когда создает контроллер, создает этот объект и подает на
вход конструктору контроллера:

Получение объекта HTTP-запроса через внедрение зависимости. Листинг 14.3


<?php namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;

class UserController extends Controller {

/**
* Сохранение данных пользователя.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$name = $request->input('name');

//
128
}

Если метод контроллера ожидает параметр из роута, укажите его после


зависимостей:

Указание параметра после внедрения зависимости. Листинг 14.4


publicfunction update(Request $request,$id)
{
//
}

Получение входных данных

Объект Illuminate\Http\Request предоставляет доступ к входным данным,


например, к переменным POST или PUT, полученным из формы. Причем,
указывать явно метод запроса не нужно, есть универсальный метод:

Получение входных данных. Листинг 14.5


$name =Request::input('name');

Получение переменной с дефолтным значением:

Значение по умолчанию для входных данных. Листинг 14.6


$name =Request::input('name','Sally');

Для определения, содержится ли переменная в запросе, можно


воспользоваться методом has.

Определение содержится ли переменная в запросе. Листинг 14.7


if (Request::has('name'))
{
//
}

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

Получение всех переменных запроса. Листинг 14.8


$input =Request::all();

Для получения избранных переменных, можно воспользоваться методами


only и except

Методы only и except. Листинг 14.9


129
$input =Request::only('username','password');// только эти

$input =Request::except('credit_card');// все, кроме этой

C массивами можно работать через нотацию с точкой:

Использование точек для работы с массивом. Листинг 14.10


$input = Request::input('products.0.name');

Классы обработки Request-запросов

Создание Request-класса с помощью команды artisan. Листинг 14.11


php artisan make:request NewsRequest

Данная команда создаст файл с именем NewsRequest и с соответствующим


классом, который содержит два метода authorize() и rules() в папке requests/.

Методы Request-файла. Листинг 14.12


public function authorize()
{
return true;// имеет ли доступ не авторизированный пользователь
}

public function rules()


{
return []; // возвращаем массив правил по обработке
}

Все правила валидации, которые используются в методе rules(), прописаны в


файле resources/lang/validation.php. Все элементы массива, которые есть в
этом файле, можно использовать в качестве правил в request-файлах.

Пример использования правил валидации в классе NewsRequest. Листинг 14.13


Public function rules()
{
return [
'name'=>'required|max:100',
'body' => 'required|min:2',
'theme_id'=>'required|numeric'
];
}

В модели, к которой мы хотим привязать эти request-данные, должно быть


определено свойство $fillable, в котором мы указываем, какие имена полей
таблицы будут задействованы при множественной вставки данных.

Модель News. Листинг 14.14


class News extends Model
130
{
protected $table = 'news';
protected $fillable = [
'name',
'body',
'user_id',
'theme_id',
];
}

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


request-запроса, и через модель, с помощью множественной вставки (метод
create()) помещать их в таблицу news:

Контроллер, обрабатывающийrequest-запрос. Листинг 14.15


<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Auth;
use App\Http\Requests;
use App\Http\Controllers\Controller;

class CabinetController extends Controller


{
public function __consturct(){
parent::__construct();
}
public function getIndex(){
return view('cabinet');
}
public function postIndex(Requests\NewsRequest $r){
$r['user_id'] = Auth::user()->id;
\App\News::create($r->all());
return redirect('cabinet');
}
}

При этом важно помнить, что имена полей в таблице должны совпадать с
именами элементов форм.

В шаблоне мы можем перехватывать ошибки валидации (волшебство laravel


заключается в том, что специально эти ошибки в шаблон передавать не
надо):

Массив errors в файле шаблона с формой. Листинг 14.16


@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong>Найденыследующиеошибки.<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
131
</ul>
</div>
@endif

Перевести ошибки на русский язык просто. Для этого в конфигурационном


файле app.php, в настройках локал и укажем ru.

Используем русскую локаль. Листинг 14.17


'locale' => 'ru',

После чего создадим в папке resources/lang еще одну папку – ru. В ее можно
вставить скопированный файл en/validation.php. И перевести на русский язык
необходимые ошибки. При этомом слова, начинающиеся с : (двоеточия) не
переводим, т.к. это атрибуты.

Элементы полей также нужно перевести на русский язык. Для этого в файле
validation.php есть элемент массива attributes. Переведем элементы формы
здесь:

Элемент массива attributes. Листинг 14.18


'attributes' => ['name'=>'наименование', 'body'=>'содержимое',
'theme_id'=>'тема'],

Теперь ошибки валидации выглядят так:


132

15. Response

HTTP-Response - это ответ фреймворка, который отдается клиенту


(браузеру), от которого пришел HTTP-запрос.

Наиболее простой способ создать HTTP-ответ - это возвратить строку в роуте


или контроллере.

Responseв виде возврата строки из роута. Листинг 15.1


Route::get('/', function()
{
return 'Hello world';
});

Создание своих ответов

Однако чаще в контроллерах приходится возвращать объект


Illuminate\Http\Response или шаблон. Возврат объекта Response позволяет
изменить HTTP-код и заголовки ответа. Этот объект наследуется от
Symfony\Component\HttpFoundation\Response, который предоставляет
разнообразные методы для построения HTTP-ответа:

Метод headerдля построенияHTTP-ответа. Листинг 15.2


use Illuminate\Http\Response;

return (new Response($content, $status))


->header('Content-Type', $value);

Для удобства можно воспользоваться хелпером response:

Хелпер response. Листинг 15.3


return response($content, $status)
->header('Content-Type', $value);

Если необходимо не просто изменить заголовки, но еще и вывести какой-


нибудь шаблон, можно воспользоваться методом view().

Использование метода view совместно с изменением заголовков. Листинг 15.4


return response()->view('hello')->header('Content-Type', $type);

С помощью метода withCookie можно отправлять cookie:

Метод withCookie. Листинг 15.5


return response($content)->header('Content-Type', $type)
->withCookie('name', 'value');
133
Рассмотрим входящие параметры метода withCookie:

Входящие параметры метода withCookie. Листинг 15.6


->withCookie($name, $value, $minutes, $path, $domain, $secure,
$httpOnly)

Метод JSON будет автоматически установить заголовок Content-Type к


применению / JSON, а также конвертировать данные массива в JSON с
помощью функции PHPjson_encode:

Использование метода json. Листинг 15.7


return response()->json(['name' => 'Abigail', 'state' => 'CA']);

Метод dowonload можно использовать для загрузки файлов. Входящим


параметром является путь к файлу.

Метод download. Листинг 15.8


return response()->download($pathToFile);

return response()->download($pathToFile, $name, $headers);

Перенаправления

Для перенаправления можно воспользоваться хелпером redirect с указанием


пути, куда необходимо перенаправиться:

Использование хелпера redirect. Листинг 15.9


return redirect('home/dashboard');

Для перенаправления на предыдущую страницу можно воспользоваться


хелпером back и методом withInput.

Перенаправление на предыдущую страницу. Листинг 15.10


return back()->withInput();

Перенаправление на именнованный роут.

Использование метода route для перенаправления на именованный роут.


Листинг 15.11
returnredirect()->route('login');
return redirect()->route('profile', [1]);

Перенаправление на экшн
134
Использование метода action для перенаправление на экшн. Листинг 18.12
return redirect()->action('UserController@profile', [1]);

Перенаправление с передачей сессионных данных.

Использование метода with. Листинг 15.13


return redirect('dashboard')->with('status', 'Profile updated!');

В шаблоне:

Использование сессионной переменной в шаблоне. Листинг 15.14


@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif

Макросы

Можно оформить свой вариант HTTP-ответа в виде макроса, чтобы


использовать его в других роутах или контроллерах в короткой форме.

HTTP-макросы определяются в методе boot() сервис-провайдера:

Определение макроса в методе boot() сервис-провайдера. Листинг 15.15


<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Routing\ResponseFactory;

class ResponseMacroServiceProvider extends ServiceProvider


{
/**
* Perform post-registration booting of services.
*
* @param ResponseFactory $factory
* @return void
*/
public function boot(ResponseFactory $factory)
{
$factory->macro('caps', function ($value) use ($factory) {
return $factory->make(strtoupper($value));
});
}
}

Используется макрос так:


135
Использование макроса. Листинг 15.16
return response()->caps('foo');
136

16. Модуль авторизации

Модуль авторизации поставляется совместно с фрэймворком Laravel. Файл


конфигурации авторизации находится в файле config/auth.php.

По умолчанию, для сохранения пользовательских данных, Laravel использует


модель App\User.

Также модуль поставляется с двумя контроллерами аутентификации из


коробки, которые находятся в App\HTTP\Controllers\Auth. AuthController
содержит методы регистрации нового пользователя и аутентификации, в то
время как PasswordController содержит логику помощи существующим
пользователям сбросить свои забытые пароли. Каждый из этих контроллеров
использует трэйты (traits), чтобы включить их необходимые методы. Для
многих приложений вам не нужно будет изменить эти контроллеры вообще.

Для создания шаблонов и ротов авторизации, необходимо выполнить artisan-


команду make:auth.

Создание шаблонов и роутов авторизации. Листинг 16.1


php artisan make:auth

Эта команда создаст необходимые папки с шаблонами: resources/views/auth и


resources/views/layouts, обновит файл routes.php и создаст еще один
контроллер, HomeController, куда будет перенаправляться пользователь
после успешной авторизации.
137

В модуле имеется встроенная валидация.

После успешной авторизации пользователь перенаправляется в /home. Чтобы


перенаправить пользователя на другую страницу, нужно в контроллере
AuthController добавить свойство $redirectTo.

Перенаправление пользователя после успешной авторизации. Листинг 16.2


protected $redirectTo = '/home';

Объект авторизированного пользователя мы можем получить с помощью


класса Auth.

Объект авторизированного пользователя. Листинг 16.3


138
$user = Auth::user();

Проверка, прошел ли пользователь авторизацию:

Проверка подлинности авторизации. Листинг 16.4


if (Auth::check()) {
// The user is logged in...
}

В маршрутах для авторизированных пользователей, мы можем использовать


middleware auth.

Middleware в роутах. Листинг 16.5


Route::get('profile', ['middleware' => 'auth', function() {
// Only authenticated users may enter...
}]);

// Using A Controller...

Route::get('profile', [
'middleware' => 'auth',
'uses' => 'ProfileController@show'
]);

Тот же middleware мы можем использовать в конструкторах контроллера.

Middleware в контроллерах. Листинг 16.6


public function __construct()
{
$this->middleware('auth');
}
139

17.Постраничная навигация

Применение постраничной навигации в конструкторе запросов

Постраничная навигация в конструкторе запросов. Листинг 17.1


$users=DB::table('users')->paginate(15);

Постраничная навигация в ORM-моделях:

Постраничная навигация в ORM-моделях . Листинг 17.2


$users=User::paginate(15);

$users=User::where('votes','>',100)->paginate(15);

Вывод ссылок постраничной навигации в шаблонах:

Вывод постраничной навигации в шаблонах. Листинг 17.3


{{$users->links()}}
140

18. Модуль обработки изображений

Для изменения размеров изображений можно использовать модуль Image


(http://image.intervention.io/getting_started/installation ), который необходимо
установить через composer.

Установка модуля Image. Листинг 18.1


Composer require intervention/image

Далее открываем файл config/app.php, в элемент массива $providers добавим


такую строчку:

Добавление элемента к массиву $providers. Листинг 18.2


'Intervention\Image\ImageServiceProvider'

Также, в этом файле находим массив $aliases, к которому необходимо


добавить еще один элемент:

Добавление элемента массива к массиву $aliases. Листинг 18.3


'Image' => 'Intervention\Image\Facades\Image'

Загружаем изображение из формы

Загрузка изображения из формы. Листинг 18.4


$img = Image::make(Input::file('photo'));

Теперь у нас будет доступен класс Image.

Уменьшение изображение с сохранением пропорций. Листинг 18.5


$img = Image::make(‘путь к оригинальному изображению’);
$img->resize(235, null, function($constraint) {
$constraint->aspectRatio();
});
$pic_small = 's_'.$filename;
$img->save('путькпапке/'.$pic_small);
141

19. ProfilerDebugbar

Сперва пропишем дополнительную зависимость в файл composer.json

Добавляем зависимость профайлера. Листинг 19.1


"require": {
"laravel/framework": "5.1.*",
"barryvdh/laravel-debugbar":"*"
},

Далее открываем файл config/app.php.

К массиву providers добавляем следующий элемент:

Новый элемент массива providers. Листинг 19.2


'Barryvdh\Debugbar\ServiceProvider',

К массиву aliases также добавим один элемент:

Новый элемент массива aliases. Листинг 19.3


'Debugbar' => 'Barryvdh\Debugbar\Facade',

Далее открываем консоль и обновляем зависимости через composer.

Обновление зависимостей. Листинг 19.4


Composer update

Осталось в файле app.php включить профайлер, выставив значение элемента


массива.

Включаем профайлер. Листинг 19.5


‘debug’=>true
142

20. Вспомогательные классы

Собственные классы библиотек лучше помещать в отдельную папку.


Создадим для этого в папке app/ еще одну папку Libs, в которой будут
храниться созданные нами классы.

Рассмотрим класс Imag, файл Imag.php.

Вспомагательный класс Imag. Листинг 20.1


<?php namespace App\Libs;

class Imag
{
public function __construct()
{

public function test($id = null)


{
return $id;
}

Данный класс мы можем вызвать в любом обработчике запроса


(маршрутизаторе, промежуточном программном обеспечении, контроллере и
т.д).

Вызов вспомагательного класса. Листинг 20.2


$е = \App::make('\App\Libs\Imag')->test('Урра! Работает!');
echo $е;
143

21. Понимание IOC

Инверсия управления (англ. InversionofControl, IoC) — важный принцип


объектно-ориентированного программирования, используемый для
уменьшения зацепления в компьютерных программах. Также архитектурное
решение интеграции, упрощающее расширение возможностей системы, при
котором контроль над потоком управления программы остаётся за каркасом -
ru.wikipedia.org.

Когда мы говорим об IoC в Laravel, то следует знать, что он стоит на трех


китах:

 Внедрение зависимостей (Dependency Injection)


 Сервис-контейнер (ServiceContainer)
 Отражения (Reflection)

Сначала мы поговорим о каждом из них в отдельности. А потом о том, как


они связаны между собой.

Внедрение зависимостей

Предположим, что у нас есть контроллер. И для его успешной работы ему
нужен объект некоего служебного класса (сервиса), который выполнит для
него какую-то работу. Для примера, пусть этим служебным классом будет
некий хитрый мэйлер, который посылает. Мы без труда можем создать
объект $mailer прямо в методе контроллера.

Создание объекта $mailer в методе контроллера. Листинг 21.1


namespace App\Http\Controllers

use Request;
use Mailers\Mailer;
use Models\User;
use Response
use App\Http\Controllers\Controller;

class MailController extends Controller


{
#...
public function sendMail()
{
//Создалиобъектмэйлера
$mailer = new Mailer;

$mailer
->from(Request::get('sender_id'))
->to(Request::get('receiver_id'))
->subject(Request::get('subject'));

$result = $mailer->send();
144

return Response::json($result);
}
#...
}

Теперь предположим, что у нас во многих наших методах используется


мэйлер. Логично вынести создание объекта $mailer в конструктор.

Вынос объекта $mailer в конструктор. Листинг 21.2


namespace App\Http\Controllers

use Request;
use App\Mailers\Mailer;
use App\Models\User;
use Response
use App\Http\Controllers\Controller;

class MailController extends Controller


{
#...
public function __construct()
{
//вынесли создание объекта мэйлера в метод-конструктор
$this->mailer = new Mailer;
}

public function sendMail()


{
$this->mailer
->from(Request::get('sender_id'))
->to(Request::get('receiver_id'))
->subject(Request::get('subject'));

$result = $this->mailer->send();

return Response::json($result);
}
#...
}

Допустим, вы заменяете класс App\Mailers\Mailer на App\Mailers\LocalMailer,


но тут есть два "но". Во-первых, нет никаких гарантий, что новый
LocalMailer имеет те же публичные методы для работы. Во-вторых, если
этот старый мэйлер используется в куче различных контроллеров, то вам
придется руками заменить его везде, где он создается. Вот здесь к нам на
помощь и приходят интерфейсы и внедрение зависимостей.

Для гарантии того, что оба мэйлера имеют одинаковые публичные методы,
нужно определить интерфейс, которому они должны удовлетворять.

Создание класса интерфейса. Листинг 21.3


namespace App\Mailers\Contracts;
145
class MailerInterface {

public function from();

public function to();

public function subject();

public function send();

и сами мэйлеры должны реализовывать этот интерфейс:

Реализация интерфейса. Листинг 21.4


namespace App\Mailers;

use App\Mailers\Contracts\MailerInterface;

class Mailer implements MailerInterface {


#реализацияинтерфейса
}

После таких манипуляций наш контроллер может одинаково хорошо


работать с любым полученным в конструкторе мэйлером вне зависимости от
конкретной его реализации локальной или почтовой.

Внедрение зависимости в конструктор класса. Листинг 21.5


public function __construct(MailerInterface $mailer)
{
$this->mailer = $mailer;
}

Внедрение этой зависимости происходит снаружи и контроллеру вообще не


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

Обычное внедрение зависимости снаружи класса выглядит так:

Внедрение зависимостей. Листинг 21.6


use App\Mailers\Mailer;
use App\Mailers\LocalMailer;

#...
$controller = new MailController(new LocalMailer);
//или
$controller = new MailController(new Mailer);

Это и называется DependencyInjection.

Сервис-контейнер
146
Сервис-контейнер в Laravel решает три основных задачи:

 Он может хранить информацию о том, как получить данные;


 Он может хранить данные, объект или информация о том, как его
получить (замыкание или имя класса);
 Он может разрешать зависимости.

Связывание

Связывание. Листинг 21.7


$this->app->bind('some', 'App\SomeClass');

Теперь, каждый раз, когда мы будем обращаться к сервис-контейнеру таким


образом:

Обращение к сервис-контейнеру. Листинг 21.8


$some = $this->app->make('some');

Он будет возвращать вновь созданный объект класса App\SomeClass.

Если мы хотим, чтобы сервис-контейнер не только возвращал нам объект, но


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

Обращение к сервис-контейнеру с использованием замыкания. Листинг 21.9


$this->app->bind('some', function($app){
$some = new \App\SomeClass('argument_1', 'argument_2');
$some->setSomething('example');
return $some;
});

Обратите внимание, что замыкание единственным аргументом принимает


объект класса Illuminate\Foundation\Application, который и является этим
самым сервис-контейнером, и он (сервис-контенер) также доступен через
алиас фасада App, хелпер app(), а также как $this->app во многих классах
Laravel. Таким образом, внутри замыкания можно вызывать данные из
контейнера, которые были определены ранее. Например, так:

Вызов данных из контейнера внутри замыканий. Листинг 21.10


$this->app->bind('some', function($app){
return new \App\SomeClass($app->make('some.else'));
});

Singleton
147
Следующий пример делает точно то же самое, что и предыдущий, однако
объект класса будет создан однажды - при первом вызове, а все
последующие обращения к тому же контейнеру будут возвращать созданный
ранее объект:

Использование метода singleton. Листинг 21.11


$this->app->singleton('some', 'App\SomeClass');

Instance

Связывание инстансов (экземпляров) ведет себя точно так же, как и


связывание одиночек, с той лишь разницей, что объект в контейнере
создается не в момент первого вызова, а еще до помещения в контейнер.

Связывание экземпляров. Листинг 21.12


$some = new Some;
$this->app->instance('some', $some);

Здесь нет необходимости в замыканиях, так как всё необходимое


пресетирование можно сделать еще до помещения в контейнер.

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


помещен, просто по имени класса,

Вызов объекта из контейнера, который не был туда помещен. Листинг 21.13


$this->app->make('App\SomeClass');

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


поместит этот объект в себя. После чего он (объект) будет доступен также
как singleton или instance, и все последующие вызовы будут возвращать тот
же объект.

Выводы

Внедрение зависимостей. Это когда мы не думаем о том, с чем мы


работаем, а думаем лишь о том, как с этим можно работать. А что именно это
будет - определяется вне контекста исполнения.

Сервис контейнер. Он обеспечивает доступ к объектам или информации о


том, как их правильно создавать.

Рефлексии. Они позволяют нам извлечь информацию о том, что хочет


получить объект, класс или замыкание.
148
Каждый раз, когда мы вызываем из контейнера что-то,

Обращение к классу зависимости. Листинг 21.14


$this->app->make('App\SomeClass');

для создания чего не было определено замыкания (то есть: он либо был
привязан без замыкания, по имнени класса bind/singleton, либо вообще не
был помещен в контейнер и вызывается просто по имени класса ), контейнер
с помощью рефлексии исследует конструктор этого класса и попытается
разрешить эти зависимости.

В начале главы мы говорили про mailer. И у нас в конструкторе была


определена зависимость:

Определение зависимости от интерфейса. Листинг 21.15


public function __construct(MailerInterface $mailer)
{
$this->mailer = $mailer;
}

Контроллеры в Laravel создаются через этот самый App::make(), а это


означает что все зависимости будут по возможности разрешены. Ну а так
как, в нашем случае, мы используем для типизации интерфейс (и, как мы
помним из мануала php, экземпляры интерфейсов и абстрактных классов
созданы быть не могут), а не конкретный класс, то нам в сервис провайдере
нужно определить разрешение для этой зависимости:

Определение зависимости в сервис-провайдере. Листинг 21.16


$this->bind('App\Mailers\Contracts\MailerInterface',
'App\Mailers\LocalMailer');

И теперь во всех контроллерах и классах, созданных через App::make(), где


будет внедрен MailerInterface, для разрешения зависимости будет поставлен
LocalMailer. И если нам когда-то взбредет в голову поменять его на что-то
иное, то нам будет достаточно заменить его в нашем сервис-провайдере на
то, что нам нужно. Само собой разумеется, что это "что нужно" должно
реализовывать MailerInterface.

Но нам не всегда нужно или хочется городить интерфейсы. Если нам


достаточно получить объект какого-то конкретного класса, то мы можем
просто типизировать аргумент этим классом, и контейнер попытается создать
его через App::make():

Типизирование аргумента классом. Листинг 21.17


public function __construct(LocalMailer $mailer)
{
149
$this->mailer = $mailer;
}

В этом случае, нам вообще не нужно прописывать привязку в сервис-


провайдере.
150

22. ServiceProviders

Основная задача поставщика услуг, или ServiceProviders – это загрузка


вспомогательных, в том числе, и собственных классов.

Если открыть конфигурационный файлa app.php, то мы увидим там


множество провайдеров, определенных в массиве providers. Это все классы,
которые будут загружены для приложения. Многие из них являются
отложенными, что означает, что они не будут загружаться при каждом
запросе, а только тогда, когда они действительно необходимы, т.е. когда они
будут вызываться.

Все провайдеры должны наследоваться от класса


Illuminate\Support\ServiceProvider. Данный абстрактный класс требует, чтобы
был определен хотя бы один метод класса-провайдера: register. В этом
методе регистрируется вспомогательный класс.

Сам провайдер можно создать с помощью artisan:

Создание провайдера с помощью artisan. Листинг 22.1


php artisan make:provider PhpqueryServiceProvider

В папке providers/ появился файл PhpqueryServiceProvider.

Метод Register()

Откроем метод register и зарегистрируем новый класс:

Регистрация нового класса провайдера. Листинг 22.2


public function register()
{
$this->app->singleton('phpQuery\phpQuery', function($app)
{
return new Connection($app['config']['riak']);
});
}

В register() мы регистрируем (bind) как singleton, т.е. класс не будет


переинициализироваться после вызова из контейнера.

Слушатели событий, роуты и фильтры роутов здесь регистрировать нельзя.

Метод boot()

Пространство имени App\Providers, в котором находится этот класс сервис-


провайдера, - место для хранения сервис-провайдеров нашего Laravel-
151
приложения, но мы можете располагать свои сервис-провайдеры где угодно
внутри PSR-4 папки (если вы не меняли composer.json, то это папка app).

Когда вызвались методы register() всех сервис-провайдеров приложения,


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

Метод boot сервис-провайдеров. Листинг 22.3


public function boot(Dispatcher $events)
{
Event::listen('SomeEvent', 'SomeEventHandler');
}

Регистрация провайдеров

Провайдеры регистрируются в файле config/app.php

Регистрация провайдеров в файле confgig/app.php. Листинг 22.5


'providers' => [
// другиесервис-провайдеры
'App\Providers\AppServiceProvider',
],

Отложенные service providers.

Если провайдер только регистрирует классы в сервис-контейнере, то вы


можете отложить вызов его метода register() до момента, когда эти классы
будут затребованы из сервис-контейнера. Это позволит не дергать файловую
систему каждый запрос в попытках загрузить файл с нужным классом с
диска.

Для того, чтобы сделать сервис-провайдер отложенным, установите свойство


defer в true и определите метод providers(), чтобы фреймворк знал, какие
классы биндятся (регистрируются в сервис-контейнере) в провайдере.

Отложенный сервис-провайдер. Листинг 22.6


<?php namespace App\Providers;

use Riak\Connection;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider {

protected $defer = true;

public function register()


{
$this->app->singleton('Riak\Contracts\Connection',
function($app)
152
{
return new Connection($app['config']['riak']);
});
}

public function provides()


{
return ['Riak\Contracts\Connection'];
}

}
153

23. ServiceConteiner

ServiceContainer (сервис-контейнер, ранее IoC-контейнер) - это мощное


средство для управлением зависимостями классов. В современном мире веб-
разработки есть такой модный термин - DependencyInjection, "внедрение
зависимостей", он означает внедрение неких классов в создаваемый класс
через конструктор или метод-сеттер. Создаваемый класс использует эти
классы в своей работе. Сервис-контейнер реализует как раз этот функционал.

Несколько упрощая, можно сказать так: когда фреймворку нужно создать


класс, он применяет не конструкцию newSomeClass(newSomeService()) , а
App::make("SomeClass"), предварительно зарегистрировав функцию,которая
создает классSomeClassи все классы, которыеSomeClass` принимает в
качестве аргументовконструктора.

Вот простой пример:

Класс Registrar. Листинг 23.1


<?php namespace App\Users;
use App\User;
use Illuminate\Contracts\Mail\Mailer;
class Registrar {
/**
* The mailer implementation.
*/
protected $mailer;
/**
* Create a new user registrar instance.
*
* @param Mailer $mailer
* @return void
*/
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
/**
* Register a new user with the application.
*
* @param array $input
* @return User
*/
public function registerNewUser(array $input)
{
//
}
}

В этом примере нам нужно в классе Registrar, который регистрирует


154
пользователей, написать отправку мейла пользователю для подтверждения
регистрации. Так как мы хотим соблюдать первый принцип SOLID -
"Принцип разделения ответственности", мы не пишем в нем код общения с
SMTP-сервером и т.п., а встраиваем, инжектим (inject) в него класс отправки
мейлов. Преимущество такого подхода - не изменяя код класса Registrar, мы
можем легко сменить способ отправки почты, например, с сервиса Mailchip
на Mailjet или другой, и для тестирования можем вообще подать класс-
заглушку.

Сервис-контейнер - очень важная вещь, без него невозможно построить


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

Binding (связывание, регистрация)

Так как практически все биндинги, т.е. соответствие строкового ключа


реальному объекту в контейнере в вашем приложении будут
регистрироваться в методе register() сервис-провайдеров, все
нижеследующие примеры даны для этого контекста.

Регистрация обычного класса

Внутри сервис-провайдера, экземпляр контейнера находится в $this->app.


Зарегистрировать (bind, связать) класс можно двумя путями - при помощи
коллбэк-функции или привязки интерфейса к реализации.

Рассмотрим первый способ. Коллбэк регистрируется в сервис-контейнере


под неким строковым ключем (в данном случае FooBar) - обычно для этого
используют название класса, который будет возвращаться этим коллбэком:

Bind. Листинг 23.2


$this->app->bind('FooBar', function($app)
{
return new FooBar($app['SomethingElse']);
});

Когда из контейнера будет запрошен объект по ключу FooBar, контейнер


создаст объект класса FooBar, в констркуторкоотрого в качестве аргумента
добавит объект из контейнера с ключом SomethingElse.

Регистрация singleton

Иногда вам нужно, чтобы объект создавался один раз, а все остальные разы,
когда вы запрашиваете его, вам возвращался тот же созданный экземпляр. В
этом случае вместо bind используйте singletone:
155
Singleton. Листинг 23.3
$this->app->singleton('FooBar', function($app)
{
return new FooBar($app['SomethingElse']);
});

Вы можете добавить в контейнер существующий экземпляр класса:

Добавление существующего экземпляра класса в контейнер. Листинг 23.4


$fooBar = new FooBar(new SomethingElse);
$this->app->instance('FooBar', $fooBar);

Есть несколько способов получить (resolve) содержимое контейнера. Во-


первых, вы можете использовать метод make():

Получение из контейнера. Листинг 23.5


$fooBar = $this->app->make('FooBar');

Во-вторых, вы можете обратиться к контейнеру как к массиву:

Обращение к контейнеру, как к массиву. Листинг 23.6


$fooBar = $this->app['FooBar'];

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


конструкторе класса, который сам ресолвится (получается) из контейнера (в
примере ниже это UserInterface), и фреймворк возьмет его из контейнера сам
(в данном случае по ключу 'UserInterface').

Связывание в конструкторе. Листинг 23.7


<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Users\Repository as UserRepository;
class UserController extends Controller {
/**
* The user repository instance.
*/
protected $users;
public function __construct(UserRepository $users)
{
$this->users = $users;
}
public function show($id)
{
//
}
}

Связывание интерфейса с реализацией


156
Особенно интересная и мощная возможность сервис-контейнера - связывать
интерфейсы с различными их реализациями. Например, наше приложение
использует Pusher для отправки и приема реал таймовых сообщений. Если
мы используем Pusher PHP SDK, мы должны инжектить экземпляр класса
PusherClient в наш класс:

Внедрение зависимостей. Листинг 23.8


<?php namespace App\Orders;
use Pusher\Client as PusherClient;
use App\Orders\Commands\CreateOrder;
class CreateOrderHandler {
/**
* The Pusher SDK client instance.
*/
protected $pusher;
public function __construct(PusherClient $pusher)
{
$this->pusher = $pusher;
}
public function execute(CreateOrder $command)
{
//
}
}

Таким образом, наш код становится завязанным на конкретный сервис -


Pusher. Если в дальнейшем мы заходим его сменить, или просто Pusher
сменит названия методов в своем SDK, мы будем вынуждены менять код в
нашем классе CreateOrderHandler.

Для того, чтобы "изолировать" класс CreateOrderHandler от постоянно


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

От класса к интерфейсу. Листинг 23.9


<?php namespace App\Contracts;
interface EventPusher {
/**
* Push a new event to all clients.
*
* @param string $event
* @param array $data
* @return void
*/
public function push($event, array $data);
}

Когда мы создадим реализацию (implementation, имплементацию) этого


интерфейса, PusherEventPusher, мы можем связать её с интерфейсом в методе
register() сервис-провайдера:

Связывание интерфейса с классом в методе register()сервис-провайдера.


157
Листинг 23.10
$this->app->bind('App\Contracts\EventPusher',
'App\Services\PusherEventPusher');

Здесь мы говорим фреймворку, что, когда из контейнера будет запрошен


EventPusher, вместо него отдавать реализацию этого интерфейса,
PusherEventPusher. Теперь мы можем переписать наш конструктор
классаСreateOrderHandler следующим образом:

Обращение к интерфейсу. Листинг 23.11


public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}

Теперь, с какой бы реализацией работы реалтаймовых сообщений мы бы ни


работали, изменять код в CreateOrderHandler нам не потребуется.

Иногда у вас может быть несколько реализаций одного интерфейса, и вы


хотите включать (инжектить) их каждый всвой класс. Например, когда
делается новый заказ, вам нужно отправлять сообщение в PubNub вместо
Pusher. Выможете сделать это следующим образом:

Контекстное связывание. Листинг 23.12


$this->app->when('App\Orders\CreateOrderHandler')
->needs('App\Contracts\EventPusher')
->give('App\Services\PubNubEventPusher');

Иногда вам может потребоваться ресолвить реализации в определенной


категории. Например, вы пишете сборщик отчетов, который принимает на
вход массив различных реализаций интерфейса Report. Вы можете
протэгировать ихследующим образом:

Тэгирование. Листинг 23.13


$this->app->bind('SpeedReport', function()
{
//
});
$this->app->bind('MemoryReport', function()
{
//
});
$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');
Теперь вы можете получить их все сразу по тэгу:
$this->app->bind('ReportAggregator', function($app)
{
return new ReportAggregator($app->tagged('reports'));
});

Laravel предлагает несколько возможностей использования сервис-


158
контейнера для повышения гибкости итестируемости вашего кода. Один их
характерных примеров - реализация Dependency Injection в контроллерах.
Laravelрегистрирует все контроллеры в сервис-контейнере и поэтому при
получении (resolve) класса контроллера изконтейнера, автоматически
ресолвятся все зависимости, указанные в аргументах конструктора и других
методов контроллера.

Использование на практике. Листинг 23.14


<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Repositories\OrderRepository;
class OrdersController extends Controller {
/**
* The order repository instance.
*/
protected $orders;
/**
* Create a controller instance.
*
* @param OrderRepository $orders
* @return void
*/
public function __construct(OrderRepository $orders)
{
$this->orders = $orders;
}
/**
* Show all of the orders.
*
* @return Response
*/
public function index()
{
$all = $this->orders->all();
return view('orders', ['all' => $all]);
}
}

В этом примере OrderRepository будет автоматически создан и подан в


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

Разумеется, контроллеры - не единственные классы, которые фреймворк


берет из сервис-контейнера. Вы можетеиспользовать этот же принцип в
коллбэках роутов, обработчиках событий, обработчиках очередей и т.д.
Примеры использования сервис-контейнера приведены в соответствующих
разделах документации.

События контейнера
159
Сервис-контейнер запускает событие каждый раз, когда объект извлекается
из контейнера. Можно ловить все события, можно только те, которые
привязыны к конкретному ключу.

Регистрация события на извлечение объекта из контейнера. Листинг 23.15


$this->app->resolvingAny(function($object, $app)
{
//
});
$this->app->resolving('FooBar', function($fooBar, $app)
{
//
});

Объект, получаемый из контейнера, передается в функцию-коллбэк.


160

24.Фасады

Фасады предоставляют "статический" интерфейс (Foo::bar()) к классам,


доступным в IoC-контейнере. Laravel поставляется со множеством фасадов и
вы, вероятно, использовали их, даже не подозревая об этом. Иногда вам
может понадобиться создать собственные фасады для вашего приложения и
пакетов (packages), поэтому давайте изучим идею, разработку и
использование этих классов.

В контексте приложения на Laravel, фасад - это класс, который предоставляет


доступ к объекту в контейнере. Весьэтот механизм реализован в классе
Facade. Фасады как Laravel, так и ваши собственные, наследуют этот
базовыйкласс.Ваш фасад должен определить единственный метод:
getFacadeAccessor. Его задача - определить, что вы хотитеполучить из
контейнера. Класс Facade использует магический метод PHP __callStatic() для
перенаправлениявызовов методов с вашего фасаде на полученный объект.

Например, когда вы вызываете Cache::get(), Laravel получает объект


CacheManager из IoC-контейнера и вызываетметод get этого класса. Другими
словами, фасады Laravel предоставляют удобный синтаксис для
использования IoC-контейнера в качестве сервис-локатора (service locator).

Практическое использование

В примере ниже делается обращение к механизму кэширования Laravel. На


первый взгляд может показаться, чтометод get принадлежит классу Cache.

Обращение к механизму кэширования. Листинг 24.1


$value = Cache::get('key');

Однако, если вы посмотрите в исходный код класса


Illuminate\Support\Facades\Cache, то увидите, что он несодержит метода get.

Класс Cache наследует класс Facade и определяет метод getFacadeAccessor().


Как вы помните, его задача -вернуть строковое имя (ключ) привязки объекта
в контейнере IoC.

Когда вы обращаетесь к любому статическому методу фасада Cache, Laravel


получает объект cache из IoC и вызывает на нём требуемый метод (в этом
случае - get).

Таким образом, вызов Cache::get может быть записан так:


161
Вызов Cache::get с помощью $app->make(). Листинг 24.2
$value = $app->make('cache')->get('key');

Создать фасад в вашем приложении или пакете (package) довольно просто.


Вам нужны только три вещи:

1. Биндинг (привязка) в IoC.

2. Класс-фасад.

3. Настройка для псевдонима фасада.

Посмотрим на следующий пример. Здесь определён класс


PaymentGateway\Payment.

Определение класса Payment. Листинг 24.3


namespace PaymentGateway;
class Payment {
public function process()
{
//
}
}

Этот класс может находиться в app/models или в любой другой директории,


где у composer настроена автозагрузка классов.

Нам нужно, чтобы этот класс извлекался из контейнера IoC, так что давайте
добавим для него привязку (binding):

Привязка класса. Листинг 24.4


App::bind('payment', function()
{
return new \PaymentGateway\Payment;
});

Самое лучшее место для регистрации этой связки - новый сервис-провайдер


который мы назовём PaymentServiceProviderи в котором мы создадим метод
register, содержащий код выше. После этого вы можетенастроить Laravel для
загрузки этого поставщика в файле app/config/app.php.

Дальше мы можем написать класс нашего фасада:

Класс фасада. Листинг 24.5


use Illuminate\Support\Facades\Facade;
class Payment extends Facade {
protected static function getFacadeAccessor() {
return 'payment';
}
162
}

Наконец, по желанию можно добавить алиас (alias, псевдоним) для этого


фасада в массив aliases файла настроек app/config/app.php- тогда мы сможем
вызывать метод process на классе Payment.

Обращение к фасаду после добавления alias-а в конфиге. Листинг 24.6


Payment::process();
163

25. Контракты

Контракты в Laravel - это набор классов-интерфейсов, определяющий некий


функционал ядра фреймворка. Например, контракт Queue определяет методы
работы с очередями, Mailer - методы для отправки мейлов. Каждый контракт
имеет свою реализацию (implementation) во фреймворке. Например, есть
реализация Queue сразличными драйверами очередей и реализация Mailer с
использованием SwiftMailer.

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


https://github.com/illuminate/contracts .

Слабая связанность и упрощение кода – основные причины использования


контрактов.

Рассмотрим пример со слабой связанностью

Слабая связанность. Листинг 25.1


<?php namespace App\Orders;
class Repository {
protected $cache;
public function __construct(\SomePackage\Cache\Memcached
$cache)
{
$this->cache = $cache;
}
public function find($id)
{
if ($this->cache->has($id))
{
//
}
}
}

Этот класс тесно связан с реализацией кэширования


\SomePackage\Cache\Memcached. Мы зависим и от способа кэширования
(memcached), и от API данной библиотеки. Если мы хотим сменить
кэширование с memcached на redis, нам придется вносить изменения в код
класса Repository.

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


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

Зависимость от интерфейса кэширования. Листинг 25.2


<?php namespace App\Orders;
164
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository {
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}

Этот код не связан ни с одной внешней библиотекой, в том числе с ядром


фреймворка! Контракт не содержит никакой конкретной реализации
кэширования, только интерфейс, и вы можете написать любую свою
реализацию кэширования, используя внешние библиотеки или нет. Кроме
того, теперь вы можете в любой момент легко изменить способ кэширования,
просто подав другую реализацию в конструктор класса при регистрации его в
сервис-контейнере в методе register() вашего сервис-провайдера.

Упрощение кода

Когда все сервисы ядра фреймворка аккуратно определены в простых


интерфейсах, очень легко определить, что именно делает тот или иной
сервис. Фактически, контракты являются краткой документацией к API
Laravel.

Кроме того, когда вы у себя в приложении внедряете в классы зависимости


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

Использование контрактов

Как получить в своем классе реализацию заданного контракта? Очень


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

Например, рассмотрим слушателя событий:

Слушатель событий. Листинг 25.3


<?php namespace App\Events;
use App\User;
use Illuminate\Contracts\Queue\Queue;
class NewUserRegistered {
165
/**
* The queue implementation.
*/
protected $queue;
/**
* Create a new event listener instance.
*
* @param Queue $queue
* @return void
*/
public function __construct(Queue $queue)
{
$this->queue = $queue;
}

public function fire(User $user)


{
// Queue an e-mail to the user...
}
}
166

26. События

Events в Laravel позволяют подписываться на события, которые


генерируются на стороне сервера.

3 действия:

1 Создать само событие.

2. Создать обработчик события или подписаться на созданное событие.

3. Вызывать событие.

Классы событий находятся в папке app/Events. Классы обработчиков (или


прослушивателей) событий – в папке app/Listeners. А вызывать событие
можно в любом месте вашего проекта.

Создание события

Команда artisan: создать класс события. Листинг 26.1


php artisan make:event PodcastWasPurchased

Подписка на события

Сервис-провайдер EventServiceProvider - это удобное место для регистрации


классов слушателей событий. В массиве listen перечисляются названия
события (в ключе массива) и название класса, который его обрабатывает.
Например, для нашего PodcastWasPurchased:

Подписка на события. Листинг 26.2


protected $listen = [
'App\Events\PodcastWasPurchased' => [
'App\Handlers\Events\EmailPurchaseConfirmation@handle',
],
];

Для генерации исполнителя (handler) события используйте artisan-команду


handler:event:

Команда handler:event. Листинг 26.3


php artisan handler:event EmailPurchaseConfirmation
--event=PodcastWasPurchased

Вызов события

Запускать события можно так:


167
Запуск события. Листинг 26.4
use Event;
use App\Events\TimeEvent;
Event::fire(new TimeEvent());

Вместо фасада Event можно использовать хелпер:

Использование хелпера event для запуска события. Листинг 26.5


event(new PodcastWasPurchased($podcast));

Слушатели в функциях замыканиях

Чтобы упростить себе работу и не создавать два класса, вы можете создать


слушателя в виде функции-замыкания. Сделать это можно в методе boot
сервис-провайдера EventServiceProvider:

Прослушиватель событий в функции замыкании. Листинг 26.6


Event::listen('App\Events\PodcastWasPurchased', function($event)
{
// Обработка события...
});

Event:generate

Использование двух команд - make:event и handler:event - каждый раз, когда


вам нужно создать обработчик события - это неудобно. Вместо этого, можно
заполнить массив listen в EventServiceProvider:

Массив listen. Листинг 26.7


protected $listen = [
'App\Events\UserLogin' => [
'App\Listeners\UserLoginListener',
],
];

И выполнить artisan-команду event:generate. Эта команда анализирует listen и


создает все необходимые классы событий и обработчиков событий:

Event:generate. Листинг 26.8


php artisan event:generate

События моделей

Модели Eloquent инициируют несколько событий, что позволяет вам


добавить к ним свои обработчики с помощью следующих методов: creating,
created, updating, updated, saving, saved, deleting, deleted, restoring, restored.
168
Когда первый раз сохраняется новая модель, возникают события creating и
created. Если модель уже существовала на момент вызова метода save,
вызываются события updating и updated. В обоих случаях также возникнут
события saving и saved.

Регистрировать слушателей событий модели можно в вашем сервис-


провайдере EventServiceProvider. Например,

Регистрация событий. Листинг 26.9


public function boot(DispatcherContract $events)
{
parent::boot($events);

User::creating(function($user)
{
dd($user);
});
}

Подписчики

Подписчики на события (Event Subscribers) - классы, которые могут быть


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

Класс подписчик событий. Листинг 26.10


class UserEventHandler {

/**
* Событиеlogin.
*/
public function onUserLogin($event)
{
//
}

/**
* Событие logout.
*/
public function onUserLogout($event)
{
//
}

/**
* Регистрация подписчиков в методеsubscriber.
*
* @param Illuminate\Events\Dispatcher $events
* @return array
*/
public function subscribe($events)
{
169
$events->listen('App\Events\UserLoggedIn',
'UserEventHandler@onUserLogin');

$events->listen('App\Events\UserLoggedOut',
'UserEventHandler@onUserLogout');
}

После того как класс определён, его можно зарегистрировать следующим


образом:

Регистрация подписчика. Листинг 26.11


$subscriber = new UserEventHandler;

Event::subscribe($subscriber);

Пример использования событий

У нас имеется таблица users, которой соответсвует следующая модель

Модель User. Листинг 26.12


<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as
AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as
AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as
CanResetPasswordContract;

class User extends Model implements


AuthenticatableContract,AuthorizableContract,CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;

protected $table ='users';

protected $fillable = ['username', 'password', 'blocked'];

protected $hidden = ['password', 'remember_token'];


}
170

Наша задача: когда из админки блокируем пользователя, полю blocked


присваиваем значение 1 или, наоборот, при разблокировке blocked
присваиваем 0. Мы должны записывать данные действие в лог. И это мы
реализуем с помощью event.

Есть контроллер UserController, в котором есть два метода – один блокирует


пользователя, второй разблокирует пользователя.

Метод блокировки пользователя:

Метод lock. Листинг 26.13


public function lock($id)
{
$user = User::find($id);
if($user){
$user->blocked = 1;
$user->save();
return redirect()->back()->with('message','saved');
}
return redirect()->back()->with('errMessage','error');
}

Рассмотрим подробнее этот метод. В метод lock передается параметр $id, где
$id – это ID пользователя, которого мы хотим заблокировать. Дальше мы
ищем пользователя по его ID, необходимо для того, чтобы избежать
блокировки несуществующего пользователя. Если пользователь найден, то
атрибуту blocked присваиваем значение 1 и сохраняем. Дальше редиректим
обратно с сообщением «saved».

И второй метод разблокировки пользователя:

Метод unlock. Листинг 26.14


public function unlock($id)
{
$user = User::find($id);
if($user){
$user->blocked = 0;
$user->save();
return redirect()->back()->with('message','saved');
}
return redirect()->back()->with('errMessage','error');
}

Данный метод аналогичен предыдущему, кроме того, что мы атрибуту


blocked присваиваем значение 0.

Теперь можно перейти к решению нашей задачи. Первое - создадим таблицу


logs со следующей структурой:

id
171
user_id

data

created_at

Где user_id – ID пользователя, который осуществил блокировку (мы же в


админке работаем только авторизованным пользователем), data – это поле в
которое мы будем записывать «ID: $id Status: $blocked» $id – это ID
блокируемого или разблокируемого пользователя, а $blocked – это статус: 1-
заблокирован, 0 — не заблокирован.

Теперь перейдем к созданию event в Laravel 5. Для этого в начале откроем


файл app/Providers/EventServiceProvider.php.

И в свойстве $listen добавим следующий код

Свойство $listen файла EventServiceProvider. Листинг 26.15


'App\Events\AddLogs' =>
[
'App\Handlers\Events\Log@handle',
],

Мы добавили в массив ключ и значение, где ключем является событие (event)


AddLogs, а элементом массив его слушателе (listener). В нашем случае
массив состоит из одного слушателя Log.

Теперь запустим команду artisan для генерации файлов события (event) и


слушателя (listener):

Генерация файлов события. Листинг 26.16


php artisan event:generate

У нас появились два файла: App\Events\AddLogs.php и


App\Handlers\Events\Log.

Файл AddLogs.php. Листинг 26.17


<?php
namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class AddLogs extends Event


{
use SerializesModels;
/**
* Create a new event instance.
*
172
* @return void
*/
public function __construct()
{

}
/**
* Get the channels the event should be broadcast on.
*
* @return array
*/
public function broadcastOn()
{
return [];
}
}

Изменим его, с учетом того, что в наше событие надо передать два параметра
$user_id и $data, которые event передаст дальше слушателю.

Измененный файл события AddLogs.php. Листинг 26.18


<?php
namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class AddLogs extends Event


{
use SerializesModels;
public $user_id;
public $data;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($user_id,$data)
{
$this->user_id = $user_id;
$this->data = $data;
}
/**
* Get the channels the event should be broadcast on.
*
* @return array
*/
public function broadcastOn()
{
return [];
}
}

Рассмотрим файл слушателя событий.

Слушатель событий, файл Log.php. Листинг 26.19


173
<?php
namespace App\Handlers\Events;
use App\Events\AddLogs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class Log
{
/**
* Create the event handler.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param AddLogs $event
* @return void
*/
public function handle(AddLogs $event)
{

}
}

Изменим файл слушателя.

Измененный файл слушателя Log.php. Листинг 26.20


<?php
namespace App\Handlers\Events;
use App\Log;
use App\Events\AddLogs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class Log
{
/**
* Create the event handler.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param AddLogs $event
* @return void
*/
174
public function handle(AddLogs $event)
{
Adminlog::create([
'user_id' => $event->user_id,
'data' => $event->data,
]);
}
}

Наш event готов к использованию.

Давайте вернемся к контроллеру UserController и поправим методы lock и


unlock – запустим наше событие. Это можно сделать при помощи метода fire
фасада Event.

Метод блокировки:

Метод lock. Листинг 26.21


public function lock($id)
{
$user = User::find($id);
if($user){
$user->blocked = 1;
$user->save();
//вызываем event
Event::fire(new AddLogs(Auth::user()->id,
'ID: '.$user->id.' Status: 1'));
return redirect()->back()->with('message','saved');
}
return redirect()->back()->with('errMessage','error');
}

Метод разблокировки

Метод unlock. Листинг 26.22


public function unlock($id)
{
$user = User::find($id);
if($user){
$user->blocked = 0;
$user->save();
//вызываем event
Event::fire(new AddLogs(Auth::user()->id,'ID: '.$user->id.'
Status: 0'));
return redirect()->back()->with('message','saved');
}
return redirect()->back()->with('errMessage','error');
}

Проверяем.
175

27.Кэш

Laravel предоставляет унифицированное API для различных систем


кэширования. Настройки кэша содержатся в файле config/cache.php. Там вы
можете указать драйвер, который будет использоваться для кэширования.
Laravel "из коробки" поддерживает многие популярные системы, такие как
Memcached и Redis.

Этот файл также содержит множество других настроек, которые в нём же


документированы, поэтому обязательн ознакомьтесь с ними. По умолчанию,
Laravel настроен для использования драйвера file, который хранит
сериализованные объекты кэша в файловой системе. Для больших
приложений рекомендуется использование систем кэширования в памяти -
таких как Memcached или APC.

Запись нового элемента в кэш. Листинг 27.1


Cache::put('key', 'value', $minutes);

Для установки времени жизни кэша, мы можем использовать carbon.

Использование объектов carbon. Листинг 27.2


$expiresAt = Carbon::now()->addMinutes(10);
Cache::put('key', 'value', $expiresAt);

Запись элемента, если он не существует

Запись элемента, если он не существует. Листинг 27.3


Cache::add('key', 'value', $minutes);

Метод add возвращает true, если производится запись элемента в кэш. Иначе,
если элемент уже есть в кэше, возвращается false.

Проверка элемента на существование. Листинг 27.4


if (Cache::has('key'))
{
//
}

Чтение элемента из кэша по имени

Обращение к элменту кэша по имени. Листинг 27.5


$value = Cache::get('key');
176
Чтение элемента и возвращение значения по умолчанию

Определение значения по умолчанию. Листинг 27.6


$value = Cache::get('key', 'default');
$value = Cache::get('key', function() { return 'default'; });

Запись элемента на постоянное хранение

Запись элемента на постоянное хранение. Листинг 27.7


Cache::forever('key', 'value');

Иногда вам может понадобиться получить элемент из кэша или сохранить


его там, если он не существует. Вы можете сделать это методом
Cache::remember:

Использование Cache::remember. Листинг 27.8


$value = Cache::remember('users', $minutes, function()
{
return DB::table('users')->get();
});

Можно совместить remember и forever:

Совмещениеrememberи forever. Листинг 27.9


$value = Cache::rememberForever('users', function()
{
return DB::table('users')->get();
});

Все кэшируемые данные сериализуются, поэтому вы можете хранить любые


типы данных.

Если понадобится получить элемент из кэша, а потом удалить его, можно


воспользоваться методом pull:

Изъятие элемента из кэша . Листинг 27.10


$value = Cache::pull('key');

Удаление элемента из кэша

Удаление элемента. Листинг 27.11


Cache::forget('key');

Все драйверы, кроме file и database, поддерживают операции инкремента и


декремента.
177

Операции инкремента. Листинг 27.12


//Увеличение числового значения:
Cache::increment('key');
Cache::increment('key', $amount);
//Уменьшение числового значения:
Cache::decrement('key');
Cache::decrement('key', $amount);

Тэги кэша

Примечание: тэги кэша не поддерживаются драйверами file и database.


Кроме того, если вы используете мультитэги для "вечных" элементов кэша
(сохраненных как forever), наиболее подходящим с точки зрения
производительности будет драйвер типа memcached, который
автоматически очищает устаревшие записи.

При помощи тэгов вы можете объединять элементы кэша в группы, а затем


очищать всю группу целиком по названиютэга. Для работы с кэшем с тэгами
используйте метод tags. Элементу кэша можно присвоить один или
несколько тэгов кэша. Список тэгов можно указать либо, перечислив через
запятую, либо массивом:

Использование тэгов кэша. Листинг 27.13


Cache::tags('people', 'authors')->put('John', $john, $minutes);
Cache::tags(array('people', 'authors'))->put('Anne', $anne,
$minutes);

Любой метод для записи в кэш можно использовать в связке с тэгами,


включая remember, forever и rememberForever. Элемент кэша также можно
получить из кэша с тэгом, так же, как и использовать другие методы кэша,
такие как increment и decrement.

Чтобы получить элемент кэша, вы должны указать все тэги, под которыми он
был сохранен:

Получение элементов кэша с тэгами. Листинг 27.14


$anne = Cache::tags('people', 'artists')->get('Anne');
$john = Cache::tags(array('people', 'authors'))->get('John');

Вы можете очистить все элементы по тэгу или списку тэгов. Например, это
выражение удалит все элементы кэша с тэгом или people, или authors, или в
обоих сразу. Таким образом, и "Anne", и "John" будут удалены из кэша:

Удаление элементов по тэгу или списку тэгов. Листинг 27.15


Cache::tags('people', 'authors')->flush();
178
Перед использованием драйвера database вам понадобится создать таблицу
для хранения элементов кэша. Ниже приведён пример её структуры в виде
миграции Laravel:

Схема миграции для таблицы хранения элементов кэша. Листинг 27.16


Schema::create('cache', function($table)
{
$table->string('key')->unique();
$table->text('value');
$table->integer('expiration');
});
179

28.Хелперы

МАССИВЫ

array_add

Добавить указанную пару ключ/значение в массив, если она там ещё не


существует.

$array = array('foo' => 'bar');

$array = array_add($array, 'key', 'value');

array_divide

Вернуть два массива - один с ключами, другой со значениями оригинального


массива.

$array = array('foo' => 'bar');

list($keys, $values) = array_divide($array);

array_dot

Сделать многоуровневый массив одноуровневым, объединяя вложенные


массивы с помощью точки в именах.

$array = array('foo' => array('bar' => 'baz'));

$array = array_dot($array);// array('foo.bar' => 'baz');

array_except

Удалить указанную пару ключ/значение из массива.

$array = array_except($array, array('ключи', 'для', 'удаления'));

array_fetch

Вернуть одноуровневый массив с выбранными элементами по переданному


пути.

$array = array(

array('developer' => array('name' => 'Taylor')),

array('developer' => array('name' => 'Dayle')),


180
);

$array = array_fetch($array, 'developer.name');// array('Taylor', 'Dayle');

array_first

Вернуть первый элемент массива, прошедший (returntrue) требуемый тест.

$array = array(100, 200, 300);

$value = array_first($array, function($key, $value)

return $value >= 150;

});

Третьим параметром можно передать значение по умолчанию:

$value = array_first($array, $callback, $default);

array_flatten

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

$array = array('name' => 'Joe', 'languages' => array('PHP', 'Ruby'));

$array = array_flatten($array);// array('Joe', 'PHP', 'Ruby');

array_forget

Удалить указанную пару ключ/значение из многоуровневого массива,


используя синтаксис имени с точкой.

$array = array('names' => array('joe' => array('programmer')));

array_forget($array, 'names.joe');

array_get

Вернуть значение из многоуровневого массива, используя синтаксис имени с


точкой.

$array = array('names' => array('joe' => array('programmer')));

$value = array_get($array, 'names.joe');


181
array_only

Вернуть из массива только указанные пары ключ/значения.

$array = array('name' => 'Joe', 'age' => 27, 'votes' => 1);

$array = array_only($array, array('name', 'votes'));

array_pluck

Извлечь значения из многоуровневого массива, соответствующие


переданному ключу.

$array = array(array('name' => 'Taylor'), array('name' => 'Dayle'));

$array = array_pluck($array, 'name');// array('Taylor', 'Dayle');

array_pull

Извлечь значения из многоуровневого массива, соответствующие


переданному ключу, и удалить их.

$array = array('name' => 'Taylor', 'age' => 27);

$name = array_pull($array, 'name');

array_set

Установить значение в многоуровневом массиве, используя синтаксис имени


с точкой.

$array = array('names' => array('programmer' => 'Joe'));

array_set($array, 'names.editor', 'Taylor');

array_sort

Отсортировать массив по результатам вызывов переданной функции-


замыкания.

$array = array(

array('name' => 'Jill'),

array('name' => 'Barry'),

);
182
$array = array_values(array_sort($array, function($value)

return $value['name'];

}));

array_where

Отфильтровать массив функцией-замыканием.

$array = array(100, '200', 300, '400', 500);

$array = array_where($array, function($key, $value)

return is_string($value);

});// Array ( [1] => 200 [3] => 400 )

head

Вернуть первый элемент массива. Полезно при сцеплении методов в PHP


5.3.x.

$first = head($this->returnsArray('foo'));

last

Вернуть последний элемент массива. Полезно при сцеплении методов.

$last = last($this->returnsArray('foo'));

ПУТИ

app_path

Получить абсолютный путь к папке app.

base_path

Получить абсолютный путь к корневой папке приложения.

public_path

Получить абсолютный путь к папке public.


183
storage_path

Получить абсолютный путь к папке app/storage.

СТРОКИ

camel_case

Преобразовать строку к camelCase.

$camel = camel_case('foo_bar'); // fooBar

class_basename

Получить имя класса переданного класса без пространства имён.

$class = class_basename('Foo\Bar\Baz'); // Baz

Выполнить над строкой htmlentities в кодировке UTF-8.

$entities = e('<html>foo</html>');

ends_with

Определить, заказчивается ли строка переданной подстрокой.

$value = ends_with('This is my name', 'name');

snake_case

Преобразовать строку к snake_case (стиль именования Си, с


подчёркиваниями вместо пробелов - прим. пер.).

$snake = snake_case('fooBar'); // foo_bar

str_limit

Ограничить строку заданным количеством символов и символами окончания.

str_limit($value, $limit = 100, $end = '...')

Example:

$value = str_limit('The PHP framework for web artisans.', 7); // The PHP...

starts_with
184
Определить, начинается ли строка с переданной подстроки.

$value = starts_with('This is my name', 'This');

str_contains

Определить, содержит ли строка переданную подстрокую.

$value = str_contains('This is my name', 'my');

str_finish

Добавить одно вхождение подстроки в конец переданной строки и удалить


повторы в конце, если они есть.

$string = str_finish('this/string', '/'); // this/string/

str_is

Определить, соответствует ли строка маске. Можно использовать звёздочки


(*) как символы подстановки.

$value = str_is('foo*', 'foobar');

str_plural

Преобразовать слово-строку во множественное число (только для


английского языка).

$plural = str_plural('car');

str_random

Создать последовательность случайных символов заданной длины.

$string = str_random(40);

str_singular

Преобразовать слово-строку в единственное число (только для английского


языка).

$singular = str_singular('cars');

studly_case

Преобразоватьстрокув StudlyCase.
185
$value = studly_case('foo_bar'); // FooBar

trans

Перевести переданную языковую строку. Это алиас для Lang::get.

$value = trans('validation.required'):

trans_choice

Перевести переданную языковую строку с изменениями. Алиас для


Lang::choice.

$value = trans_choice('foo.bar', $count);

URLs

action

Сгенерировать URL для заданного экшна (метода) контроллера.

$url = action('HomeController@getIndex', $params);

route

Сгенерировать URL для заданного именованного роута.

$url = route('routeName', $params);

asset

Сгенерировать URL ко внешнему ресурсу (изображению и пр.).

$url = asset('img/photo.jpg');

link_to

Сгенерировать HTML-ссылку на указанный URL.

echo link_to('foo/bar', $title, $attributes = array(), $secure = null);

linktoasset

Сгенерировать HTML-ссылку на внешний ресурс (изображение и пр.).

echo link_to_asset('foo/bar.zip', $title, $attributes = array(), $secure = null);

linktoroute
186
Сгенерировать HTML-ссылку на заданный именованный маршрут.

echo link_to_route('route.name', $title, $parameters = array(), $attributes =


array());

linktoaction

Сгенерировать HTML-ссылку на заданное действие контроллера.

echo link_to_action('HomeController@getIndex', $title, $parameters = array(),


$attributes = array());

secure_asset

Сгенерировать HTML-ссылку на внешний ресурс (изображение и пр.) через


HTTPS.

echo secure_asset('foo/bar.zip', $title, $attributes = array());

secure_url

Сгенерировать HTML-ссылку на указанный путь через HTTPS.

echo secure_url('foo/bar', $parameters = array());

url

Сгенерировать HTML-ссылку на указанный абсолютный путь.

echo url('foo/bar', $parameters = array(), $secure = null);

ПРОЧЕЕ

csrf_token

Получить текущее значение CSRF-токена.

$token = csrf_token();

dd

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

dd($value);

value

Если переданное значение - функция-замыкание, вызвать её и вернуть


187
результат. В противном случае, вернуть само значение.

$value = value(function() { return 'bar'; });

with

Вернуть переданный объект. Полезно при сцеплении методов в PHP 5.3.x.

$value = with(new Foo)->doWork();


188

29. Elixir

Laravel Elixir предоставляет чистый и гибкий API для определения основных


Gulp-задач вашего Laravel-приложения. Elixir поддерживает несколько
основных препроцессоров CSS и JavaScript и даже инструменты
тестирования.

Перед запуском Elixir необходимо убедиться, что Node.js установлен на


вашем компьютере.

Проверяем, установлен ли node.js на компьютере. Листинг 29.1


node -v

По умолчанию Laravel Homestead включает в себя всё необходимое. Однако,


если вы не используете Vagrant, то вы можете легко установить Node,
посетив их страницу загрузки (https://nodejs.org/en/).

После установим gulp, как глобальный проект:

Установка gulp. Листинг 29.2


npm install --global gulp

Осталось только установить Elixir! В корне в свежей версии Laravel вы


найдете файл package.json . Это то же самое, что и файл composer.json, только
он определяет зависимости Node, а не PHP. Вы можете установить
перечисленные в нём зависимости, выполнив:

Установка Elixir. Листинг 29.3


npm install

Компилирование, объединение и минимизация файлов осуществляется в


корневом файле gulpfile.js. Подразумевается, что необходимые для
компиляции less, sass и coffescript – файлы находятся в папке resourses/assets

Компилирование sass-файлов. Листинг 29.4


elixir(function(mix) {
mix.sass("app.scss");
});

Пример sass-файла

Файл app.scss. Листинг 29.5


$main: #444;
189
.btn {
color: $main;
display: block;
}
.btn-a {
color: lighten($main, 30%);
&:hover {
color: lighten($main, 40%);
}
}

Выполнение всех задач gulp-файла:

Выполнение всех gulp задач. Листинг 29.6


gulp

В корневой папке появится папка css с двумя файлами: app.css и app.css.map.


Файл app.css.map необходим, чтобы можно было по текущему css-файлу
восстановить sass-файл.

Другие gulp-задачи

Комбинирование стилей и сохранение по указанному пути. Листинг 29.7


elixir(function(mix) {
mix.styles([
"normalize.css",
"main.css"
], 'public/build/css/everything.css');
});
190

30.Разработка пакетов

Пакеты (packages) - основной способ добавления нового функционала в


Laravel. Пакеты могут быть всем, чем угодно - от классов для удобной
работы с датами типа Carbon до целых библиотек BDD-тестирования,
наподобие Behat.

Конечно, всё это разные типы пакетов. Некоторые пакеты самостоятельны,


что позволяет им работать в составе любого фреймворка, а не только Laravel.
Примерами таких отдельных пакетов являются Carbon и Behat. Любой из них
может быть использован в Laravel после простого указания его в файле
composer.json.

С другой стороны, некоторые пакеты разработаны специально для


использования в Laravel. В предыдущей версии Laravel такие пакеты
назывались "bundles". Они могли содержать роуты, контроллеры, шаблоны,
настройки и миграции, специально рассчитанные для улучшения приложения
на Laravel. Так как для разработки самостоятельных пакетов нет особенных
правил, этот раздел документации в основном посвящён разработке именно
пакетов для Laravel.

Все пакеты Laravel распространяются через Packagist и Composer, поэтому


нужно изучить эти прекрасные средства распространения кода для PHP.

Простейший способ создать пакет для использования в Laravel - с помощью


команды workbench инструмента командной строки Artisan. Сперва вам
нужно установить несколько параметров в файле app/config/workbench.php.
Там вы найдёте такие настройки, как name и email. Их значения будут
использованы при генерации composer.json для вашего нового пакета. Когда
вы заполнили эти значения, то всё готово для создания заготовки.

Использование команды workbench. Листинг 30.1


php artisan workbench vendor/package --resources

Где vendor - имя поставщика услуг, package - это имя создаваемого вами
пакета.

К примеру, мой логин на github — mikhalkevich, а создаваемый пакет я хочу


назвать obmenka, соответственно, я должен выполнить команду:
191
Создание пакета mikhalkevich/obmenka. Листинг 30.2
php artisan workbench --resources mikhalkevich/obmenka

Где флаг resourses говорит, что необходимо так же создать специфичные для
Laravel папки: migrations, views, config и тд.

Чтобы Laravel при запуске приложения автоматически подгружал наш пакет,


нам необходимо в файл app/config/app.php в массив providers добавить
строчку:

Добавление пакета в массив providers конфигурационного файлаapp.php.


Листинг 30.3
'Mikhalkevich\Obmenka\ObmenkaServiceProvider',

Имена классов поставщика услуг следуют схеме [Package]ServiceProvider, но


перед этим указывается полный namespace класса.

Структура пакета

В папке src мы можем видеть знакомую структуру папок, названия которых


говорят сами за себя.

Заметим, что в папке src/Mikhalkevich/Obmenka/ хранится класс нашего


поставщика услуг, а также туда стоит класть все вспомогательные классы
нашего пакета.

Посмотрим на класс ObmenkaServiceProvider. Метод register будет вызван,


как только пакет был зарегистрирован, а метод boot вызывается каждый раз
перед обработкой запроса.

Создадим в папке src нашего пакета файл routes.php с двумя роутами:

Роуты файла routes.phpсоздаваемого пакета. Листинг 30.4


Route::get('/blog/', array(
'as' => 'posts_list',
'uses' => 'Cherryoff\Nbblog\NbblogController@showList'
));

Route::get('/blog/{link}', array(
'as' => 'post',
'uses' => 'Cherryoff\Nbblog\NbblogController@showPost'
))->where('link', '[A-Za-z-_]+');
192
Где в качестве контроллера указываем полный путь до нашего еще не
созданного контроллера. Далее в папке controllers создадим контроллер
ObmenkaController со следующим содержимым:

Содержимое ObmenkaController. Листинг 30.5


<?php namespace Cherryoff\Nbblog;

use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\URL;

class ObmenkaController extends Controller {

public function showList()


{
return 'Posts list';
}

public function showPost($link)


{
return 'Get post:'.$link;
}

Теперь у нас прописаны пути и контроллер должен отвечать нам простыми


сообщениями. Но если мы перейдем по ссылке sandbox.local/blog/, то
получим исключение о том, что страница не найдена. Все дело в том, что
приложение не знает о том, что у нашего пакета есть свои пути, и, чтобы
исправить это, подключим файл routes.php в конце метода boot класса
ObmenkaServiceProvider:

Подключение маршрутов пакета. Листинг 30.6


include __DIR__.'/../../routes.php';

Чтобы показать composer где искать необходимые файлы, добавим в


composer.json (нашего пакета!!!) в секцию classmap строчку «src/controllers»,
после чего выполним:
193
Обновление классов автозагрузок. Листинг 30.7
composer dump-autoload

Теперь роуты должны работать.

Далее, создадим папку models и файл Post.php в ней. Листинг файла


представлен ниже:

Модель Post. Листинг 30.8


<?phpnamespace Cherryoff\Obmenka;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Config;

/**
* Модель записи в блоге
*/

classPostextendsModel {

protected$table = 'posts';
//Добавляем в выдачу вычисляемое поле
protected$appends = array('cut');
//Делаем поля доступными для автозаполнения
protected$fillable = array('header', 'link', 'article');

//Некоторые правила валидиции


publicstatic$rules = array(
'header' =>'required|max:256',
'link' =>'required|between:2,32|unique',
'article' =>'required'
);

publicfunctiongetCutAttribute(){
return Str::limit($this->attributes['article'], 120);
}

Теперь необходимо добавить папку models в секцию автозагрузки


composer.json (нужно добавить выше строчки "src/controllers") нашего
проекта и выполнить composer dump-autoload так же, как мы это делали в
случае с контроллером.

Строкой в консоли создадим миграцию для нашего пакета:

Создаем миграцию для пакета. Листинг 30.9


194
php artisan migrate:make create_obmenka_posts_table
--bench="mikhalkevich/obmenka"

В папке src/migrations/ появился класс только что созданной миграции. В его


методе up пропишем:

Файл миграции. Листинг 30.10


Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('link', 32);
$table->string('header', 256);
$table->text('article');
$table->timestamps();
$table->softDeletes();
});

В методе down():

Содержимое метода down(). Листинг 30.11


Schema::dropIfExists('posts');

Выполняем миграцию:

Выполнение миграции. Листинг 30.12


php artisan migrate --bench="mikhalkevich/obmenka"

Теперь заполним только что созданную таблицу начальными данными. Для


этого в папке src создадим папку seeds с файлом ObmenkaSeeder.php со
следующим содержимым:

Seeds. Листинг 30.13


<?php namespace Cherryoff\Nbblog;

use Illuminate\Database\Seeder;

class NbblogSeeder extends Seeder {

public function run()


{
$posts = [
[
'header'=>'Header post number one',
'link'=>'one',
'article'=>'Body text',
],
[
'header'=>'Very important news',
'link'=>'news',
195
'article'=>'Body text',
],
];

foreach ($posts as $post){


Post::create($post);
}
}

Добавим папку seeds в секцию автозагрузки composer.json и снова выполним


dump-autoload.

Теперь загрузим начальные данные командой:

Загрузка начальных данных. Листинг 30.14


php artisan db:seed --class="\Mikhalkevich\Obmenka\ObmenkaSeeder"

Создадим шаблоны видов для нашего блога, разместив их в папке src/views:

Базовый шаблон. Листинг 30.15


<!DOCTYPE html>
<html>
<head>
<link href='http://fonts.googleapis.com/css?
family=Lora&subset=latin,cyrillic' rel='stylesheet' type='text/css'>
<title>
@yield('title')
</title>
</head>
<body>
<div class="content">
<header>
<h1>My simple blog</h1>
<small>Just blog package for Laravel</small>
</header>
<nav>
<ul>
<li><a href="/">Main page</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
</nav>
@yield('content')
</div>
</body>
</html>

В контроллере будем подключать такой подшаблон:

List.blade.php. Листинг 30.16


@section('title')
196
List
@stop

@section('content')
<small>Number of posts in the blog: {{$count}}</small>
<ul class="posts-list">
@forelse($posts as $post)
@include('nbblog::preview')
@empty
<li><h3>No records</h3></li>
@endforelse
</ul>
@stop

Preview.blade.php

Preview.blade.php. Листинг 30.17


<li>
<span><small>{{$post->created_at}}</small></span>
<h2><a href="/blog/{{$post->link}}">{{$post->header}}</a></h2>
<p>{{$post->cut}}</p>
</li>

Еще один файл подшаблона, post.blade.php

Post.blade.php. Листинг 30.18


@section('title')
{{$header}}
@stop

@section('content')
<div class="post-block">
<span><small>{{$created_at}}</small></span>
<h2>{{$header}}</h2>
<p>
{{$article}}
</p>
</div>
@stop

Итак, с шаблонами закончено, теперь можно приступить к наполнению их


данными. Для этого создадим файл viewComposers.php прямо в папке src.
(Мы можем создать этот файл в любом месте нашего пакета, главное, не
забыть его подключить).

ViewComposers.php. Листинг 30.19


<?php
/**
* Не забываем использовать имя своего пакета перед названием
вида
*/
View::composer(array('obmenka::list', 'obmenka::post'),
function($view){
197
$view->with('uri', 'blog');
});

View::composer('obmenka::list', function ($view) {


$view->with('count', \Mikhalkevich\Obmenka\Post::count())-
>with('posts', \Mikhalkevich\Obmenka\Post::all());
});

Мы только что привязали переменную uri к шаблону списка постов и


шаблону поста (в дальнейшем, когда мы будем получать эту переменную из
настроек, нам будет удобнее передавать ее в виды в одном месте), и вместе с
шаблоном списка постов мы отдаем сразу все записи.

Теперь необходимо подключить созданный файл в классе нашего


поставщика услуг (src/Cherryoff/Nbblog/NbblogServiceProvider.php) так же,
как мы это делали с файлом route.php.

Подключение файла ViewComposers. Листинг 30.20


include __DIR__.'/../../viewComposers.php';

Меняем класс нашего контроллера

Конроллер NbblogController. Листинг 30.21


class NbblogController extends Controller {

public function __construct(){


$this->layout = View::make('nbblog::layout');
}

public function showList()


{
$this->layout->content = View::make('nbblog::list');
}

public function showPost($link)


{
$post = Post::where('link', '=', $link)->firstOrFail();
$this->layout->content = View::make('nbblog::post',
$post);
}
}

В папке public нашего пакета создадим папку css и добавим туда файл
main.css.

Опубликуем внешние ресурсы нашего пакета командой:

Публикация внешних ресурсов пакета. Листинг 30.22


198
php artisan asset:publish --bench="mikhalkevich/obmenka"

В папке public нашего приложения появился файл main.css, который


расположился в папке packages/cherryoff/nbblog/css/. Так laravel делает со
всеми внешними ресурсами пакетов. А значит, что это соглашение об
именовании внешних ресурсов поможет нам обратится к этому файлу из
нашего шаблона.

Путь к таблице стилей нашего пакета. Листинг 30.23


/packages/mikhalkevich/obmenka/css/main.css
199

31. Laravel Homestead

Если у вас уже есть готовый сервер и он вас полностью устраивает, то эту
главу можно пропустить.

Vagrant предоставляет простой и элегантный способ создания и управления


виртуальными машинами. Laravel Homestead является официальным
"образом" (box) для Vagrant'а и предоставляет замечательную среду
разработки, не требуя устанавливать PHP, веб-сервер и какое бы то ни было
дополнительное серверное ПО на вашей локальной машине. Больше не стоит
беспокоиться о захламлении вашей операционной системы! Боксы Vagrant'а
являются полностью одноразовыми. Если что-то пойдет не так, вы сможете
уничтожить и пересоздать бокс за считанные минуты!

Homestead работает под любыми версиями Windows, Mac и Linux и


включает веб-сервер Nginx, PHP 5.6, MySQL, Postgres, Redis, Memcached и
другие вкусности, которые могут потребоваться вам для разработки
потрясающих Laravel-приложений.

Текущая версия Homestead создана и протестирована для использования под


Vagrant 1.6.

 Что внутри
 Ubuntu 14.04
 PHP 5.6
 Nginx
 MySQL
 Postgres
 Node (включая Bower, Grunt и Gulp)
 Redis
 Memcached
 Beanstalkd
 Laravel Envoy
 Fabric + HipChat Extension

Установка VirtualBox и Vagrant

Перед запуском средыHomestead, вы должныустановитьVirtualBox и Vagrant.


Оба этих программных продукта имеют легкие в использовании
установщики для всех популярных операционных систем.
200
После установки vagrant откроем консоль и определим текущую версию
vagrant следующей командой:

Определение версии vargrant. Листинг 31.1


vagrant -v

Далее, для создания виртуальной машины нам нужна команда up, которая
создаст виртуальную машину по предворительным настройкам из файла
vagrantfile (https://docs.vagrantup.com/v2/vagrantfile/).

Создание виртуальной машины. Листинг 31.2


vagrant up

Эта команда установит

Добавление бокса в Vagrant

Как только VirtualBox и Vagrant будут установлены, вам следует добавить


бокс laravel/homestead в Vagrant, используя следующую команду в
командной строке. Процесс скачки бокса займет какое-то время, в
зависимости от скорости вашего интернет-соединения:

Добавление бокса vagrant. Листинг 31.3


vagrant box add laravel/homestead

Установка Homestead

С помощью Composer и PHP

На машине должен быть установлен PHP и Composer

После того как бокс Homestead добавлен в Vagrant, при помощи композера
установите глобально инструмент Homestead CLI:

Установка HomesteadCLI. Листинг 31.4


composer global require "laravel/homestead=~2.0"
201
После установки инструмента, создайте конфигурационный файл
Homestead.yaml (C:\Users\username\.homestead):

Создание конфигурационного файла homestead.yaml. Листинг 31.5


homestead init

С помощью git

В этом варианте вы можете обойтись без установки PHP на локальную


машину, вам понадобится только установленный Git+msysgit.

Склонируйте репозиторий с HomesteadCLI в произвольную директорию:

Установка HomesteadCLI с помощью git. Листинг 31.6


git clone https://github.com/laravel/homestead.git Homestead

Для создания файла Homestead.yaml (C:\Users\username\.homestead)


выполним команду:

Создание файла Homestead.yaml. Листинг 31.7


bash init.sh
202

32. Обработка файлов .csv

Форма загрузки прайса формата .csv. Листинг 1.


<form method="POST" action="{{asset('home')}}"
enctype="multipart/form-data">
{{csrf_field()}}
<div class="form-group">
<label for="exampleInputFile">Прайс формата .csv</label>
<input type="file" id="exampleInputFile" name="price">
<p class="help-block">
Убедитесь в том, что прайс правильного формата
</p>
</div>
<button type="submit" class="btn btn-default">Загрузить</button>
</form>

Рассмотрим экшн-обработкчик формы

Обработка формы. Листинг 1.


public function postIndex()
{
if ($_FILES) {
// Инициализация переменной – пути к временному файлу
$tmp_name = $_FILES['price']['tmp_name'];
// Инициализация переменной – имени файла
$name = $_FILES['price']['name'];
// Инициализация переменной - дирректории
$dir = public_path() . "/media/prices/";
// Если такой директории нет - создаем
if (!is_dir($dir)) {
@mkdir($dir, 0777);
}
// Проверяем, если файл реально был загружен…
if (is_uploaded_file($tmp_name)) {
// Перемещаем его из временной директории в конечную
move_uploaded_file($tmp_name, $dir . $name);
} else {
echo("File upload error");
}
}
}

Вся дальнейшая обработка файла идет после функции move_uploaded_file():

. Листинг 1.
$handle = fopen($dir.$name, ‘r’);
while($data = fgetcsv($handle, 1000, ‘;’)){
print_r($data);
}
203

33. Парсинг

Встроенный модуль для парсигна html-страниц - Crawler.

Подключение Crawler. Листинг 32.1


use Symfony\Component\DomCrawler\Crawler

Создадим контракт, или интерфейс ParseContract.php в папке app\Parse

Интерфейс ParseContract.php . Листинг 32.2


namespace App\Parser;

Interface ParseContract
{
public function getParse($path);
}

Для данного интерфейса создадим имплемент.

Класс парсинга. Листинг 32.3


<?php

namespace app\Parser;
use Symfony\Component\DomCrawler\Crawler;
use App\Parser\ParseContract;

class Forum3 implements ParseContract


{
public $url;
public function getParse($path){
$this->url = 'http://www.forum3.ru/';
$file = file_get_contents($this->url);
$crawler = new Crawler($file);
//использование $name = $this->html($crawler, "body");
return $this->url;
}
public function text($obj, $val = null)
{
$risk = $obj->filter($val)->count();
if ($risk == 0) {
$rams = '';
}else{
$rams = $obj->filter($val)->text();
}
return $rams;
}
public function html($obj, $val = null)
{
$risk = $obj->filter($val)->count();
if ($risk == 0) {
$rams = '';
}else{
204
$rams = $obj->filter($val)->html();
}
return $rams;
}
}

Вспомогательный класс типаж, расширяющий базовую функциональность


crowler-а.

Типаж с реализацией методов html() и text(). Листинг 32.4


<?php
namespace App\Parser;
trait ParseTrait{
public function text($obj, $val = null)
{
$risk = $obj->filter($val)->count();
if ($risk == 0) {
$rams = '';
}else{
$rams = $obj->filter($val)->text();
}
return $rams;
}
public function html($obj, $val = null)
{
$risk = $obj->filter($val)->count();
if ($risk == 0) {
$rams = '';
}else{
$rams = $obj->filter($val)->html();
}
return $rams;
}
}

В данном классе находятся два метода, которые проверяют наличие


селектора на странице парсинга. Методы возвращают либо пустую строку,
либо объект найденного селектора.

В используемом классе подключим данный типаж:

Использование типажа. Листинг 32.5


class Onliner implements ParseContract
{
use ParseTrait;
public $char;
public function getParse($site_product_url){
$this->setCharacter($site_product_url,".product-specs__table");
$character = $this->getCharacter();
}
public function setCharacter($path, $selector){
$file = file_get_contents($path);
$crawler = new Crawler($file);
$table = $this->html($crawler, $selector);
$this->char = $table;
205
return true;
}
public function getCharacter(){
return $this->char;
}
}

Парсинг https

Для парсинга закрытых протоколов используют CURL. Например, для


парсинга товаров onliner.by,

Парсим https с помощю curl. Листинг 32.6


$http = 'https://catalog.api.onliner.by/search/' . $name . '?
group=1&page=10';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $http);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
curl_close($ch);
$data = json_decode($result);
206

IV. Ресурсы Laravel


1. Real-time приложение с широковещательными событиями

Версия Laravel 5.1 включает в себя функционал, называемый вещанием


событий, который с легкостью позволяет создавать приложения реального
времени (real-time) на PHP. Этот функционал позволяет приложению
посылать события на различные “облачные” PubSub-сервисы, наподобие
Pusher‘а или в Redis.

Разберем простое TODO-приложение и превратим его в real-time


приложение, используя вещание событий в Laravel.

Положим начальный код в директорию todo-app, путем клонирования


репозитория из git’а.

Клонируем приложение из git’-а. Листинг 1.1


git clone https://github.com/cwt137/l51-todo-app todo-app

Как только клонирование завершится, перейдите в директорию todo-app. Нам


необходимо установить все зависимости приложения, выполнив следующую
команду (в зависимости от того, как в вашей системе установлен Composer,
команда может быть немного другой):

Загружаем зависимости. Листинг 1.2


composer install

После установки зависимостей необходимо проинициализировать базу


данных.

Выполняем миграции таблиц. Листинг 1.3


php artisan migrate

Широковещательные события

Для работы с широковещательными событиями, мы создадим обычное


событие Laravel, но при этом будем реализовывать интерфейс
ShouldBroadcast. Когда Laravel видит, что событие реализует этот интерфейс,
он понимает, что это широковещательное событие. Этот интерфейс требует
от нас реализации метода broadcastOn. Он должен возвращать массив строк-
названий каналов, на которые следует вещать данное событие.
207
Чтобы создать нужные нам классы-события, нужно выполнить несколько
команд artisan’а:

Создаем классы-события. Листинг 1.4


php artisan make:event ItemCreated
php artisan make:event ItemUpdated
php artisan make:event ItemDeleted

Заменим файл app/Events/ItemCreated.php и его содержимое следующим


кодом:

Содержимое файла ItemCreated.php. Листинг 1.5


<?php

namespace App\Events;

use App\Item;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class ItemCreated extends Event implements ShouldBroadcast


{
use SerializesModels;

public $id;

public function __construct(Item $item)


{
$this->id = $item->id;
}

public function broadcastOn()


{
return ['itemAction'];
}
}

Система событий Laravel впоследствии сериализует этот объект и будет


вещать его в облачный сервис на канал itemAction. Оставшаяся пара событий
похожа на первое.

Откройте файл app/Events/ItemUpdated.php и замените его содержимое


следующим кодом:

Содержимое файла ItemUpdated. Листинг 1.6


<?php

namespace App\Events;

use App\Item;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
208
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class ItemUpdated extends Event implements ShouldBroadcast


{
use SerializesModels;

public $id;
public $isCompleted;

public function __construct(Item $item)


{
$this->id = $item->id;
$this->isCompleted = (bool) $item->isCompleted;
}

public function broadcastOn()


{
return ['itemAction'];
}
}

Откройте файл app/Events/ItemDeleted.php и замените его содержимое


следующим кодом:

Содержимое файла ItemDeleted.php . Листинг 1.7


<?php

namespace App\Events;

use App\Item;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class ItemDeleted extends Event implements ShouldBroadcast


{
use SerializesModels;

public $id;

public function __construct(Item $item)


{
$this->id = $item->id;
}

public function broadcastOn()


{
return ['itemAction'];
}
}

Существует несколько мест, в которых мы могли бы “выбрасывать” наши


события. Мы могли бы делать это внутри контроллера или же внутри
событий базы данных. В этом примере мы воспользуемся событиями базы
209
данных, поскольку лично мне это кажется более естественным и не
захламляет контроллер лишним кодом. Также, выбрасывание событий из
слоя базы данных делает возможным возбуждение этих событий из,
например, скрипта для командной строки или же по cron-скрипту.

Библиотека для работы с БД Eloquent вызывает события каждый раз, когда


модель создается, сохраняется после обновления или удаляется. Мы будем
прослушивать эти события, чтобы возбуждать свои собственные
широковещательные события. Это можно сделать внутри сервис-провайдера.

Откройте файл app/Providers/AppServiceProvider.php и замените его


содержимое следующим кодом:

Содержимое файла AppServiceProvider.php. Листинг 1.8


<?php

namespace App\Providers;

use Event;
use App\Item;
use App\Events\ItemCreated;
use App\Events\ItemUpdated;
use App\Events\ItemDeleted;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{

public function boot()


{
Item::created(function ($item) {
Event::fire(new ItemCreated($item));
});

Item::updated(function ($item) {
Event::fire(new ItemUpdated($item));
});

Item::deleted(function ($item) {
Event::fire(new ItemDeleted($item));
});
}

public function register()


{
//
}
}

Pusher

В данном примере мы воспользуется облачным сервисом Pusher. Поддержка


этого сервиса встроена в Laravel и его легче всего настроить.
210
После регистрации на сайте Pusher’а (http://pusher.com/), перейдите в раздел
администрирования и создайте новое приложение todo-app. Обратите
внимание на app_id , key и secret . Они понадобятся нам позже.

Чтобы наше приложение смогло начать использовать Pusher, необходимо


добавить соответствующую библиотеку. Это делается при помощи
composer’а:

Подключение библиотеки pusher. Листинг 1.9


composer require 'pusher/pusher-php-server:*'

Мы также добавим немного JavaScript-кода на нашей странице. Откройте


resources/views/index.blade.php (шаблон домашней страницы) и разместите
следующий код прямо перед закрывающим тэгом body:

Подключение javaScript. Листинг 1.10


<script src="//js.pusher.com/2.2/pusher.min.js"></script>
<script>
var pusher = new Pusher("{{ env(PUSHER_KEY) }}");
</script>
<script src="js/pusher.js"></script>

Код, приведенный выше, загружает клиентскую JavaScript библиотеку для


работы с Pusher’ом, создает экземпляр Pusher, передавая наш ключ (key) в
конструктор, а также загружает специфичную логику нашего приложения.

В начальной версии приложения элементы списка добавлялись и удалялись


на основе событий, происходящих на странице (отправка формы, щелчок по
иконке удаления и т.д.). Поэтому мы открыли два окна, и оба не обновлялись
автоматически. Одно окно не может видеть, что происходит в другом. Чтобы
сделать приложение “живым” (real-time), мы будем добавлять и удалять
элементы на основе событий из Pusher’а. Но сначала откройте файл
public/js/app.js и закомментируйте все вызовы методов addItem() и
removeItem(). Но будьте внимательны – не удалите сами определения этих
функций.

Создайте файл public/js/pusher.js и вставьте в него следующий код:

Pusher.js. Листинг 1.11


( function( $, pusher, addItem, removeItem ) {

var itemActionChannel = pusher.subscribe( 'itemAction' );

itemActionChannel.bind( "App\\Events\\ItemCreated",
function( data ) {

addItem( data.id, false );


} );
211

itemActionChannel.bind( "App\\Events\\ItemUpdated",
function( data ) {

removeItem( data.id );
addItem( data.id, data.isCompleted );
} );

itemActionChannel.bind( "App\\Events\\ItemDeleted",
function( data ) {

removeItem( data.id );
} );

} )( jQuery, pusher, addItem, removeItem);

Приложение подписывается на канал itemAction . По умолчанию, Laravel


использует полное имя класса-события в качестве имени события для
Pusher’а. JavaScript код, приведенный выше, прослушивает три события:
App\Events\ItemCreated , App\Events\ItemUpdated и App\Events\ItemDeleted .
Имеются также обратные вызовы функций, которые обрабатывают эти
события. Таким образом, теперь элементы добавляются или удаляются на
основе событий от Pusher’а.

Откройте файл .env и разместите внизу следующие строки:

Установка ключей для pusher’а. Листинг 1.12


PUSHER_KEY=YOUR_PUSHER_KEY
PUSHER_SECRET=YOUR_PUSHER_SECRET
PUSHER_APP_ID=YOUR_PUSHER_APP_ID
212

2. QuickAdminс ролевым доступом

https://github.com/lube8uy/laravel5-quickadmin - Данный проект можно


использовать в качестве шаблонного для создания своих собственных.
Рассмотрим его подробнее.

Описание

 Модель User c ролью администратора


 Seeds для загрузки таблицы с двумя пользователями. Один с ролью
админ, другой – авторизированный пользователь.
 Все необоходимые действия с авторизированными пользователями:
вход в систему, выход, сброс и запоминание пароля.
 CRUD-действия с таблицей users.
 Обработка статусов страниц 403, 404, 500.

Установка

1) Клонируем проект.git clone https://github.com/lube8uy/laravel5-


quickadmin
2) Запускаем: composer install
3) Включить фай .env, убрав .example из файла .env.example. Здесь же
прописать настройки для подключения к базе данных.
4) Запускаем миграции:php artisan migrate
5) Запускаем seeds: php artisan db:seed --class=UserTableSeeder
6) Входим под админом, используя admin@quickadmin.com / adminadmin
илиplain@quickadmin.com / plainuser

Если всё правильно сделали, получили вот такую чудесную адаптивную


админку.
213

И что самое приятное – через блок управления users – мы можем управлять


пользователями (создавать, удалять, редактировать).

Закрываем доступ для не админов:

Встроенный метод hasRole(). Листинг 2.1


if(!$this->auth->user()->hasRole(‘admin’)){
Return redirect(‘home’);
}
214

3. АдминкаFrozenNode

https://github.com/FrozenNode/Laravel-Administrator - ссылка модуля админки.

Для установки админки, добавим запись в файл composer.json

Добавление зависимости в файле composer.json. 3.1


"frozennode/administrator": "5.*"

Не забываем обновлять сам composer, иначе он может подтянуть старые


зависимости:

Обновление версии composer. Листинг 3.2


composer selfupdate

И только после этого обновляем зависимости.

Обновление зависимостей проекта. Листинг 3.3


composer update

В конфигурационном файле app.php добавляем элемент массива providers

Добавление элемента массива providers. Листинг 3.4


'providers'=> [
'Frozennode\Administrator\AdministratorServiceProvider',
]

С помощью команды vendor:publish создаем конфигурационный файл


administrator.php

Vendor:publish. Листинг 3.5


php artisan vendor:publish

В папке config появился конфигурационный файлa administrator.php.

Также в папке config необходимо создать папку administrator, а в ней папку


settings.

Конфигурационные файлы

Рассмотрим настройки главного конфигурационного файла administrator.php.


215
Параметры файла administrator.php. Листинг 38.11
'uri' => 'admin', // url-адрес админки
'domain' => '',
'title' => 'Admin', //
'model_config_path' => config_path('administrator'), // папка для
конфигурационных файлов, папку administrator нужно создать
'settings_config_path' => config_path('administrator/settings'), //
папка для настроек, папку administrator/settings нужно создать
'menu' => array(), // в этом массиве пишем ссылки на файлы
конфигов, которые должны находиться в папке administrator.
'permission'=> function() //если функция возвращает true, тогда для
текущего пользователя доступ есть, если функция возвращает false –
доступа нет.
{
return Auth::check();
},
'use_dashboard' => false, // если хотим использовать свои шаблоны,
необходимо выставить в true
'dashboard_view' => '', // если use_dashboard выставлен в true,
здесь указываем ссылку на шаблон.
'home_page' => 'catalogs', // какой блок управления открывать
первым, по адресу /admin
'back_to_site_path' => '/', //ссылка, которая возвращет на сайт
'login_path' => 'auth/login', //если пользователь не авторизирован,
куда его перенаправить
'logout_path' => 'auth/logout', // ссылка на выход
'login_redirect_key' => 'redirect',
'global_rows_per_page' => 20, // количество записей на странице в
постраничной навигации
'locales' => array(), // языки

Найдем в конфигурационном файле administrator.php элмент массива menu.


Здесь регистрируются файлы админки, в котором описано поведение блоков
управления. Добавим ссылку на файл users.php.

Элемент массива menu конфигурационного файла administrator.php. Листинг


3.9
'menu' => array('users'),

Таким образом, мы подключили конфигурационный файл users, который


должен находиться в папке config/administrator.

Рассмотрим его.

Конфигурационный файл users. Листинг 3.10


<?php
return array(
'title' => 'Users',
'single' => 'User',
'model' => 'App\User',
'form_width'=> 500,
'columns' => array(
'id', // вывод колонки id
216
'picture'=>array( // вывод Html-кода
'title'=>'Фото',
'output' => '<img src="/public/uploads/products/resize/
(:value)" height="100" />',
),
),
'filters' => array(
'id', 'name',
'email' => array(
'title' => 'E-mail',
),
),
'edit_fields' => array(
'email' => array(
'title' => 'E-mail',
'type' => 'text',
),
),
);

В первую очередь, нас интересуют элементы columns, filters и edit_fields.


Columns – какие поля из модели выводить. Filters – по каким делать поиск.
Edit_fields – формы добавления и редактирования.

Теперь, если авторизированный пользователь зайдет по пути /admin, то


увидит вот такую страницу:

Edit_fields

В этом массиве мы создаем форму для редактирования и добавления записей.


Элементами первого уровня являются наименование полей таблицы либо
свойств модели, с которой мы работаем.

Key

Key. Листинг 3.12


'id' => array(
217
'type' =>'key',
'title' => 'ID',
),

Обработка и добавление изображений, элемент image

Image. Листинг 3.13


'image' => array(
'title' => 'Image',
'type' =>'image',
'location' => public_path() . '/uploads/products/originals/',
'naming' => 'random',
'length' => 20,
'size_limit' => 2,
'sizes' => array(
array(65, 57, 'crop', public_path() .
'/uploads/products/thumbs/small/', 100),
array(220, 138, 'landscape', public_path() .
'/uploads/products/thumbs/medium/', 100),
array(383, 276, 'fit', public_path() .
'/uploads/products/thumbs/full/', 100)
)
)

Текстовое поле, Text.

Text. Листинг 3.14


'name' => array(
'type' =>'text',
'title' => 'Name',
'limit' => 30, //по умолчанию не лимитировано
)

Password

Password. Листинг 3.15


'password'=> array(
'type'=>'password',
'title'=>'Password',
)

Textarea

Textarea. Листинг 3.16


'name' => array(
'type' =>'textarea',
'title' => 'Name',
'limit' => 300, //optional, defaults to no limit
'height' => 130, //optional, defaults to 100
)

Текстовый редактор.
218
Тип wysiwyg превращает textarea в текстовый редактор:

Wysiwyg. Листинг 3.17


'entry' =>array(
'type' =>'wysiwyg',
'title' => 'Entry',
)

Markdown

Markdown. Листинг 3.18


'name' => array(
'type' =>'markdown',
'title' => 'Name',
219
'limit' => 300, //optional, defaults to no limit
'height' => 130, //optional, defaults to 100
)

Relationship (связи)

Relationship belongs to. Листинг 3.19


'user' => array(
'type' => 'relationship',
'title' => 'User',
'name_field' => 'name', //what column or accessor on the other
table you want to use to represent this object
)

В подключаемой модели должна быть прописана связь belongsTo:

Связь belongsTo. Листинг 3.20


class Hat extends Eloquent {

public function user()


{
return $this->belongsTo('User');
}
}
220

VI. Приложение
1. PHPShtorm

Указание пути к интерпретатору PHP

Открываем вкладку Settings (Cntrl + Alt + S) или File -> Settings

Далее находим (либо набираем в поиске) вкладку PHP.


221

Нажимаем кнопку «Многоточие».

Далее в открывшемся окне нажмем плюсик ->Otherlocal. И указываем путь к


интерпретатору PHP.

Добавление консольной команды artisan

В настройках находим вкладку Commant LineToolSupport. Далее плюсик. В


выпадающем списке выбираем ToolbasedonSymfonyConsole.
222

Указываем пути к PHP и к Artisan


223

Пишем имя alias и жмем Ok.

Чтобы проверить, как команды artisan сейчас будут работать, выбираем

Tools-> Run Command или(Cntrl + Shft+X).


224
Теперь в консоли можно использовать команды artisan.

Подключение composer

Заходим в настройки file -> settings

В полях прописываем пути к файлам PHP, composer.phar и composer.json


225

Теперь мы можем добавлять зависимости из меню tools.

Для добавление зависимости, нажимаем add dependency


226

Горячие клавиши PHPStorm

Ctrl+shft+n Поиск файлов по названию.

Ctrl+shft+alt+n Поиск файлов по содержимому.

Ctrl+D Дублировать строку под курсором.

Ctrl+Shift+D Ctrl+Y Удалить строку под курсором.

Ctrl+Alt+L Отформатировать код.

Ctrl+Shift+^v Переместить строку вверх/вниз.

Ctrl+Shift+l Форматирование кода


227

2. Модальное окно на ajax

Для создания модального окна на ajax воспользуемся библиотекой jQuery.

Создадим функцию манипулирования модальным окном. Функция будет


возвращать модальное окно, если оно существует либо создавать новое.

Функция манипулирования модальным окном. Листинг 2.10


var fx = {
“initModal” : function(){
if($(“.modal-window”).length == 0) {
return $(“<div>”)
.addClass(“modal-window”)
.appendTo(“body”);
}
else {
return $(“.modal-window”);
}
}
}

Далее модифицируем обработчик события click.

Вызов функции модального окна. Листинг 2.11


jQuery(function($) {

var fx = {
“initModal” : function(){
if($(“.modal-window”).length == 0) {
return $(“<div>”)
.addClass(“modal-window”)
.appendTo(“body”);
}
else {
return $(“.modal-window”);
}
}
}

$(“li>a”).bind(‘click’, function(event){
event.preventDefault();
// Проверим, что сработал обработчик события и выведем текст ссылки
в консоль firebug
var data = $(this).attr(‘href’);
modal = fx.initModal();
});
})

Теперь по событию click будет появляться модальное окно. Но мы его не


заметим до тех пор, пока не пропишем css-стили для класса .modal-window

Стили .modal-window. Листинг 2.12


.modal-window {
228
position:absolute;
top:150px;
left:50%;
width:400px;
margin-left:200px;
padding:10px;
border:solid 1px #ccc;
background-color:#fff;
z-index:99;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 1px 1px 5px #999;
-moz-box-shadow: 1px 1px 5px #999;
box-shadow: 1px 1px 5px #999;
}

Метод Ajax

Основная функция для работы с AJAX-запросами – это функция ajax().

Использование вспомогательной функции ajax. Листинг 2.13


<script type="text/javascript">
$(function () {
$("button:first").click(function(){
$.ajax({
url: "testAjax.php",
type: "POST",
data: "name=Jonh&age=35",
timeout: 3000,
beforeSend: function(){
$("div").text("Загрузка...");
},
success: function(data){
$("div").html(data);
},
error: function(xhr, status){
$("div").html("<span>" + status + "</span>");
}
});
});
$("button:last").click(function(){
$("div").empty();
});
});
</script>

<div id="target"></div>
<button>Загрузить</button>
<button>Очистить</button>

Рассмотрим основные опции функции ajax.

 url – файл, к которому будет отправлен запрос.


 type – способ передачи данных: $_POST либо $_GET
229

 timeout – время выполнения запроса, число в милисекундах. Работа


ajax останавливается, если за данное время запрос не успел
обработаться. Параметр необязательный.
 beforeSend – функция срабатывающая, во время выполнения запроса.
Т.к. по умолчанию, ajax-запросы передаются в асинхронном режиме, то
в процессе выполнения запроса, мы можем выполнять любые другие
функции.
 success - функция срабатывающая после успешного выполнения
запроса.
 error – функция, выводящая ошибки запроса, если таковые имеются.
 data – строка с передаваемыми данными. Объект должен представлять
собой пары ключ/значение.

Часто возникает следующая ситуация: все AJAX-запросы должны


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

Совместное использование ajaxSetap() и ajax(). Листинг 2.14


<script type="text/javascript">
$(function () {
$.ajaxSetup({
url: "testAjaxSetup.php",
type: "POST",
timeout: 3000,
beforeSend: function(){
$("div:last").empty();
$("#result").text("Загрузка...");
},
success: function(data){
$("div:last").html(data);
$("#result").text("Готово!");
},
error: function(xhr, status){
$("#result").html("<span>" + status + "</span>");
}
});
$("button:eq(0)").click(function(){
$.ajax({ data: "q=1&er=none" });
});
$("button:eq(1)").click(function(){
$.ajax({ data: "q=2&er=none" });
});
});
</script>
</head>
<body>
<div id="result"></div>
<div></div>
<button>Запрос №1</button>
230
<button>Запрос №2</button>

Как видно из листинга, все передаваемые параметры, за исключением


параметра data, находятся в функции ajaxSetup. Задача функции ajax сводится
к тому, чтобы связать конкретный селектор с данными, которые необходимо
передать в файл-обработчик.

По щелчку на любую из кнопок листинга в testAjaxSetup.php будут поступать


переменные $_POST[‘q’] и $_POST[‘er’].

Вернемся к нашему модальному окну.

Подключение ajax. Листинг 2.15


$(“li>a”).live(‘click’, function(event){
event.preventDefault();
// Проверим, что сработал обработчик события и выведем текст ссылки
в консоль firebug
var data = $(this).text();
modal = fx.initModal();
$.ajax({
type: “Post”,
url: “ajaxfile.php”,
data: “url=” +data,
success: function(data) {
modal.append(data);
},
error: function(msg) {
modal.append(msg);
}
});
});

Теперь по клику будет создаваться модальное окно и в это окно будет


помещаться всё текстовое содержимое файла ajaxfile.php (всё, что выводится
на экран в данном файле).

Создание кнопки закрытия окна

Создание кнопки закрытия окна. Листинг 2.16


$(“li>a”).live(‘click’, function(event){
event.preventDefault();
// Проверим, что сработал обработчик события и выведем текст ссылки
в консоль firebug
var data = $(this).text();
modal = fx.initModal();
$(“<a>”).attr(“href”, “#”)
.addClass(“modal-close-btn”)
.html(“&times;”)
.click(function(event){
event.preventDefault();
$(“.modal-window”).remove();
}).appendTo(modal);
$.ajax({
type: “Post”,
231
url: “ajaxfile.php”,
data: “url=” +data,
success: function(data) {
modal.append(data);
},
error: function(msg) {
modal.append(msg);
}
});
});

Добавим css-стили для кнопки закрытия

Стили для класса modal-close-btn. Листинг 2.17


.modal-close-btn{
position:absolute;
top:1px;
right:1px;
margin:0;
padding:0;
text-decoration:none;
color:red;
font-size:18px;
}
.modal-close-btn:before{
position:relative;
top:-1px;
content: “Закрыть”;
font-size:12px;
}

Эффект плавного исчезновения модального окна

Добавим в объектный литерал функцию закрытия окна boxout()

Вызов функции модального окна. Листинг 2.18


jQuery(function($) {

var fx = {
“initModal” : function(){
if($(“.modal-window”)).length == 0 {
return $(“<div>”)
.addClass(“modal-window”)
.appendTo(“body”);
}
else {
return $(“.modal-window”);
}
},
“boxout” : function() {
$(“.modal-window”).fadeOut(“slow”, function(){
$(this).remove();
});
}
}
232
});

Чтобы включить данную функцию в сценарий, внесем изменения в


обработчик события клика на кнопку “Закрыть”.

Вызов функции закрытия окна. Листинг 2.19


$(“li>a”).live(‘click’, function(event){
event.preventDefault();
// Проверим, что сработал обработчик события и выведем текст ссылки
в консоль firebug
var data = $(this).text();
modal = fx.initModal();
$(“a”).attr(“href”, “”)
.addClass(“modal-close-btn”)
.html(“&times;”)
.click(function(event){
fx.boxout();
});
$.ajax({
type: “Post”,
url: “ajaxfile.php”,
data: “url=” +data,
success: function(data) {
modal.append(data);
},
error: function(msg) {
modal.append(msg);
}
});
});

Окончательный код

Модальное окно. Листинг 2.20


<script type="text/javascript">
jQuery(function($){
var fx = {
'initModal': function(){
if($(".modal-window").length == 0){
$("<div>")
.attr("id", "jquery-overlay")
.css({
"background":"#000 repeat-x",
"opacity": "0.7",
"position":"fixed",
"height":"100%",
"width":"100%",
"left" : 0,
"top":0
})
.fadeIn("slow")
.appendTo("body");
return $("<div>")
.addClass("modal-window")
.fadeIn("slow")
.appendTo("body");
233
}else{
return $(".modal-window");
}
}
}

$(".mainblocktext a")
.live("click", function(event){
event.preventDefault();
var data = $(this).attr("href").replace("<?=Kohana::
$base_url?>", "");
var modal = fx.initModal();
$("<a>").attr("href", "#")
.addClass("modal-close-btn")
.html("&times;")
.click(function(event){
event.preventDefault();
$(".modal-window").fadeOut("slow",
function(){$(this).remove();});
$("#jquery-overlay").fadeOut("slow",
function(){$(this).remove();});
})
.appendTo(modal);
$.ajax({
type: "POST",
url: "<?=Kohana::$base_url?>tovars/tovarsajax",
data: "id=" + data,
success: function(data){
modal.append(data);
},
error: function(msg){
modal.append(msg);
}
});
});
}
)
</script>
234

3.Node.js

Node или Node.js — серверная платформа, использующая язык


программирования JavaScript.

HTTP-сервер
Можно сказать, что HTTP-протокол (HyperText Transfer Protocol – протокол
передачи гипертекста) является частным случаем протокола TCP. Так и в
ядре Node.js модуль для создания протокола HTTP (который так и называется
http) наследует функциональность модуля Net (модуль протокола TCP).
Модуль HTTP представляет базовую HTTP-функциональность,
обеспечивающую приложению сетевой доступ к запросам и ответам.
Рассмотрим пример создания HTTP-сервера:

Создание HTTP-сервера. Листинг 3.1


var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, {‘content-type’: ‘text/plain’});
res.end(‘Hello world!’);
}).listen(8128);
console.log(‘Server running on 8128’);

Набираем в браузере http://127.0.0.1:8128 и увидим на экране Hello world!


Следует обратить внимание на важную деталь: если мы запустим еще один
процесс, то консоль выдаст ошибку. Система не может слушать один и тот
же порт дважды.
Для того чтобы еще раз запустить прослушивание того же порта, необходимо
закрыть предыдущее прослушивание.
С помощью функции createServer и безымянной функции обратного вызова,
создается новый сервер. Входящие параметры функции обратного вызова:
req (серверный запрос или поток чтения – это объект http.serverRequest) и res
(серверный ответ или поток записи – это объект http.serverResponse).
У объекта http.serverResponse имеются следующие методы:
 res.writeHead(), который отправляет заголовок ответа с кодом статуса
ответа.
 res.end(), который подает сигнал о завершении передачи данных и тело
ответа для вывода на экран.
 res.write(), который выводит данные на экран без сигнала о
завершении переадчи данных.
Метод http.Server.listen прослушивает входящие подключения к заданному
порту. Метод listen является асинхронным, т.е. не блокирует выполнение
235
программы в ожидании подключения. Поэтому, функция console.log()
листинга может выполниться раньше подключения.
Кроме потока чтения и записи, HTTP поддерживает кодировку
фрагментированной передачи. Этот тип кодировки применяется для
обработки больших объемов данных. При этом запись данных может
начаться еще до получения оставшейся части запрошенных данных.
Модули Net и HTTP могут также подключаться к UNIX-сокету, а не к
конкретному сетевому порту, что позволяет поддерживать взаимодействие
между процессами в пределах одной и той же системы.
236

4. Express-приложение

Создать шаблонный проект node.js приложение довольно просто.

Сперва устанавливаем фрэймворк Express в качестве глобального node-


модуля.

Установка глобального модуля Exress. Листинг 4.1


npm install –g express
//или
npm i –g express-generator@3

Через проводник заходим в нужный каталог, в котором хотим создать


приожение. Запускаем консоль командной строки.

В открывшейся командной строке набираем:

Создание шаблонного приложения site. Листинг 4.2


Express site // установка в дирректорию site
Express // установка в отркытую дирректорию

Приложение создает каталог site со следующими подкаталогами: public,


routes, views. И файл app.js, который, в свою очередь, создает сервер.
Рассмотрим его.

Установка зависимостей

После установки приложения установим все необходимые зависимости,


которые прописаны в файле package.json.

Установка зависимостей. Листинг 4.3


Npm i

Такая команда в консоли установит две зависимости: express и jade.


237
Далее необходимо добавить прослушиватель нужного порта в файле app.js.
Прослушиватель создадим в конце файле, перед module.exports, когда уже
вся промежуточная логика объекта app() объявлена.

Добавление прослушивателя в файле app.js. Листинг 4.3


app.listen(3000)

Запуск приложения через консоль

Запуск приложения. Листинг 4.4


node app.js

Шаблон

В файл index.jade добавим ссылки.

Index.jade. Листинг 4.5


extends layout

block content
h1= title
nav.topmenu
a(href='index') Главная
a(href='news') Новости
a(href='services') Услуги
a(href='concatct') Контакты
div.mainblock
h2 Главная
div.block_for_text.
Завершать элемент можно точкой, показывающей, что дальнейший
блок содержит только текст.
div.copyright &copy; Все права защищены. MyStudio 2014г.

Настройка, файл app.js

Настройка маршрутов осуществляется в файле app.js. Добавим еще один


маршрут (в листинге выделен жирным).

Настройка маршрутизации. Листинг 4.6


app.get('/', routes.index);
app.get('/users', user.list);
app.get('/:id', routes.index);

В файле route/index.php проверим на существование параметр req.params.id.


Если такая переменная есть, то в переменную var indx добавляем значение
req.params.id. В противном случае, переменная indx равна значению по
умолчанию. Далее, вместо значения ‘Express’, в шаблон передаем
переменную indx.

Значение из адресной строки. Листинг 4.7


238
if(req.params.id){
var indx = req.params.id;
}else{
var indx = 'index';
}

Конфигурирование

Подключим модуль nconf. Для этого в адресной строке набираем:

Файл index.js. Листинг 4.8


npm i nconf

Любой модуль, который мы подключаем, необходимо прописать в файле


packege.json:

Добавляем зависимость в файл packege.json. Листинг 4.9


{
"name": "application-name",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "3.4.4",
"jade": "*",
"nconf": "*",
}
}

Далее создадим папку config, в папке config – файл index.js

Файл index.js. Листинг 4.10


var nconf = require('nconf');
var path = require('path');

nconf.argv()
.env()
.file({file: path.join(__dirname, 'config.json')});

module.exports = nconf;

Как видно из листинга, модуль nconf подключает json-файл config.json,


который должен находится в текущей директории. В данном файле будем
хранить все настройки сайта.

Файл config.json. Листинг 4.11


{
"port":3000
}
239
Внесем изменения в файл app.js

Использование конфигурационного модуля в файле app.js. Листинг 4.12


var config = require('./config');

http.createServer(app).listen(config.get('port'), function(){
console.log('Express server listening on port '
+ config.get('port'));
});

Подключение базы данных

Будем работать с модулем Moongose, который необходимо установить.

Далее расширим конфигурационный файл:

Файл config.json с подключением базы данных. Листинг 4.13


{
"port":3000,
"mongoose": {
"uri": "mongodb://localhost/kurs",
"options": {
"server": {
"socketOptions": {
"keepAlive": 1
}
}
}
}
}

В папке config создадим еще один файл mongoose.js, задача которого


подключиться к базе данных. Конфигурацию подключения будем брать из
файла json.

Mongoose.js. Листинг 4.14


var mongoose = require('mongoose');
var config = require('../config');

mongoose.connect(config.get('mongoose:uri'),
config.get('mongoose:options'));

module.exports = mongoose;

В корне проекта создадим папку для моделей models и модель.

Модель themas. Листинг 4.15


var mongoose = require('../config/mongoose'),
Schema = mongoose.Schema;

var schema = new Schema({


name: {
type: String,
240
required: true
},
body: {
type: String,
unique: true,
required: true
},
url: {
type: String,
unique: true,
required: true
},
});

exports.Themas = mongoose.model('themas', schema);

После того, как модель готова, вставим данные. Для этого, например, в файле
app.js выполним следующий код.

Вставка значений в коллекцию themas. Листинг 4.16


var Themas = require('./models/themas').Themas;
var themas = new Themas({
name: 'Добро пожаловать на сай',
body: 'Текст для главной',
url: 'index',
});
themas.save(function(err, user, affected){
console.log('Ok');
});

Чтобы выполнить вставку значений, в консоли перезапустим (или запустим


заново) приложение.

Перезапуск приложения. Листинг 4.17


node app.js

После выполнения в коллекцию themas вставятся новые данные. Далее


заменим данные для других страниц и еще раз выполним перезагрузку. И так
несколько раз до тех пор, пока коллекция не заполнится.

После чего необходимо удалить код, отвечающий за вставку значений.

Обновим файл index.js из папки routes, где и будет осуществляться


непосредственный запрос в базу данных.

Обработка и передача данных в шаблон. Листинг 4.18


exports.index = function(req, res){
if(req.params.id){
var indx = req.params.id;
}else{
var indx = 'index';
}
var Maintexts = require('../models/maintexts').Maintexts;
241
Maintexts.findOne({'url':indx}, function(err, ttext){
if(!ttext){
ttext = {
name:'Добро пожаловать на сайт',
body:'Извините, страница не найдена'
}
}
res.render('index', {
ttext: ttext,
});
});
};

В шаблоне index.jade переменная ttext будет выводиться так:

Вывод значений в шаблоне index.jade. Листинг 4.19


h2 = ttext.name
div = ttext.body

Если в переменной ttext.body имеется html-код, то выводить эту переменную


нужно по-другому:

Вывод html-кода. Листинг 4.20


!{ttext.body}

Подключение скриптов и стилей

Пути к скриптам и стилям лушче всего держать в конфигурационном файле в


элементах массива. Так мы сможем управлять массивом, добавлять или
удалять стили и скрипты на разных страницах приложения

Массив scripts в конфигурационном файле со скриптами по умолчанию.


Листинг 4.21
"scripts": ["/javascripts/angular.min.js",
"/javascripts/ui-bootstrap-tpls-0.10.0.min.js"
],

В файле app.js после подключения конфигурационного файла извлечем


данный массив и передадим его в шаблон layout.jade, используя объект locals

Передача массива в шаблон jade. Листинг 4.22


app.use(function (req, res, next) {
res.locals = {
userid: req.session.user,
title: 'On-line Университет развития личности',
scripts: config.get('scripts')
};
next();
});

В главном файле шаблона layout.jade, с помощью цикла for, пройдемся по


всем элементам массива:
242
Вывод скриптов на экран. Листинг 4.23
for script in scripts
script(src='#{script}', type='text/javascript')

Далее можно наращивать массив script, добавляя в него необходимые


элементы в новых контроллерах:

Добавление нового элемента массива. Листинг 4.24


exports.index = function(req, res, next) {
congif = require('../config');
scripts = congif.get("scripts");
scripts[2] = '/vendor/bower_components/jquery/jquery.js';

res.render('chat', {
scripts: scripts
});
};

В scripts[2] индекс 2 указывает на то, что мы добавляем 3-ий элемент


массива.
243

5. Обзор рынка IT

С учетем скорости развития информационных технологий, прогноз и обзор


перспективных web-технологий, можно дать только на 3-4 года вперед.

Новой технологии, которая сможет объединить серверное (бэкенд) и


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

В последнее время наблюдается бурный рост рынка IT: за последние


несколько лет количество вакансий на рынке IT-труда увеличилось на 40%
(!).

Рост вакансий на рынке IT-труда связан со следующими показателями:

 Рост и развитие самих компаний (причем, рост и развитие – это разные


показатели, которые рассмотрим позже). Т.е. открываются новые
вакансии под новые web-технологии.
 Текучка кадров. В сравнении с другими рынками, на рынке IT – самая
большая текучка кадров.

Текучка кадров бывает двух видов:

 Положительная. Лучшие специалисты уезжают на ПМЖ в Москву,


Европу, США. Это утечка продуктивных программистов или, как
раньше было модно говорить, утечка “мозгов”.
 Отрицательная. IT-компаниям под новый проект либо под замену
мигрировавшим специалистам, проще найти новых специалистов на
рынке труда, чем переобучать своих. Но в такой сложившейся
ситуации виноваты сами программисты этих компаний, которые вскоре
пополнят ряды безнадежных безработных. Именно в силу свой
высокой квалификации, они не могут пересилить себя, согласиться на
добровольное отречение от устаревших знаний, которыми они
владеют.

Следует различать два вида программирования, а, соответственно, и


программистов, занятых в этой сфере:

 Репродуктивное программирование. Это люди, которые закончили


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

 Продуктивное программирование. Это специалисты, которые


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

Компании находятся в постоянном поиске и тех, и других специалистов. Но,


если репродуктивные программисты – это программисты одного проекта
(узконаправленные специалисты ищутся под конкретный заказ/проект), то
продуктивные вскоре становятся незаменимыми сотрудниками в любом
проекте.

Отличительная черта продуктивных программистов – способность к


развитию и саморазвитию. Они обретают способность решать всё более
сложные самоорганизационные задачи, что выражается, прежде всего,
возрастающей сложностью используемых для этого мыслительных моделей
(web-технологии, паттернов проектирования, языков программирования…).
Причем, они не только саморазвиваются, но и подталивают к развитию
компанию.

Развитие – это способность адекватно реагировать на изменения во


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

Так, рост компании не подразумевает её развитие: можно нанять 100 человек,


которые будут делать одни и те же механические дейтсвия, и это будет
ростом компании, т.к. увеличивается её численность. Можно нанять одного
продуктивного программиста, который одним технологическим решением
сможет заменить 100 человек, и это будет развитием компании.

Следует помнить, что в IT один год равен пяти. Это значит, что какими бы
мы продвинутыми специалистами на данный момент не были, через
несколько лет наши знания устареют. Если мы за это время не успеем
перестроиться, получить новые навыки, то останемся в неудел. Но для того,
чтобы перестраиваться, нужно знать куда.

Чтобы определить направление этого рынка, нужно обладать развитой


интуицией и хорошими аналитическими способностями.

Два направления web-технологий: серверный (backend) и клиентский


(frontend).

Рассмотрим современный FrontEnd, BackEnd, их взаимодействие между


собой и вспомогательные web-технологии.
245
Инструментарий

 IDE: NetBeanse (+PHPDoc), Adobe Breackets, PHPStorm, WebShtorm,


SablimeText3, и WebMatrix компании Microsoft
 Apache, права доступа 777 Windows Server
 Linux
 GIT, система контроля версий
 bitbacket.org, github.com – репоозитории для git-проектов

FrontEnd

1.      Гибкая блочная верстка.

2.      Резиновая и фиксированная верстка, традиционная блочная и табличная

3.      Адаптивная верстка.

4.      JavaScript, jQuery, Node.js

5.      JSON

6.      Селекторы.

7.      архитектурный шаблонMVVM

8.      JavaScriptи библиотеки jQuery(библиотека запросов)

9.      Backbone – MVVM (ModelView-ViewModel) библиотека. Backbone


требуетUnderscore.js иjQuery. Если они не нужны можно использовать
Exoskeleton– форк Backbone, где никаких зависимостей не нужно. Данную
библиотеку рационально использвать в крупном и среднем проекте, который
работает со множеством данных,

10.Angular и KnockoutJS.

11.BackBone-фрэймворк – Marionette.js(модульный фрэймворк)

12.CoffeeScript

13.Less: переменные, миксины или функции, расширения, импорт,


вложенность, соединение в одно свойтсво несколько свойств.

14.Sass: переменные, вложенность, импорт файлов, миксины или функции,


логические операции. Фрэймворк Compass(compass-style.org) на основе Sass.

15.Live Editor,CKEditor
246
16.Системы сборки FrontEnd-а.

17.Jade

18.MongoDB

19.HTML5 и APIHTML5: видео и аудио, холст, перетаскивание, работа с


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

20.Bootstrap3, APIBootstrap

21.Браузерные web-консоли. В частности, FireBugдля FireFox.

22.Основы SEO.

23.Shema.org, генератор shema.orgмикроформат данных, микроданные

24.Photoshop.

BackEnd

1) Языки программирования:

·        PHP+

·        Java+

·        Pithon-

·        Perl-

·        Ruby-

·        Asp.net -

2) Сервера (это ПО, получающая запросы от клиента, либо компьютеры, где


это ПО находится):

3) СУБД (это ПО предназначенное для работы с базами данных):

·        MySQL (PHPMyAdmin)

·        NoSQL, SQL-lite

·        Oracle
247
·        MS-SQL

4) Понятие паттернов. MVC, HMVC. Маршрутизация. Взаимодействие с


.htaccess.

5) ООП.

6) Один, лучше 2-3 фрэймворка. Для PHP– это Kohana, YII, ZendFramework,
Symfony+Доктрина (для работы с запросами), Laraval. Самый быстрый
Falcon– расширение для PHP, написано на C++, чтобы поставить, необходим
выделенный сервер, или надо просить хостинговую компанию.

7) CMSDrupalи 1Cбитрикс (для PHP) MoDex. Joomla, WordPress

8) Шаблонизатор twig

10) Системы контроля вервсий: GIT. Mercurial, SVN, Subversion.


Репозитории.

11) Менеджер зависимостей: Composer.

12) Регулярные выражения, Строковые функции

13) Платежные системы.

14) Парсинг. CURL

15) UnitTest

16) Умение делать аудит, анализ и консультирование проектируемых и уже


существующих сайтов. Ajile-технологии разработки проектов. Умение
разрабатывать проекты без технического задания.
248

Михалькевич Александр Викторович

PHP PRO

Авторская редакция
Компьютерная верстка
249

Подписано в печать 12.01.2016. Формат 60х84 1/16.


Бумага HPOffice, Печать лазерная.
Усл. печ. л. . Уч.-изд. л. .
Тираж 1000 экз. Заказ.