Может войдёшь?
Черновики Написать статью Профиль

Основы Laravel 5: Формы

перевод Основы Laravel 5 Laracasts

Это перевод видео-урока с Laracasts, серия Laravel 5 Fundamentals, урок №10Forms от . Перевод обновлён . Опечатка? Выдели и нажми Ctrl+Enter.

(0:00)
Приветствую вас ещё раз. Итак, мы хорошо продвинулись – мы можем отобразить все наши статьи, и можем конечно же иметь дело с ними поодиночке. Теперь как насчёт основного процесса создания статьи? Для этого нам понадобится оконечный маршрут (endpoint), так? А также потребуется форма. Нам нужен способ получения данных из этой формы и добавления их в нашу таблицу статей. Нам также нужно будет перенаправить пользователя после создания им статьи. Так что должно произойти много всяких мелких вещей.

(0:30)
К счастью, Laravel опять же облегчит нам работу, так что давайте начнём писать код.
Если перейдём на routes.php, нам нужен новый маршрут, так что скажем: «Когда мы получаем GET-запрос к articles/create...» и кстати, заметьте что со всеми этими URI я следую общепринятому соглашению и мы поговорим об этом ещё в будущем уроке. По крайней мере пока что следите как я предпочитаю называть их, я не беру имена с потолка, а действительно следую соглашению.

(1:00)
Итак, когда пользователь заходит на ту страницу, мы хотим загрузить метод PHPcreate() на PHPArticlesController:

PHP
Route::get('articles/create''ArticlesController@create');

Это наш следующий шаг... PHPArticlesController, добавим новый метод здесь для PHPcreate() и загрузим вид articles.create:

PHP
return view('articles.create');

Довольно легко.
Дальше, конечно же, нужно его создать, так что идём в resources/views/articles, и добавляем новый файл create.blade.php, который расширяет мастер-страницу нашего приложения, и далее мы сделаем раздел для содержимого: @section('content').

(1:30)
Начнём с заголовка: <h1>Write a New Article</h1>, и затем горизонтальную линию <hr/>, и дальше нам тут нужна форма. Мы можем создать её вручную или притянуть стандартный для Laravel пакет, который нам упростит задачу. Давайте так и сделаем.
Обратно в терминал. Я сделаю require на новый пакет:

shcomposer require illuminate/html

(имя Illuminate используется Laravel для всех своих разных компонентов).

(2:00)
В нашем случае мы хотим illuminate/html, который конечно же не входит в базовую установку фреймворка. Давайте подтянем его, на это уйдёт секунд 10-15.
Итак, пока процесс идёт, давайте перейдём в браузер, а именно в репозиторий GitHub. Вот что мы загружаем в данный момент. Вы увидите нечто под названием PHPFormBuilder, а также PHPHTMLBuilder.
Думайте о них как о способах ускорения процесса создания форм и написания HTML.

(2:30)
В моём случае я в основном использую PHPFormBuilder, однако это просто общий класс. Было бы круто иметь хорошо налаженный способ для взаимодействия с ним, и здесь мы подходим к идее фасадов в Laravel. Мы будем говорить об этом подробнее в будущем видео, а пока что вперёд – посмотрим на него в действии и тогда вы начнёте понимать, когда стоит использовать фасад Laravel, а когда нет. В нашем случае, заметьте что у нас тут есть этот фасад формы, так как же нам сообщить Laravel об этом пакете, который мы только что притянули?

(3:00)
Ну что же, я вам покажу: для большинства пакетов в Сети, работающих вместе с Laravel, вы найдёте класс PHPServiceProvider. Считайте что это Laravel-специфичный класс для начальной загрузки. Он регистрирует любые объекты внутри контейнера Laravel, настраивает конфигурацию — в самом деле, он делает всё что вы захотите чтобы он делал.

(3:30)
Если всё что я вам только что сказал звучит абсолютно странно и ново, то опять же – это нормально. Просто следуйте шагам и в будущих видео мы ещё поговорим о вещах типа поставщиков услуг (service providers). Пока что мы просто хотим зарегистрировать его, и полный путь тут — PHPilluminate/html/HtmlServiceProvider. Так что если я перейду в мою директорию /config в app.php и прокручу вниз, вы увидите здесь большой список различных поставщиков услуг, нечто вроде кирпичиков для Laravel 5.

(4:00)
Заметьте, тут есть штуки для аутентификации (PHPAuthServiceProvider), командной шины (PHPBusServiceProvider), работы с кэшем (PHPCacheServiceProvider), наш PHPControllerServiceProvider, наш PHPValidationServiceProvider – все эти разные вещи. И если вы и правда хотите узнать больше о том как устроен Laravel, то рассмотрите любые из этих поставщиков услуг и вы начнёте понимать как тут всё устроено внутри. Как бы то ни было, в нашем случае мы добавим вот этот новый:

PHP
'Illuminate\Html\HtmlServiceProvider'

(4:30)
Далее, мы говорили об идее фасада, и я хочу сослаться на тот фасад. Не забудьте, если переключимся назад, мы здесь хотим использовать этот класс PHPFormFacade, а также PHPHtmlFacade. Так что если прокрутим ещё немного вниз, вы увидите что у нас есть эти псевдоимена. Это даёт нам глобальный способ доступа к этим классам в пространстве имён. Давайте сразу приступим и добавим тут один новый:

PHP
'Form' => 'Illuminate\Html\FormFacade'

(5:00)
Ещё раз отмечу, этот файл – именно то, на что мы указываем.
Далее, хоть мы и не будем его использовать в этом уроке, мы хотим создать ещё один фасад для HTML, так что давайте сделаем его. Копируем строку, подставим Html, и укажем на PHPHtmlFacade:

PHP
'Html' => 'Illuminate\Html\HtmlFacade'

Вот и всё. Мы сделали всё чтобы настроить этот пакет, а значит мы можем вернуться к нашему виду create (create.blade.php) и воспользоваться им.

(5:30)
Теперь мы можем делать штуки типа:

PHP
{!! Form::open() !!}

И давайте также закроем форму:

PHP
{!! Form::close() !!}

Окей, оставим тут всё как есть и посмотрим что получится в браузере. Если мы вернёмся туда и зайдём на articles/create... Опа! Похоже мы получили PHPModelNotFoundException, что означает мою ошибку при регистрации маршрута. Но ничего, это хороший пример для вашего обучения.
Итак, почему же мы получили это? Если вернёмся в PHPArticlesController, увидим что мы не выбираем никакие записи из БД.

(6:00)
Однако мы получаем PHPModelNotFoundException вот отсюда. Похоже мы вызываем метод PHPshow() вместо PHPcreate(). Я вам это докажу:

PHP
dd('show')

И если мы вернёмся и обновим страницу, то вы увидите что вызывается действительно именно этот метод, а не PHPcreate(). Почему же это происходит? Ответ имеет отношение к тому как мы регистрируем наши маршруты.
Заметьте что у нас тут есть наша маска {id} выше этого раздела, то есть мы здесь говорим: «Ответь на articles/что-либо, и затем загрузи метод PHPshow()».

(6:30)
Так? Но когда мы доходим до этого места, это не работает, поскольку наша маска срабатывает раньше. Так что всегда когда вы хотите использовать точный путь, просто убедитесь что эти маршруты прописаны выше масок.
Итак, мы сказали articles/create, этот конкретный URI должен направлять сюда (ArticlesController@create).

(7:00)
Далее идёт article/... и далее какой-либо идентификатор, и только он загружает метод PHPshow(). ОК, попробуем это сейчас:

laravel5.dev/articles/create

Обновите страницу, и вот, теперь всё работает. Запомните это на случай если вдруг попадёте в эту же ловушку. Как бы то ни было, если мы посмотрим на исходный код – заметьте что тот небольшой кусок кода, что мы ввели, используя пакет PHPIlluminate/Html в самом деле делает больше, чем мы написали.
По умолчанию он отправит POST-запрос, полагая что действие – это просто текущий URL (т.е. мы просто отправим форму на этот же URL).

(7:30)
Это также добавит уникальный CSRF-токен и это защитит нас и обеспечит немного безопасности, о чём мы поговорим чуть позже. И ещё теперь мы можем делать вещи вроде этого... Да, почему бы нам не создать текстовый ввод? ОК... например для имени:

PHP
{!! Form::text('name') !!}

Хорошо, теперь идём обратно, обновим и форма создана за нас.

(8:00)
Даже лучше, мы можем делать штуки типа... Давайте зададим подпись для имени (то есть элемента с именем — параметром name — равным name), а Name: будет значением этой подписи:

PHP
{!! Form::label('name''Name:') !!}

ОК, если обновим, как видите это нам немного помогает в написании HTML. Снова в браузер, обновим – вот что получилось. Но в нашем случае мы пользуемся Bootstrap, верно? Почему бы нам не применить некоторые классы Bootstrap?

(8:30)
Опять идём в наш редактор. Обернём это в div с классом form-group. Вот так:

<div class="form-group">
</div>

И теперь элементы управления нашей формы будут класса form-control, и мы можем добавить это как третий параметр:

PHP
{!! Form::text('name'null, ['class' => 'form-control']) !!}

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

(9:00)
Так например, я могу сказать PHP'foo' => 'bar', и вы увидите как это сработает. Обратно в исходник, обновим, и сейчас вы увидите в дополнение к нашем классу, мы также создали здесь другой тип специального атрибута. Вот в точности как это работает. ОК, так или иначе, если мы посмотрим на это в пользовательском интерфейсе... и вот оно, мы используем Bootstrap.
Теперь, когда мы понимаем основы, почему бы нам не построить форму для добавления новой статьи? Если мы очень быстро глянем на нашу миграцию, у нас есть заголовок title, тело body, и этого достаточно, мы пока что проигнорируем дату публикации (published_at).

(9:30)
Так, скажем вот здесь, нам нужно задать заголовок:

PHP
{!! Form::label('title''Title:') !!}

Дальше мы сделаем ещё один, но на этот раз я покажу вам небольшой фрагмент кода — сниппет. В PhpStorm или любом другом редакторе, вы можете создавать специальные сниппеты, которые реально сэкономят вам уйму времени. Как вы можете себе представить, в большинстве проектов вы будете создавать много форм, так что постарайтесь ускорить это насколько возможно.

(10:00)
Например, взгляните-ка: textfield, [Tab]... и теперь я могу достроить здесь всё, в нашем случае — body, и все необходимые значения по умолчанию будут изменены. Просто ознакомьтесь с Live Templates в PhpStorm... кстати мы освещаем их здесь на сайте Laracasts, если вы захотите узнать как создавать свои сниппеты, то это очень просто. Единственное что я здесь изменю – я не хочу текстовое поле (text), а мне нужна большая текстовая область (textarea).
ОК, давайте посмотрим как это выглядит.

(10:30)
Обратно в браузер, обновим и готово.
Нам осталось лишь создать кнопку Submit, и у меня есть и такой сниппет тоже... submitfield, [Tab]... И мы скажем — Add Article.
ОК, заметьте разницу – здесь я говорю Form, и я хочу кнопку Submit, не текстовое поле, не текстовую область, не флажок и не выбор из выпадающего меню, мы хотим кнопку Submit. И потому здесь снова я даю пару Bootstrap-классов.

(11:00)
Итак, теперь когда я заполню эту форму и нажму Add Article, что должно произойти? Будет отправлен POST-запрос к текущей странице, и в некоторых случаях вы возможно захотите этого, но в других случаях, особенно если вы знакомы с идеей REST, то может вы захотите воспользоваться другой структурой. И это как раз наш случай. Давайте перейдём обратно в routes.php и скажем: «Мы хотим ответить на POST-запрос к articles, и затем мы загрузим ArticlesController@store»:

PHP
Route::post('articles''ArticlesController@store');

(11:30)
Снова мы здесь везде следуем соглашению, и если вам любопытно и вы хотите слегка забежать вперёд нас, то просто поищите на сайте Laracasts про REST и вы сможете узнать больше. В нашем случае, если мы хотим сохранить новую статью, то отправим POST-запрос на имя коллекции, у нас это articles.
Итак, обратно в PHPArticlesController.

(12:00)
У нас новый метод — PHPstore(), и он отвечает за принятие данных из этой формы, загрузку их в БД, и далее скорее всего перенаправление нас куда-либо, возможно назад на главную страницу для просмотра всех статей. Встаёт вопрос – как нам из этого метода получить доступ к значениям, которые пользователь вводит в ту форму? Я покажу вам, и по сути, я покажу вам пару разных способов.
Для начала я покажу простейший вариант где мы используем фасад.

(12:30)
В самом верху вы увидите что мы вставляем PHPIlluminate\Http\Request; однако в случае с фасадом мы получаем немного лучший внешний вид и интерфейс к собственно классу за фасадом. Это может быть удобно, так что мы тут просто скажем: PHPuse Request;
ОК, теперь если мы хотим прочитать входные данные, то я могу сказать:

PHP
$input Request::all();

(получить все входные данные и не важно, из GET или POST).

(13:00)
Так почему бы нам не сделать это сейчас? Давайте просто вернём входные данные так чтобы вы могли увидеть их на экране:

PHP
return $input;

Однако остался ещё один шаг. Не забудьте что сейчас форма публикует на текущую страницу, но мы настроили маршрут для публикации в /articles, так что нам нужно сослаться туда с нашей формы. Вот как нам это сделать. Снова мы можем использовать именной маршрут, вы пока его не изучили, но если вы забегали вперёд, то можете использовать такой подход.

(13:30)
Можете использовать URL, или action, очень похоже на то что мы осветили в предыдущем уроке, всё это применимо и здесь. В данном случае, почему бы нам не поступить проще? Используем старый добрый URL и это будет articles.
И помните, запрос по умолчанию всё ещё будет POST. Итак, если мы теперь обновим, вот что мы хотим – POST-запрос к /articles. ОК, давайте попробуем. Введём какой-нибудь фиктивный текст, отправим его, идём по тому маршруту, возвращаем данные, и теперь мы видим, что у нас есть доступ к ним.

(14:00)
Это значит, что если вы, например, хотите доступ только к заголовку, то можете сказать:

PHP
$input Request::get('title');

Давайте на это посмотрим. Обновим, и вот у нас это значение, и конечно так же будет и для любых других определённых вами полей.
ОК, теперь когда мы знаем что можно сделать PHPRequest::all() для получения всех входных данных, как нам, в конце концов, создать новую статью? И не забудьте из нашего урока Eloquent 101 мы научились, что можно сделать штуки типа: PHPArticle::create(), или можем сделать:

PHP
$article = new Article;

(14:30)
и затем вручную установить поля так:

PHP
$article->title 'the title';

или:

PHP
$article->title $input['title'];

И, кстати, не беспокойтесь насчёт SQL-иньекций, Eloquent защищает нас от этого, так что вам не нужно вручную экранировать строки.

(15:00)
Другой способ — передать значения в конструктор, то есть если вы хотите создать статью, но не сохраняя её, то это будет хорошим способом:

PHP
$article = new Article(['title' -> ])

Но в нашем случае, очень простая задача, так что мы применим PHPcreate(), и если мы захотим, то можем передать все входные данные напрямую:

PHP
Article::create($input);

Если вы помните, ранее мы говорили об идее уязвимости массового назначения (mass assignment), где люди могут воспользоваться нашим недосмотром, если мы просто передадим тот массив, что был послан из формы, напрямую в метод наподобие этого.

(15:30)
Вот почему, ещё раз повторюсь, у нас есть это поле PHP$fillable в нашем классе PHPArticle. Оно говорит: «Вот единственные поля, которые можно массово назначать». Так что если какой-нибудь хакер попытается изменить имена этих элементов на id или user_id для манипуляции нашими данными, чего мы не ожидаем, то это не страшно, всё это будет отброшено, и, значит, этот метод абсолютно безопасен.
Хорошо, мы получили входные данные, создали статью. Почему бы нам теперь не перенаправить обратно на тот самый вид (PHParticles.index)? ОК, мы получили входные данные, создали статью и сохранили её в БД.

(16:00)
Теперь мы не хотим возвращать её, а хотим перенаправить. Куда именно? Почему бы не перейти обратно на страницу со списком статей?
Итак, я могу сказать:

PHP
return redirect('articles');

ОК, давайте посмотрим что случится. Я предвижу ошибку, но давайте взглянем на это в действии.
Мы вернёмся в браузер, мы хотим создать статью.
Введём в поля текст New Article, new body, и если я нажму кнопку Add Article – опа, сбой!

(16:30)
И это потому что, как вы помните, мы не добавили поле published_at. Так почему бы нам не смухлевать и не сказать тут:

PHP
$input['published_at'] = Carbon::now();

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

(17:00)
Таким образом когда вы получаете все статьи, мы cможем ограничиться только теми для которых атрибут published_at соответствует настоящему или прошлому.
Хорошо, давайте импортируем полный путь к этому классу. Напомню, мы ссылаемся на него как PHPCarbon\Carbon. И теперь это должно сработать. Обратно в браузер, добавим статью ещё раз, и вот она внизу, так что да, всё сработало, но должна ли она находиться внизу?

(17:30)
Новая статья в блоге должна быть наверху, так ведь? Давайте это исправим.
Вот здесь наверху, когда мы получаем все статьи, похоже что мы хотим упорядочить их в нисходящем порядке, новые сверху. Давайте заменим это на:

PHP
$articles Article::latest()->get();

Просто добавим это как есть. Получить последние статьи.
Теперь, если вам любопытно, мы заглянем в код PHPlatest() в нашем строителе классов — заметьте, что всё что он делает – это вызывает PHPorderBy($column'desc').

(18:00)
Это просто удобный метод для нас, и это означает, что если вы хотите, то можете просто вызвать:

PHP
Article::order_by('published_at','desc')->get();

(отсортировать в нисходящем порядке и затем получить результат). Так тоже было бы хорошо, но Laravel делает это ещё немного проще, поскольку как вы понимаете это очень распространённая задача. И как вы можете представить, в дополнение к PHPlatest(), также есть и PHPoldest() для обратного действия.

(18:30)
Хорошо, давайте перейдём обратно, мы установим наш столбец в published_at и мы готовы к запуску. Назад, обновить, и в этот раз новейшие статьи показываются сверху, как мы и ожидали.
Итак, мы почти закончили этот урок, но я хочу оставить вам на обдумывание один момент. У нас есть проблема. Что если мы просто добавим статью, но не введём никаких данных?

(19:00)
Хм, что же здесь происходит? Если мы посмотрим исходный код страницы, то мы увидим, что да, мы действительно добавили новую запись в БД, но у нас не было никаких проверок или ограничений чтобы защитить её, и мы оказались в такой странной ситуации. Так что в следующем видео мы поговорим о проверках ввода.

Как вы считаете, полезен ли этот материал? Да Нет

Комментарии (6)

Prowler

На 5.3 выкинет ошибку:

FatalErrorException in HtmlServiceProvider.php line 36: Call to undefined method Illuminate\Foundation\Application::bindShared()

решается переименованием метода bindShared() в singleton() в строках, сопсно 36 и 49.

Источник: https://laracasts.com/discuss/channels/laravel/call-to-undefined-method- illuminatefoundationapplicationbindshared

Denys

для 5.3

PHP
composer require "laravelcollective/html":"^5.3.0"

'providers' 
=> [
    
// ...
    
Collective\Html\HtmlServiceProvider::class,
    
// ...
  
],


'aliases' => [
    
// ...
      
'Form' => Collective\Html\FormFacade::class,
      
'Html' => Collective\Html\HtmlFacade::class,
    
// ...
  
],
Egoiste

перевод статьи конечно АГОНЬ но так в целом очень даже не плохо

Valtasaar

Кто знает есть ли у автора youtube? Пр нику на ларе не нашел.

Andrei_1

Спасибо за поправки. Очень пригодились. Возникает только вопрос вот сейчас май 2018 а поправки были замечены в мае 2016. Видно по одному месту.... автору на этот фреймворк.

Proger_XP

Автор-то американец и исправно выпускает обновления, а вот переводчиков на русский раз два и обчелся.

Написать комментарий

Разметка: ? ?

Авторизуйся, чтобы прокомментировать.