{{Laracast Laravel 5 Fundamentals, 25, When You Want a View Partial to Always Receive Data, 11.02.2015, 27.07.2016, https://laracasts.com/series/laravel-5-fundamentals/episodes/25}}
%%(hvlraw)
%%
(0:00)
Вот один момент, который вы обнаружите, когда начнёте самостоятельно строить свои приложения. Вы окажетесь в таких ситуациях, где кажется, что вы постоянно получаете один и тот же набор данных и передаёте его в одно и то же новое представление. Например, допустим, вам нужно подготовить некоторую информацию из базы данных, чтобы заполнить вашу панель навигации. ОК, это нормально.
Вы делаете это для вашей страницы %%(t)articles%%, но потом понимаете, что для любой другой страницы не нужно делать это снова, поскольку все эти представления содержат панель навигации, так что мне нужно получать эти данные каждый раз.
(0:30)
И очень быстро ваши контроллеры будут ужасно выглядеть, потому что вы беспричинно дублируете эти вызовы снова и снова, лишь потому что вы не знаете, что ещё можно тут сделать.
Поверьте, есть более простой способ, так что давайте используем этот пример навигационной панели. Я нахожусь на сайте %%(t)getbootstrap.com%%, где есть куча примеров, и мы используем Bootstrap, так что мы довольно быстро с этим разберёмся.
(1:00)
Давайте скопируем это, а затем я вернусь к своему редактору, к нашей главной странице, и затем вставлю этот код прямо здесь. Или опять же, если вы хотите, чтобы всё было действительно чисто и решите извлечь этот код в partial, то я не вижу с этим никаких проблем.
Хорошо, давайте оставим это как есть и переключимся обратно в Chrome, обновим, и готово, но, похоже, нам нужно немного увеличить отступ. Перейдём к главной странице стилей приложения (%%(t)app.scss%%) и давайте увеличим его до 60 пикселей:
%%(css)
body {
padding-top: 60px;
}
%%
(1:30)
ОК, я запущу Gulp чтобы скомпилировать всё, и теперь, если я переключусь обратно и обновлю, вот наша панель навигации, и мы создали её очень легко с помощью Twitter Bootstrap. Круто.
Так что давайте возьмём 30 секунд и заполним некоторые из этих ссылок. Мы установим имя сайта как %%(t)Blog%%, и сделаем раздел для наших статей %%(t)%%, и пожалуй пока этого достаточно. Обновим и готово.
(2:00)
Теперь мы можем сделать ссылку на главную страницу, вот так, где у нас даже нет никаких стилей, но также у нас есть ссылки на странице со статьями и все остальное, что может понадобиться. Хорошо, так что дальше, на самом деле в основном ради примера, давайте представим, что нашей панели навигации нужно что-то из нашей базы данных, и на самом деле в этом маленьком простом приложении у нас там почти ничего нет. Так что, может быть, в верхнем правом углу мы хотим просто отобразить самую свежую опубликованную статью.
(2:30)
Тогда независимо от того, где вы находитесь на сайте, вы можете видеть самую последнюю статью в самом верху.
Это звучит вполне разумно. Так почему бы нам не сделать чтобы эта страница так работала? Тогда мы увидим проблему: нам придётся делать это каждый раз, и мы будем их решать с помощью того, что мы называем, компоновщиком вида. Так что я переключусь обратно, и прямо здесь находится наш раздел основной навигации.
Давайте создадим вторую навигацию, и она будет в правой стороне экрана и мы можем использовать Bootstrap-класс %%(t)navbar-right%%, чтобы сдвинуть её вправо:
%%(h)
%%
(3:00)
Теперь здесь, во-первых, позвольте мне показать вам это, вот, готово, вы можете её видеть. Но опять же, мы хотим, чтобы здесь было название наиболее недавно опубликованной статьи. Так что заменим это нашей ссылкой, которую сгенерируем здесь.
Опять же, не забывайте, что у вас есть метод %%link_to()%%. Есть %%link_to_action()%%, есть %%link_to_route()%%, можно даже вручную это сделать, не забывайте. Так что если вы хотите заполнить это, то можете использовать функцию %%url()%% или %%route()%% или %%action()%% для подготовки URL.
(3:30)
Любой из них прекрасно подойдёт, просто выберите один и, возможно, попытайтесь придерживаться его, но да любой из них подойдёт. Я уже показал вам, как использовать %%link_to_action()%%, так что давайте применим его?
%%
- {!! link_to_action('ArticlesController@show', $latest->title, [$latest->id]) !!}
%%
телом будет что-то вроде %%$latest->title%%. Может быть, сделаем здесь переменную %%$latest%% и она будет ссылаться на последнюю опубликованную статью.
(4:00)
Просто знайте, что мы ещё не сделали этого. И, наконец, не забывайте, что мы хотим показать статью, поэтому %%(t)articles/{id}%% с каким-нибудь идентификатором. И мы должны сказать Laravel какой из них мы хотим, и в данном случае это будет %%(t)id%% последней статьи. В этом случае мы всё ещё используем идентификатор статьи внутри URL. На самом деле это нужно изменить. Мы должны сделать что-то вроде транслитерированной строки, но мы сделаем это вместе чуть позже, это будет хорошим примером.
Хорошо, но, конечно, если мы перейдём назад и обновим это, то получим ошибку, не так ли?
(4:30)
Так как мы ещё не установили эту переменную. Так что это наш следующий шаг.
Мы идём к %%ArticlesController%%, и по крайней мере, мы думаем, что это следующий шаг. Мы идем к методу %%index()%%, и затем мы передаём то, что следует рассматривать в качестве переменной %%$latest%%. В этом случае мы могли бы сказать:
%%
$latest = Article::latest()->first;
return view('articles.index', compact('articles', 'latest'));
%%
На самом деле легко. Так что, возвращаемся к Chrome, обновим, и да, похоже на нашу последнюю статью. И похоже, что у меня тут была небольшая опечатка, но это не имеет значения.
(5:00)
Но как бы хорошо это не выглядело, если мы нажимаем на неё, то блин, опять ошибка. Если попытаемся пойти изменить статью, это тоже не удастся.
Если попытаемся создать новую статью, тоже не работает. Вы догадываетесь о чём я? Поскольку мы добавили это к панели навигации и поскольку каждому виду нужна панель навигации или допустим боковая панель или какой-то модуль в боковой панели, то есть вещи такого рода.
И так как им требуются эти данные, вполне ясно, что мы не хотим делать одно и то же каждый раз.
(5:30)
Да, это будет работать в данном случае, но это плохой способ, так что я отменю это. Вместо этого, похоже нам нужен способ сказать: "Каждый раз, когда мы готовим представление для этой панели навигации, я хочу обеспечить его этими данными, потому что они потребуются, так что, Laravel, каждый раз, при его построении, так сказать, я хочу убедиться, что у тебя есть эти данные".
Я надеюсь вы понимаете о чём я.
(6:00)
Вот что мы сделаем – я вернусь к своей главной странице, и я решил, что помещу этот код в его собственном представлении. Здесь очень много всего происходит, и когда я буду смотреть на этот код через полгода, то было бы чище, если бы я извлёк его в маленькие partial'ы. По крайней мере я бы предпочёл так сделать. Так что в %%(t)resources/views/partials%% мы создаём %%(t)nav.blade.php%%. И я включу этот partial:
%%
@include('partials.nav')
%%
(6:30)
Далее я создам компоновщик вида (view composer), который говорит: "Каждый раз, когда вы притягиваете этот конкретный вид, я хочу убедиться, что я даю вам для работы объект в переменной %%$latest%%". Теперь, чтобы начать, мы добавим это к поставщику, %%AppServiceProvider%%, и затем позднее, потому как нам, возможно, придётся делать это много раз в приложении, мы создадим отдельного поставщика в конце видео. Ваш типичный поставщик услуг...
(7:00)
Мы ещё не говорили о них подробно, поскольку это немного более высокий уровень, чем тот где вы сейчас находитесь, но основная идея в том что у вас есть два метода: %%register()%% и %%boot()%%. Я снова буду использовать немного раздражающую терминологию, но %%register()%% нужен для привязки вещей к IoC-контейнеру в Laravel. Вы ещё можете увидеть, что его называют контейнером услуг. Это даёт нам хороший способ, например, ассоциировать интерфейсы с конкретными классами.
(7:30)
Или ещё это дает нам возможность сказать: "Каждый раз, когда я запрашиваю класс такого рода, я хочу убедиться, что вы построите его для меня в таком конкретном стиле". И опять же, если это пролетело мимо ушей, то это нормально, мы вернёмся к этому в будущем.
Пока что пусть это поварится у вас в голове. Далее у нас есть метод %%boot()%%. Думайте о методе %%boot()%%, как о том, что вызывается после того, как все поставщики услуг были зарегистрированы. Другими словами, как только всё готово к работе, вызывайте метод %%boot()%% на этих поставщиков и делайте всё, что вам нужно.
(8:00)
В этом случае мы хотим зарегистрировать компоновщик вида, так что именно это мы и будем делать:
%%
view()->composer('partials.nav');
%%
и имя вида который мы хотим скомпоновать здесь будет %%(t)partials/nav%%. Мы могли бы сделать %%(t)partials/nav%% или мы можем ещё использовать точечную нотацию, так что сделаем так.
Далее, я мог бы либо ссылаться здесь на класс, потому что часто вы захотите выделенный объект, для обработки этого процесса или мы могли бы использовать замыкание здесь, вот так.
(8:30)
Так что, когда Laravel компонует вид под названием %%(t)partials.nav%%, и затем передаёт объект %%$view%%, то я хочу привязать некоторые вещи к этому виду. Мы скажем:
%%
$view->with('latest', \App\Article::latest()->first());
%%
и давайте возьмём и импортируем это.
Хорошо, мы создали наш первый компоновщик вида. Так что, если я теперь переключусь обратно в Chrome и обновлю страницу, теперь всё работает.
(9:00)
И даже лучше, поскольку эта переменная теперь привязана к виду навигации, а не к конкретному методу контроллера, это будет работать везде, где мы используем область навигации. Итак, давайте создадим новую статью:
%%(t)
laravel5.dev/articles/create
%%
всё работает.
Или давайте просмотрим статью и изменим её:
%%(t)
laravel5.dev/articles/16/edit
%%
Это по-прежнему будет работать. Очень хорошо, но теперь давайте перейдем обратно к PhpStorm. Я говорил о создании для этого специализированного поставщика услуг. Если бы он был у нас один, то я думаю этого бы было достаточно.
(9:30)
Но представьте себе, если у вас их несколько, то очень быстро ваш обычный поставщик услуг в приложении будет раздуваться. Так что вместо этого, если мы ожидаем что их у нас будет много, то давайте дадим им свой собственный дом и своего собственного поставщика услуг. Если я переключусь обратно к терминалу, у нас есть для этого генератор:
%%(sh)
php artisan
%%
Я прокручу вверх, и вот что мы хотим (%%(t)make:provider%%).
%%(sh)
php artisan make:provider ViewComposerServiceProvider
%%
(10:00)
Хорошо, так что теперь я возьму всё вот это, переключусь на мой новый %%ViewComposerServiceProvider%% и вставлю здесь, но пока этого ещё недостаточно. Если я переключусь обратно в Chrome, и мы постараемся обновить одну из этих страниц, мы вернулись к тому где были раньше, и это потому, что, да, вы создали нужный файл, но вы не сказали Laravel, что этот новый поставщик услуг существует.
(10:30)
Вот когда вам нужно пойти в файл %%(t)config/app.php%%, и на самом верху, вы помните, когда мы говорили о том, что здесь Laravel определяет всех своих различных поставщиков услуг? Здесь мы можем добавить ещё одного:
%%
'App\Providers\ViewComposerServiceProvider',
%%
Вот так, теперь Laravel знает о нём.
Так что, если мы перейдём назад к нашему %%ViewComposerServiceProvider%%, не забывайте, что мы должны импортировать это ещё раз.
(11:00)
ОК, это всё выглядит хорошо. Если мы переключимся назад и обновим, мы вернулись туда, где были раньше, но теперь думайте об этом как о специальном доме для наших компоновщиков видов.
Теперь снова, ещё дальше, если вы предполагаете, что у вас их будет куча, то, ещё раз, это может стать немного запутанным, потому что вам придётся пробраться через них, чтобы выяснить, что именно вы хотите. Тогда вы можете захотеть сделать что-то вроде этого, вы можете извлечь это в метод.
(11:30)
И назвать допустим %%composeNavigation()%%. И готово.
Так что теперь, когда мы вернёмся к этому, всё стало чуть более понятным. Если я заинтересован в компоновщике для области навигации, то я могу перейти в этот выделенный метод.
%%(t)
* Создать панель навигации.
%%
Ещё раз, переключимся назад, обновим, и это всё ещё работает. Теперь, чтобы закончить, как я уже сказал, могут быть некоторые случаи, когда это не настолько просто, как такой быстрый вызов. Вам может понадобиться сделать какие-то вычисления и вам потребуется несколько методов, и, возможно, много методов, чтобы выяснить всё это.
(12:00)
В тех ситуациях не стоит загрязнять поставщика услуг. Это не его забота заниматься всеми этими вещами, так что вместо этого, в таких случаях, вот что вы можете сделать.
Вы можете сказать:
%%
view()->composer('partials.nav', 'App\Http\Composers\NavigationComposer')
%%
на этот раз я не стану передавать замыкание. Я передам какой-то путь класса как строку, а затем за кадром Laravel обнаружит: "О, они пытаются найти класс из моего контейнера сервисов, так что я создам его и вызову на нём особый метод".
(12:30)
Просто имейте в виду, что для наших конкретных потребностей, мы придержимся этого, но гипотетически, если вам нужно больше, то, это то что вы можете сделать.
Если мы оставим всё как есть, Laravel построит этот объект для нас и вызовет на нём метод %%compose()%%, но если вы хотите, чтобы он вызвал что-то другое, то просто обновите его вот так.
(13:00)
Хорошо, так почему бы нам это не настроить? Мы добавим новый каталог для наших компоновщиков, а затем внутри класс с именем %%NavigationComposer%%, с пространством имён %%App\Http\Composers%%.
Теперь, как я уже сказал, по умолчанию будет вызываться метод %%compose()%%, а затем здесь передаваться объект %%$view%% (и я импортирую это). Несмотря на то, что кстати, вы не должны этого делать, но просто чтобы дать вам представление о том, с каким конкретно объектом вы работаете.
(13:30)
Так что если вы хотите оставить это так (и многие так и делают, многие сообщества даже предпочитают это), то это нормально. Вы не обязаны этого делать. Это просто сводится к предпочтению. В любом случае, если мы перейдём назад, теперь мы можем схватить это ещё раз, вставить здесь, или сделать всё, что вам нужно. Но если мы вернёмся и обновим, теперь мы вернулись туда, где были раньше, но на этот раз мы используем специальный класс компоновщика.
(14:00)
И это может быть полезно, когда вам нужна передышка для подготовки и создания конкретного вида. Или, может быть виду нужна куча объектов, в таких случаях, ещё раз, очень легко иметь специальный класс для компоновки каждого из них. И дальше, вы можете определить любые зависимости, которые вам нужны в конструкторе. В данном случае мы используем модель Eloquent напрямую, но в будущем вы узнаете о таких вещах, как хранилища, которые могут быть на первый взгляд немного сложными.
(14:30)
В основном думайте о них как о коллекциях, и хорошая выгода от них в том, что мы можем принимать сложные запросы и изолировать их в методы с читаемыми именами.
В этом случае, как я уже сказал, это легко. Но что, если вам нужно включить что-то и что если вам нужно что-то присоединить вручную, и далее, что если есть какие-то конкретные условия и тому подобное? Очень быстро это станет запутанным.
(15:00)
Вместо этого, вы захотели бы сделать что-то вроде:
%%
$view->with('latest', $this->articles->ofSomeType());
%%
Получить мою коллекцию статей, и затем дать мне те, которые соответствуют этому читаемому имени %%(t)ofSomeType%%, независимо от того, какое будет соответствующее имя метода.
Это может быть намного чище, и затем вы берёте этот специфический вызов Eloquent и оборачиваете его в это читаемое имя метода, и это может быть удобно. Так что, если вы выберете этот путь, вам просто нужно определить его. Так что вы можете сказать: "Дайте мне моё хранилище статей прямо здесь":
%%
public function __construct(ArticlesRepository)
%%
(15:30)
И поскольку Laravel настолько удивительный, он создаст его для вас автоматически, и это своего рода то, что мы имеем в виду, когда говорим о контейнерах сервисов.
И если вы услышите рядом жаргон, типа автоматической инъекции или автоматического разрешения, некоторые из таких запутанных, нечётких слов, то на самом деле это относится именно к ним. Вы определяете необходимую зависимость, а затем Laravel будет делать всё возможное, чтобы построить и создать экземпляр этого объекта и передать его вам, например, так:
%%
public function __construct(ArticlesRepository $articles)
%%
(16:00)
Но в данном случае это просто не нужно. Мы всегда хотим придерживаться мнения, что всё должно быть простым-простым до тех пор, пока у нас больше нет иного выбора, кроме как добавить чуть больше сложности. В нашем примере, если мы перейдём назад, у нас здесь было всего 3 строки кода и они красиво справились с работой. Это то, что мы здесь и оставим, а далее, в следующем эпизоде, мы изучим что-нибудь другое.