{{Laracast Laravel 5 Fundamentals, 22, Selecting Tags From the UI, 6.02.2015, 18.07.2016, https://laracasts.com/series/laravel-5-fundamentals/episodes/22}} %%(hvlraw) %% (0:00) В последнем видео вы узнали об отношениях многие-ко-многим, и о том, как мы можем создавать сводные таблицы (pivot tables) и изображать их с помощью Eloquent. Так что теперь давайте прицепим всё это к нашему пользовательскому интерфейсу. Я думаю, прямо тут, ниже даты публикации, мы добавим список с множественным выбором, в котором пользователи смогут выбирать теги, которые они хотят применить к этой статье. Давайте начнём. Просто чтобы освежить нашу память из %%(t)articles/create%% мы включаем partial нашей формы. Давайте перейдём туда. (0:30) Хорошо, здесь мы добавим наше текстовое поле для тегов, но на самом деле это должен быть элемент %%(t)select%%. Порядок параметров здесь немного иной, у нас есть имя выбранного элемента, значения по умолчанию, я жёстко пропишу второй параметр (hardcode), а третьим параметром будет выбранный вариант в списке. И далее у нас есть любые дополнительные атрибуты, которые нужно применить к элементу: %% {!! Form::label('tags', 'Tags:') !!} {!! Form:select('tags', ['defaults'], null, ['class' => 'form-control']) !!} %% (1:00) Итак, позвольте мне показать вам, как это выглядит. Мы переключаемся в Chrome, и да, у нас есть элемент выбора, содержащий только лишь то жёстко-прописанное значение. Но, как я уже говорил, я хочу, чтобы здесь был множественный выбор, чтобы вы могли применить несколько тегов к вашей статье. ОК, чтобы это исправить, я добавлю атрибут %%(t)multiple%% к элементу выбора. Так что если я вернусь и обновлю страницу, теперь мы получаем кое-что другое. И если мы посмотрим в исходный код, то вы увидите, что наш атрибут был применён вот здесь. (1:30) OK, далее, мы, конечно же, не хотим жёстко-прописанные значения. Так что на самом деле это должно выглядеть как список тегов, не так ли? Ну, может быть, я назову это %%$tags%%, а затем передам его дальше. Давайте посмотрим, как это может выглядеть. Мы идём к нашему методу %%create()%%, мы извлекаем наши метки: %% $tags = \App\Tag::all(); %% и мы могли бы получить их все, но, как вы узнали в последнем видео, у нас на самом деле есть метод под названием %%lists()%%: %% $tags = \App\Tag::lists('name'); %% дайте мне массив всех значений из столбца, допустим, %%(t)name%%. (2:00) Хорошо, так почему бы нам не пойти дальше и не импортировать это? Передадим это в вид: %% return view('articles.create', compact('tags')); %% и давайте посмотрим, что тут получилось. Итак, вернёмся к Chrome, обновим страницу, и в данном случае у нас здесь просто один тег. Так что вот оно. Имея это в виду, почему бы нам не создать несколько тегов за кадром? %%(sh) php artisan thinker %% и затем: %% \App\Tag::create(['name' => 'work']); %% но знаете что? Мне только что пришла в голову мысль, я думаю, что это не сработает, и да, здесь ошибка. (2:30) Но это даст вам хороший пример. Мы уже немного говорили об уязвимостях связанных с массовым назначением, и о том, что Laravel держит нас в безопасности. В данном случае мы пытаемся передать %%(t)name%% для метода %%create()%%, но мы не позволяем это, поскольку по сути оно занесено в чёрный список. И в результате бросается исключение. Если мы вернёмся, давайте перейдём к нашей модели %%Tag%%, мы можем сейчас здесь её обновить. Заполняемым полем для %%Tag%% пока что будет %%(t)name%%: %% protected $fillable = [ 'name' ]; %% %%(t) * Заполняемые поля для тега. %% Хорошо, теперь я думаю, мы можем попробовать ещё раз. (3:00) И готово. Давайте добавим ещё один или, может быть парочку, может быть %%(t)food%%, и как насчёт %%(t)coding%%? %% \App\Tag::create(['name' => 'food']); \App\Tag::create(['name' => 'coding']); %% ОК, так что теперь, если мы сделаем: %% \App\Tag::lists('name'); %% мы получим массив из 4 элементов. Но на самом деле это даёт мне хорошую возможность показать вам кое-что ещё. Когда мы передаём этот массив в наш список опций для этого элемента select, их значения (option values) будут просто установлены в индекс этого массива. (3:30) То есть 0, 1, 2 и 3. Но на самом деле я хочу, чтобы значение опции было таким же, как сам текст, и я опишу это немного больше, когда мы дойдём до этого. Но пока что, заметьте что произойдёт, если мы передадим второй параметр, вот так: %% \App\Tag::lists('name', 'name'); %% ОК, мы заметим, что поле %%(t)name%% будет установлено равным ключу. И в конечном счете, это именно то, что мы хотим. Так что давайте вернёмся к нашему контроллеру здесь и обновим вот это: %% $tags = Tag::lists('name', 'name'); %% Класс. (4:00) Теперь здесь, в нашем виде для формы мы передаем этот массив всех тегов нашего приложения в поле выбора. Надеюсь это должно заработать. Так что я вернусь в Chrome, обновлю и готово. И если мы хотим выбрать более одного тега, то нам лишь нужно сделать Cmd+ или Ctrl+клик. Хорошо, но есть ещё одна вещь, которую мы должны принять к сведению. Если я переключусь обратно на %%ArticlesController%%, когда мы отправляем форму, я просто хочу показать вам, что мы здесь получим. Так что давайте отделим значение от этого поля выбора тегов: %% dd($request->input('tags')); %% (4:30) Хорошо, давайте заполним это, и я выберу их здесь все. Но обратите внимание, когда я нажал на кнопку, то только один из них был передан. Кажется все остальные были проигнорированы. И это потому, что мы должны работать с ним как с массивом. Так что, если мы вернёмся к форме, всё, что мы должны сделать, чтобы это заработало, это поменять вот здесь (%%(t)tags[]%%). Это всё, что мы должны сделать. Хорошо, давайте вернёмся, обновим и попробуем снова. Но на этот раз вы увидите, мы действительно получаем массив. (5:00) Отлично, это именно то, что мы хотим. Хорошо, давайте подумаем об этом. Мы отправляем форму, и теперь у нас есть массив тегов, которые мы хотим связать с этой конкретной статьёй. И вы уже знаете как использовать метод %%attach()%% для создания новой записи в нашей сводной таблице. Так что похоже, что у нас есть всё, что нам здесь нужно. Давайте попробуем: Я переключусь к нашему %%ArticlesController%%. Для начала, давайте будем здесь слегка многословными, а потом сделаем рефакторинг в конце видео. (5:30) Итак, мы хотим получить все теги, и мы знаем что можем сделать: %% $tags = $request->input('tags'); %% и это будет массив со всеми тегами что пользователь может добавить к статье. Но дальше задумайтесь, нам нужны идентификаторы этих тегов, потому что в конечном счете мы будем делать что-то вроде этого: %% $articles->tags()->attach([1, 2, 3, 4]) %% мы передаём массив всех идентификаторов тегов, с которыми мы хотим их связать. Но сейчас у нас есть просто имя тега. (6:00) Могли бы мы вместо этого просто установить это равным идентификатору тега? Давайте посмотрим, как это может выглядеть. На этот раз я снова сделаю %%dd()%%, но вы увидите, что итог будет несколько иным: %% dd($request->input('tags')); %% Идём обратно в браузер, обновим, добавим новую статью. Я выберу все теги, и теперь мы получаем только идентификаторы тегов, и на самом деле это именно то, что мы хотим в данном случае. Так что это сделает всё для нас намного проще. (6:30) Я могу изменить это на %%$tagIds%% и тогда мы можем сказать %%$article%%... но где мне взять мою статью? Мы создали её вот здесь и она будет возвращена нам, так что мы сможем сделать это. Теперь я могу сказать: %% $article->tags()->attach($tagIds); %% и, наконец, почему бы нам не убрать эту переменную? И вы знаете, на этом всё! Таким образом, мы начинаем с создания статьи и связывания её с аутентифицированным пользователем, а затем мы обновляем сводную таблицу. (7:00) Мы говорим %%$article%% для этой сводной таблицы %%tags()%%, я хочу связать её и использовать в частности, идентификатор этой статьи с этим массивом тегов. Поэтому обратите внимание, когда мы вызываем %%attach()%%, вы можете передать целое число, идентификатор или массив идентификаторов. Так что я думаю, теперь мы готовы попробовать это по-настоящему. Давайте напишем %%(t)Some Personal Article%% (Какая-то личная статья), вставим здесь фиктивный текст, и присвоим ей тег %%(t)personal%%, и допустим %%(t)coding%%. (7:30) ОК, мы добавляем статью. Всё сработало, если мы нажимаем на неё, то ещё не можем этого видеть, но давайте удостоверимся, что это сработало. %%(sh) php artisan tinker %% и мы хотим статью с %%(t)id%%авным 15: %% $article = \App\Article::find(16); %% потом скажем: %% $article->tags->toArray(); %% вот они – %%(t)coding%% и %%(t)personal%%. Так что теперь, когда мы знаем, что это работает, почему бы нам не отобразить их на странице? (8:00) Я переключусь на вид %%(t)articles/show%%, и затем как насчёт того, чтобы прямо здесь нам вставить теги: %%(h)
Tags:
%% это не будет самым красивым решением, но будет работать. Так что мы скажем: %% %% и позже, возможно, в следующем видео, я покажу вам, как превратить это в ссылку, так чтобы мы могли просмотреть все статьи в нашем блоге, которые связаны с этим конкретным тегом. (8:30) Хорошо, давайте перейдём обратно в браузер: %%(t) laravel5.dev/articles/16 %% обновим и готово. Вот наши теги. Но как насчёт какой-нибудь другой статьи? %%(t) laravel5.dev/articles/4 %% У нас нет здесь никаких тегов, но мы всё ещё видим надпись. Может быть, это нормально, или может быть, вы хотите проверить здесь, если у нас есть какие-то теги, то мы скажем, если это не пусто, т.е. %%(t)@unless%% наш список тегов пуст, то отобразить их: %% @unless ($article->tags->isEmpty()) ... @endunless %% Запомните, стоит думать об %%(t)@unless%% как об эквиваленте %%(t)IF NOT%% (если не). (9:00) Так что теперь, если я вернусь и обновлю, мы ничего не увидим. Но если у нас есть теги, то вы увидите их. ОК, это всё хорошо, но у нас всё ещё есть одна проблема. У нас есть форма %%(t)create%%, но как насчёт нашей формы %%(t)edit%%? Что ж, давайте посмотрим. К сожалению, у нас сразу возникла ошибка. Так что, похоже, для того, чтобы эта форма заработала, она должна содержать список тегов. (9:30) В этом видео мы просто пойдём по пути дублирования кода. Но я хочу, чтобы вы подумали о том, как было бы здорово, если бы был способ связать переменные с любым видом, даже если бы это был partial, который мы хотим включить? На самом деле есть такой способ, но мы пока не будем его рассматривать. Так что давайте вернёмся к %%ArticlesController%% и если мы пойдём к методу %%create()%%, мы захватили наши тэги. Так почему бы нам ещё раз просто не сделать то же самое временно вот здесь, в %%еdit()%%, и затем передаём дальше: %% $tags = Tag::lists('name', 'id'); return view('articles.edit', compact('article', 'tags')); %% (10:00) OK, обновим и теперь она отображается, но у нас есть новая проблема. Мы знаем, что у этой статьи есть 2 тега, %%(t)personal%% и %%(t)coding%%, но они здесь не отражены. Так как же нам это сделать? Давайте перейдём обратно в форму. Вы помните, что для нашего метода %%select()%% мы передали имя и различные опции, а затем, под конец идёт то, что следует рассматривать в качестве выбранного значения, и вы здесь можете передать строку или массив. (10:30) Так, например, если мы хотим, рассматривать тег с идентификатором 1 как выбранный, то вот, пожалуйста. Или как насчет другого? Как насчет %%(t)coding%%, который имеет %%(t)id%% равным 5? ОК, тогда мы можем сказать, 1 и 5 - [1, 5]. Обновим и теперь вы видите, как это работает, не так ли? Так что похоже нам нужно найти способ, чтобы связать это. И у нас есть несколько решений. (11:00) Вы можете вызвать какой-нибудь метод вроде %%$article->tagsList()%% непосредственно на статье, чтобы получить выбранные значения. Но мы используем привязку модели к форме, так что было бы здорово, если бы мы могли сделать что-то вроде этого: %% public function getTagsAttribute() %% Мы могли бы использовать метод вроде %%getTagsAttribute()%%, но это уже не будет работать, потому что если мы это перезапишем и вернём массив или что-то типа того, то каждый раз, когда мы попытаемся использовать %%$article->tags()%% в наших разных файлах, то он больше не будет работать, не так ли? Поскольку мы перезаписали его. (11:30) Тогда, возможно, мы могли бы использовать что-то вроде %%getTagListAttribute()%%. Хорошо? И когда мы будем говорить %%(t)tagList%% или %%(t)tag_list%%, то этот метод будет срабатывать. Тогда мы могли бы: %% return $this->tags->lists('id'); %% получить массив идентификаторов тегов, связанных с этой статьей. Хорошо, мы скажем: %%(t) * Получить список идентификаторов тегов, связанных с текущей статьей. %% и это должно будет вернуть нам массив. (12:00) Теперь, если мы перейдём обратно к %%(t)form.blade.php%%, мы можем изменить здесь с %%(t)tags%% на %%(t)tag_list%%. Далее я могу возвращать здесь %%(t)null%%, поскольку привязка модели позаботится об этом за меня. И чтобы доказать это вам, давайте вернёмся в браузер, обновим, и вот смотрите. Всё работает. Но только помните, мы изменили имя, так что давайте вернёмся к %%ArticlesController%%, где мы делаем %%store()%% для новой статьи и поправим здесь код соответствующим образом. (12:30) Хорошо, всё начинает выглядеть довольно здорово. Почему бы нам не запустить процесс с нуля чтобы увидеть поток работ? На этот раз это будет %%(t)Some Other Work Related Article%% (Ещё одна статья связанная с работой), добавим текст, и тегами здесь будут %%(t)work%% и %%(t)food%%. Хорошо, добавим статью, посмотрите на неё, у неё есть эти 2 тега, и если мы попытаемся её изменить, то они будут здесь выбраны. Всё очень круто, но я здесь оставлю вас с небольшой интригой. (13:00) Что если мы позже захотим изменить эту статью, и мы решим, что она не имеет ничего общего с едой, и я решу удалить этот тег? Хорошо, мы обновим статью и кажется, что всё работает, но мы посмотрим на неё и, минуточку, ничего не произошло, потому что ранее... Давайте я вам покажу. В %%ArticlesController%% мы по сути добавляли новые элементы для сводной таблицы. И мы не думали о процессе, когда пользователь решит удалить один из них. (13:30) Похоже нам нужно найти способ, чтобы просто синхронизировать их. Так что я оставлю этот вопрос открытым. Если вы хотите работать дальше и разобраться самостоятельно, то вперёд – станете сильнее! Если же нет – смотрите следующий урок.