Фактически, любой 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
use App\Http\Controllers\ArticleCommentController;
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]);