Фактически, любой CRUD состоит из 7 маршрутов, контроллера и шаблонов. Причем большая часть этого кода идентична, особенно маршруты. Они не содержат логики и всегда строятся по одному и тому же принципу.
Laravel частично заимствовал из Rails еще один механизм, который называется "ресурсная маршрутизация". Он упрощает создание типичных CRUD, за счет полной унификации всех маршрутов и способов их обработки. Вместо описания 7 разных маршрутов, ресурсная маршрутизация позволяет указать один метамаршрут:
<?php
Route::resource('articles', ArticleController::class);
Внутри себя он превращается в те самые семь маршрутов, которые мы реализовывали в предыдущих уроках. Их можно увидеть с помощью команды artisan:
php artisan route:list
+-----------+-------------------------+------------------+---------+
| Method | URI | Name | Action |
+-----------+-------------------------+------------------+---------+
| GET|HEAD | / | | Closure |
| GET|HEAD | articles | articles.index | index |
| POST | articles | articles.store | store |
| GET|HEAD | articles/create | articles.create | create |
| GET|HEAD | articles/{article} | articles.show | show |
| PUT|PATCH | articles/{article} | articles.update | update |
| DELETE | articles/{article} | articles.destroy | destroy |
| GET|HEAD | articles/{article}/edit | articles.edit | edit |
+-----------+-------------------------+------------------+---------+
# Обратите внимание на имя плейсхолдера. Ниже станет понятно почему здесь article, а не id
Довольно неплохо. В проектах где подобных CRUD много (любой типичный веб-проект), ресурсный маршрутизатор очень помогает. Он не просто сокращает количество кода, но и дает хорошую унификацию. Нужно меньше думать и меньше спорить. Все уже спроектировано.
Следующий шаг – упрощение контроллера. Во-первых, можно сразу сгенерировать контроллер, со всеми нужными обработчиками. Во-вторых, этот контроллер можно интегрировать с нужной моделью:
php artisan make:controller ArticleController --resource --model Article
На выходе получим такой контроллер:
<?php
namespace App\Http\Controllers;
use App\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\Article $article
* @return \Illuminate\Http\Response
*/
public function show(Article $article)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Article $article
* @return \Illuminate\Http\Response
*/
public function edit(Article $article)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Article $article
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Article $article)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Article $article
* @return \Illuminate\Http\Response
*/
public function destroy(Article $article)
{
//
}
}
Обратите внимание на параметры обработчиков. Laravel самостоятельно находит нужную сущность и достает ее из базы данных. Это позволяет хоть немного, но сократить код.
Ресурсы могут быть вложенными. Это дает возможность строить пути, отражающие зависимости между сущностями на сайте:
# Примеры с Хекслета
# Урок /courses/{course}/lessons/{lesson}
/courses/js-testing/lessons/asserts
# Список пройденных курсов /u/{user}/courses
/u/mokevnin/courses
Принцип построения адресов точно такой же, как и для обычного ресурса, но с включением указания на родительский ресурс:
# Список
/entities/{entity}/subentities
# Сущность
/entities/{entity}/subentities/{subentity}
# Все остальные маршруты строятся по такому же принципу.
# Впереди добавляется /entities/{entity}.
Вложенный ресурс можно генерировать автоматически:
php artisan make:controller ArticleCommentController --resource --model ArticleComment --parent Article
Например, вот так выглядит ресурс комментарии к статьям:
<?php
Route::resource('articles.comments', ArticleCommentController::class);
Для вложенного ресурса, в экшены, кроме самой сущности передается и родительская сущность:
<?php
# /articles/{article}/comments/{comment}
# Обе сущности можно получить через параметры
public function edit(Article $article, ArticleComment $comment)
{
return view('article_comment.edit', compact('article', 'comment'));
}
Если ресурс называется articles.comments
, то параметр следует назвать $comment
, а не $articleComment
. Другими словами, имя параметра выбирается в единственном числе по имени ресурса.
Немного по-другому начинает работать хелпер route
. Для построения ссылок, там где участвуют оба ресурса, нужно использовать массив для их передачи:
<?php
route('articles.comments.edit', [$article, $comment]);
Заключение
Ресурсная маршрутизация – удобный механизм, позволяющий немного упростить создание CRUD. Он берет на себя много работы и дает программисту возможность сосредоточиться на логике.
Существует негласное правило, насколько вложенной может быть вложенная маршрутизация. Считается, что не стоит делать более одного вложения. Иначе ссылки получаются очень длинными, а код начинает усложняться, так как приходится оперировать сразу тремя сущностями и более.
Самостоятельная работа
- Удалите все маршруты связанные со статьями
- Удалите контроллер статей (шаблоны оставьте)
- Добавьте ресурсную маршрутизацию
articles
- Сгенерируйте для него контроллер
ArticleController
- Реализуйте CRUD
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.