Исправление ошибки «Target class does not exist» в Laravel 8

Исправление ошибки «Target class does not exist» в Laravel 8

Восьмая версия фреймворка содержит множество изменений. Одно из них — удаление дефолтного пространства имён маршрутов.

Это не ломает обратную совместимость, так как старые проекты могут без проблем перейти на Laravel 8, без каких либо изменений, но вот новые проекты должны учитывать этот фактор.

Разработчики, которые не знают об этом и пытаются в своих новых приложениях определять маршруты по старому, сталкиваются с ошибками вроде такой:

Target class [HomeController] does not exist

Большая проблема так же ещё в том, что множество уроков по Laravel, касающихся маршрутизации, теперь неправильны. Они подразумевают, что по прежнему существует дефолтное пространство имён для маршрутов.

Jeffrey Way about Laravel 8 route namespace

Изменения

До Laravel 8 файл RouteServiceProvider.php содержал следующий код:

protected $namespace = 'App\Http\Controllers'; 
...
Route::middleware('web')
    ->namespace($this->namespace)
    ->group(base_path('routes/web.php'));

Что означает: Laravel загружает маршруты routes/web.php, используя мидлвар web и пространство имён App\Http\Controllers. Соответственно, всякий раз, когда вы объявляете маршрут с использованием строкового синтаксиса, Laravel будет искать этот контроллер в папке App\Http\Controllers:

// Laravel будет искать App\Http\Controllers\HomeController
Route::get('/home', 'HomeController@show');

В Laravel 8 переменная $namespace была удалена, а объявление Route изменено на:

Route::middleware('web')
    ->group(base_path('routes/web.php'));

Обратите внимание, что пространство имён больше не используется. Это означает, что когда вы используете строковый синтаксис, Laravel больше не ищет ваш контроллер в папке App\Http\Controllers.

// Где же HomeController? Я не могу его найти
Route::get('/home', 'HomeController@show');

Как исправить

Раз проблема в том, что фреймворк не знает где искать контроллер, то нужно ему об этом рассказать. Это можно сделать тремя способами:

  1. Добавить пространство имён обратно, что бы его можно было использовать как раньше.
  2. Использовать полное пространство имён контроллеров при использовании строкового синтаксиса в ваших маршрутах.
  3. Использовать action-синтаксис (рекомендуется).

Добавление пространства имён вручную

Всё довольно просто. Откройте файл RoutesServiceProvider.php и вы увидите следующее:

public function boot()
{
    $this->configureRateLimiting();

    $this->routes(function () {
        Route::middleware('web')
            ->group(base_path('routes/web.php'));

        Route::prefix('api')
            ->middleware('api')
            ->group(base_path('routes/api.php'));
    });
}

Вам нужно добавить в него три строки и получите всё как было в Laravel 7:

protected $namespace = 'App\Http\Controllers';  // добавьте эту строку
  
public function boot()
{
    $this->configureRateLimiting();

    $this->routes(function () {
        Route::middleware('web')
            ->namespace($this->namespace)  // добавьте эту строку
            ->group(base_path('routes/web.php'));

        Route::prefix('api')
            ->middleware('api')
            ->namespace($this->namespace)  // добавьте эту строку
            ->group(base_path('routes/api.php'));
    });
}

Мы объявили переменную $namespace с дефолтным пространством имён для наших контроллеров и использовали её для маршрутов web и api.

Использование полного пространства имён

Нужно будет изменить все ваши маршруты — добавить к именам контроллеров пространства их имён. Например для HomeController в папке app/Http/Controllers это будет так:

// Говорим Laravel, где искать наш контроллер
Route::get('/home', 'App\Http\Controllers\HomeController@show');

Использование action-синтаксиса

Рекомендую использовать именно этот вариант, так как он защищен от опечаток и обеспечивает поддержку IDE, ведь мы явно указываем используемый класс. Вместо использования обычного строкового синтаксиса мы задаем массивом конкретный класс и метод.

// Строковый синтаксис
Route::get('/home', 'HomeController@show');

// Action-синтаксис (не забудьте импортировать класс контроллера)
use App\Http\Controllers\HomeController;
Route::get('/home', [ HomeController::class, 'show' ]);

Обратите внимание, что мы не передаем HomeController в кавычках, а задаем класс HomeController::class, который вернёт App\Http\Controllers\HomeController. Второе значение в массиве — это метод, который нужно вызвать в контроллере, соответственно в итоге это означает: «В HomeController вызвать метод show».

Теперь все ваши маршруты снова должны работать!

Автор: Juan Litvin
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.