- Объявление групп внутри групп
- Объявление метода resolveRouteBinding в вашей модели
- assign withTrashed() to Route::resource() method
- Пропустить нормализацию ввода
- Субдомены с подстановочными знаками
- Что скрывается за маршрутами?
- Привязка модели маршрута: вы можете определить ключ
- Возврат маршрута: если нет другого маршрута
- Проверка параметров маршрута с помощью RegExp
- Ограничение количества запросов
- Параметры строки запроса для маршрутов
- Отдельные маршруты по файлам
- Перевести глаголы ресурса
- Пользовательские имена маршрутов ресурсовs
- Нетерпеливые отношения нагрузки
- Именование контроллеров ресурсов
- Легко выделяйте меню панели навигации
- Сгенерировать абсолютный путь с помощью помощника route()
- Переопределите преобразователь привязки маршрута для каждой из ваших моделей
- Если вам нужен общедоступный URL, но вы хотите, чтобы они были защищены
- Использование Gate в методе промежуточного программного обеспечения
- Простой маршрут со стрелочной функцией
- Отдача представления напрямую в роуте
- Каталог маршрута вместо файла маршрута
- Группировка ресурсов маршрута
- Пользовательские привязки маршрутов
- Два способа проверить имя маршрута
- Привязка модели маршрута к мягко-удаленным моделям
- Получить URL без параметров запроса
- Настройка поведения отсутствующей модели в привязках модели маршрута
- Исключить посредники из роута
- Группы контроллеров
Порой необходимо установить свои правила для роутов, которые находятся внутри группы. Laravel позволяет это реализовать без проблем.
Route::group(['prefix' => 'account', 'as' => 'account.'], function() {
Route::get('login', [AccountController::class, 'login']);
Route::get('register', [AccountController::class, 'register']);
Route::group(['middleware' => 'auth'], function() {
Route::get('edit', [AccountController::class, 'edit']);
});
});
Привязка модели маршрута в Laravel — это здорово, но бывают случаи, когда мы не можем просто позволить пользователям легко получать доступ к ресурсам по идентификатору. Возможно, нам потребуется подтвердить их право собственности на ресурс.
Вы можете объявить метод resolveRouteBinding в своей модели и добавить туда свою пользовательскую логику.
public function resolveRouteBinding($value, $field = null)
{
$user = request()->user();
return $this->where([
['user_id' => $user->id],
['id' => $value]
])->firstOrFail();
}
Совет от @notdylanv
До Laravel 9.35 - только для Route::get()
Route::get('/users/{user}', function (User $user) {
return $user->email;
})->withTrashed();
С версии Laravel 9.35 - доступно и для Route::resource()
Route::resource('users', UserController::class)
->withTrashed();
Или для конкретного метода
Route::resource('users', UserController::class)
->withTrashed(['show']);
Laravel автоматически обрезает все входящие строковые поля в запросе. Это называется нормализация ввода.
Иногда вам может не хотеться такого поведения.
Вы можете использовать метод skipWhen в посреднике TrimStrings и вернуть true, чтобы пропустить его.
public function boot()
{
TrimStrings::skipWhen(function ($request) {
return $request->is('admin/*);
});
}
Совет от @Laratips1
Вы можете создать группу маршрутов по имени динамического поддомена и передать его значение каждому маршруту.
Route::domain('{username}.workspace.com')->group(function () {
Route::get('user/{id}', function ($username, $id) {
//
});
});
Если вы используете Laravel UI package, то вы вероятно желаете узнать какие маршруты скрываются за Auth::routes()
?
Откройте файл /vendor/laravel/ui/src/AuthRouteMethods.php
.
public function auth()
{
return function ($options = []) {
// Authentication Routes...
$this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
$this->post('login', 'Auth\LoginController@login');
$this->post('logout', 'Auth\LoginController@logout')->name('logout');
// Registration Routes...
if ($options['register'] ?? true) {
$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
$this->post('register', 'Auth\RegisterController@register');
}
// Password Reset Routes...
if ($options['reset'] ?? true) {
$this->resetPassword();
}
// Password Confirmation Routes...
if ($options['confirm'] ?? class_exists($this->prependGroupNamespace('Auth\ConfirmPasswordController'))) {
$this->confirmPassword();
}
// Email Verification Routes...
if ($options['verify'] ?? false) {
$this->emailVerification();
}
};
}
Использование этой функции по умолчанию просто так:
Auth::routes(); // без параметров
Но вы можете указать параметры для включения или отключения определенных маршрутов:
Auth::routes([
'login' => true,
'logout' => true,
'register' => true,
'reset' => true, // для сброса паролей
'confirm' => false, // для дополнительных подтверждений пароля
'verify' => false, // для проверки электронной почты
]);
Совет взят из предложения от MimisK13
Вы можете выполнить привязку модели в маршруте, например, Route::get('api/users/{user}', function (User $user) { … }
- но не только по полю ID. Если вы хотите использовать {user}
из названия username
поля, поместите это в модель:
public function getRouteKeyName() {
return 'username';
}
Если вы хотите указать дополнительную логику для ненайденных маршрутов, вместо того, чтобы просто создавать страницу 404 по умолчанию, вы можете создать для этого специальный маршрут в самом конце вашего файла маршрутов.
Route::group(['middleware' => ['auth'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
Route::get('/home', [HomeController::class, 'index']);
Route::resource('tasks', [Admin\TasksController::class]);
});
Route::fallback(function() {
// код выполнится, если ни один из указанных маршрутов не был найден
});
Мы можем проверять параметры непосредственно в маршруте с параметром “where”.
Довольно типичным случаем является префикс ваших маршрутов по языковому стандарту, такие какfr/blog
или en/article/333
.
Как мы можем гарантировать, что эти две первые буквы не используются для чего-то другого, кроме языка?
routes/web.php
:
Route::group([
'prefix' => '{locale}',
'where' => ['locale' => '[a-zA-Z]{2}']
], function () {
Route::get('/', [HomeController::class, 'index']);
Route::get('article/{id}', [ArticleController::class, 'show']);
});
Вы можете ограничить некоторый URL-адрес, который будет вызываться не более 60 раз в минуту, используйте throttle:60,1
:
Route::middleware('auth:api', 'throttle:60,1')->group(function () {
Route::get('/user', function () {
//
});
});
Но также вы можете сделать это отдельно для публичных и для авторизованных пользователей:
// максимум 10 запросов для гостей, 60 для аутентифицированных пользователей
Route::middleware('throttle:10|60,1')->group(function () {
//
});
Кроме того, у вас может быть поле БД users.rate_limit, тем самым, можно ввести лимит для конкретного пользователя:
Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () {
Route::get('/user', function () {
//
});
});
Если вы передадите дополнительные параметры маршруту в массиве, эти пары ключ / значение будут автоматически добавлены в строку запроса сгенерированного URL-адреса.
Route::get('user/{id}/profile', function ($id) {
//
})->name('profile');
$url = route('profile', ['id' => 1, 'photos' => 'yes']); // Результат: /user/1/profile?photos=yes
Если у вас есть набор маршрутов, относящихся к определенному «разделу»,
вы можете выделить их в специальный файл routesXXXXX.php
и просто включить его в routes/web.php
Для примера routes/auth.php
в Laravel Breeze от самого Тейлора Отвела:
Route::get('/', function () {
return view('welcome');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth'])->name('dashboard');
require __DIR__.'/auth.php';
Где в routes/auth.php
:
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\RegisteredUserController;
// ... больше контроллер
use Illuminate\Support\Facades\Route;
Route::get('/register', [RegisteredUserController::class, 'create'])
->middleware('guest')
->name('register');
Route::post('/register', [RegisteredUserController::class, 'store'])
->middleware('guest');
// ... И еще маршруты
Но вы должны использовать этот include()
только тогда, когда этот отдельный файл маршрута имеет
те же настройки для префиксов/посредников, иначе необходимо сгруппировать их в app/Providers/RouteServiceProvider
:
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
// ... Ваш файл маршрутов указан где-то тут
});
}
Если вы используете контроллеры ресурса, но хотите изменить глаголы URL на неанглийские для целей SEO,
и желаете вместо create
использовать испанский crear
, то
можете настроить его с помощью метода Route::resourceVerbs()
в App\Providers\RouteServiceProvider
:
public function boot()
{
Route::resourceVerbs([
'create' => 'crear',
'edit' => 'editar',
]);
}
При использовании контроллеров ресурсов в routes/web.php
вы можете указать параметр ->names()
,
поэтому префикс URL-адреса в браузере и префикс имени маршрута, который вы используете во всем проекте Laravel, могут отличаться.
Route::resource('p', ProductController::class)->names('products');
Таким образом, приведенный выше код будет генерировать такие URL-адреса, как p
, p{id}
, p{id}edit
и т.д.
Но вы должны вызывать их в коде с помощью route('products.index')
, маршрут('products.create')` и т. д.
Если вы используете привязку модели маршрута и считаете, что не можете использовать нетерпеливую загрузку для отношений, подумайте еще раз.
Итак, вы используете привязку модели маршрута
public function show(Product $product) {
//
}
Но у вас есть отношения belongsTo и вы не можете использовать product->with('category')
с нетерпеливой загрузкой?
Вы действительно можете! Загрузите отношение с помощью ->load()
public function show(Product $product) {
$product->load('category');
//
}
В контроллерах ресурсов в routes/web.php
можно указать параметр ->names()
, поэтому префикс URL и префикс имени маршрута могут отличаться. Это создаст такие URL-адреса, как p
, p{id}
, p{id}edit
и т. д. Но вы бы назвали их:
- route('products.index)
- route('products.create)
- и т.д.
Route::resource('p', \App\Http\Controllers\ProductController::class)->names('products');
Используйте Route::is('route-name')
, чтобы легко выделить меню панели навигации.
<ul>
<li @if(Route::is('home')) class="active" @endif>
<a href="/">Home</a>
</li>
<li @if(Route::is('contact-us')) class="active" @endif>
<a href="/contact-us">Contact us</a>
</li>
</ul>
Совет от @anwar_nairi
route('page.show', $page->id);
// http://laravel.test/pages/1
route('page.show', $page->id, false);
// /pages/1
Совет от @oliverds_
Вы можете переопределить преобразователь привязки маршрута для каждой из ваших моделей.
В этом примере у меня нет контроля над знаком @ в URL-адресе, поэтому с помощью метода resolveRouteBinding
я могу удалить знак @ и разрешить модель.
// Route
Route::get('{product:slug}', Controller::class);
// Request
https://nodejs.pub/@unlock/hello-world
// Product Model
public function resolveRouteBinding($value, $field = null)
{
$value = str_replace('@', '', $value);
return parent::resolveRouteBinding($value, $field);
}
Совет от @Philo01
Если вам нужен общедоступный URL-адрес, но вы хотите, чтобы он был защищен, используйте подписанный URL-адрес Laravel.
class AccountController extends Controller
{
public function destroy(Request $request)
{
$confirmDeleteUrl = URL::signedRoute('confirm-destroy', [
$user => $request->user()
]);
// Отправить ссылку по электронной почте...
}
public function confirmDestroy(Request $request, User $user)
{
if (! $request->hasValidSignature()) {
abort(403);
}
// Пользователь подтверждается, нажав на письмо
$user->delete();
return redirect()->route('home');
}
}
Совет от @anwar_nairi
Вы можете использовать гейты, указанные в App\Providers\AuthServiceProvider
в методе промежуточного программного обеспечения.
Для этого нужно просто поставить внутрь can:
и имена нужных гейтов.
Route::put('/post/{post}', function (Post $post) {
// Текущий пользователь может обновить сообщение...
})->middleware('can:update,post');
Вы можете использовать функцию стрелки php в маршрутизации без использования анонимной функции.
Для этого вы можете использовать fn() =>
, это выглядит проще.
// Вместо
Route::get('/example', function () {
return User::all();
});
// Вы можете
Route::get('/example', fn () => User::all());
Вы можете использовать Route::view(uri, bladePage)
, чтобы вернуть представление напрямую, без использования функции контроллера.
//this will return home.blade.php view
Route::view('/home', 'home');
Вы можете создать каталог /routes/web/ и заполнить /routes/web.php только:
foreach(glob(dirname(__FILE__).'/web/*', GLOB_NOSORT) as $route_file){
include $route_file;
}
Теперь каждый файл внутри /routes/web/ действует как файл веб-маршрутизатора, и вы можете организовать свои маршруты в разные файлы.
Если в ваших маршрутах много контроллеров ресурсов, вы можете сгруппировать их и вызвать один Route::resources() вместо множества одиночных операторов Route::resource().
Route::resources([
'photos' => PhotoController::class,
'posts' => PostController::class,
]);
Знаете ли вы, что в Laravel можно определить собственные привязки маршрутов?
В этом примере мне нужно реализовать портфолио с помощью слаг. Но слаг не уникален, потому что несколько пользователей могут иметь портфолио с именем «Foo».
Поэтому я определяю, как Laravel должен разрешать их, из параметра маршрута:
class RouteServiceProvider extends ServiceProvider
{
public const HOME = '/dashboard';
public function boot()
{
Route::bind('portfolio', function (string $slug) {
return Portfolio::query()
->whereBelongsto(request()->user())
->whereSlug($slug)
->firstOrFail();
});
}
}
Route::get('portfolios/{portfolio}', function (Portfolio $portfolio) {
/*
* Портфолио будет результатом запроса, определенного RouteServiceProvider
*/
})
Совет от @mmartin_joo
Вот два способа проверить имя маршрута в Laravel.
// #1
<a
href="{{ route('home') }}"
@class="['navbar-link', 'active' => Route::current()->getName() === 'home']"
>
Home
</a>
// #2
<a
href="{{ route('home') }}"
@class="['navbar-link', 'active' => request()->routeIs('home)]"
>
Home
</a>
Совет от @AndrewSavetchuk
By default, when using route model binding will not retrieve models that have been soft-deleted.
You can change that behavior by using withTrashed
in your route.
Route::get('/posts/{post}', function (Post $post) {
return $post;
})->withTrashed();
Совет от @cosmeescobedo
Если по какой-то причине ваш URL-адрес имеет параметры запроса, вы можете получить URL-адрес без параметров запроса, используя метод запроса fullUrlWithoutQuery
следующим образом:
// Исходный URL: https://www.amitmerchant.com?search=laravel&lang=en&sort=desc
$urlWithQueryString = $request->fullUrlWithoutQuery([
'lang',
'sort'
]);
echo $urlWithQueryString;
// Результат: https://www.amitmerchant.com?search=laravel
Совет от @amit_merchant
По умолчанию Laravel выдает ошибку 404, когда не может связать модель, но вы можете изменить это поведение, передав замыкание отсутствующему методу.
Route::get('/users/{user}', [UsersController::class, 'show'])
->missing(function ($parameters) {
return Redirect::route('users.index');
});
Совет от @cosmeescobedo
Вы можете исключить посредника на уровне маршрута в Laravel, используя метод без промежуточного ПО.
Route::post('/some/route', SomeController::class)
->withoutMiddleware([VerifyCsrfToken::class]);
Совет от @alexjgarrett
Вместо использования контроллера в каждом маршруте рассмотрите возможность использования группы контроллеров маршрута. Добавлено в Laravel начиная с версии 8.80.
// Было
Route::get('users', [UserController::class, 'index']);
Route::post('users', [UserController::class, 'store']);
Route::get('users/{user}', [UserController::class, 'show']);
Route::get('users/{user}/ban', [UserController::class, 'ban']);
// Стало
Route::controller(UsersController::class)->group(function () {
Route::get('users', 'index');
Route::post('users', 'store');
Route::get('users/{user}', 'show');
Route::get('users/{user}/ban', 'ban');
});
Совет от @justsanjit