Laravelでページにアクセスしようとしたときに、指定のコントローラが存在しているにも関わらずTarget class [○○Controller] does not exist と表示されたときの原因と対処法について。
エラーの発生状況
以下2パターンの記述でエラーが発生
web.phpの記述
パターン1: Laravel7以前の記述
Route::get('/user/', 'UserController@index' );
パターン2: Laravel8の記述
Route::get('/user/', [ UserController::class, 'index'] );
上記の2つのパターンともに明確にこのエラーが発生した理由があります。
コントローラーの記述
今回のエラーとコントローラの記述は関係ないのですが参考として。
app/Http/Controllers/UserController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function userShow( Request $request ){
return "This is UserController UserShow Action Result";
}
}
2つのパターン毎にエラーの理由が異なるのでそれぞれについて解説します。
パターン1: Laravel7以前の記述のエラーの原因と対処法
Laravel7以前の記述のエラーの原因
Route::get('/user/', 'UserController@index' );
上記の記述方法はLaravel8にしたときにエラーが発生します。
理由としては、Laravel7までは、ルーティングを記述する routes/web.phpファイルにデフォルトの状態でコントローラの名前空間(App\\Http\\Controllers)が適用されていたのですが、Laravel8でこのデフォルトの設定が外れたためです。
以下のように、Laravel7で ‘UserController@index’ のように記述すれば、 Laravel側でUserControllerクラスを App\Http\Controllers\UserController に置き換えていました。
これが、Laravel8では UserController と認識するようになったためです。
バージョン | web.phpのデフォルトの名前空間 | 記述例 | 実際の名前空間 |
---|---|---|---|
Laravel7 | App\\Http\\Controllers | UserController | App\Http\Controllers\UserController |
Laravel8 | なし(グローバル) | UserController | UserController |
Laravel7以前の記述のエラーの対処法
この場合のエラーの対処法は2つあります。
- web.phpファイルの上部で指定のクラスをUse宣言する。
- Laravel7と同じように、デフォルトでコントローラの名前空間が適用されるようにする。
web.phpファイルの上部で指定のクラスをUse宣言する
シンプルに使おうとしているクラスの完全な名前をuse宣言すれば、これまで通りに使うことができます。
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Http;
//追記
use App\Http\Controllers\UserController;
Route::get('/user/', 'UserController@index' );
もしくは、Routeの中で完全なクラス名を記述することでも対応できます。
Route::get('/user/', 'App\Http\Controllers\UserController@index' );
デフォルトでコントローラの名前空間が適用されるようにする
Laravel7と同じように、デフォルトでコントローラの名前空間が適用されるようにすることも可能です。
app/Providers/RouteServiceProvider.php というファイルの中にこの処理が記載してあるのですが、Laravel8ではデフォルトでコメントアウトしてあるので、これを外します。
/**
* The controller namespace for the application.
*
* When present, controller route declarations will automatically be prefixed with this namespace.
*
* @var string|null
*/
protected $namespace = 'App\\Http\\Controllers';
これで、web.phpが名前空間App\Http\Controllersの中に置かれた状態になります。
仕組みとしては、その下に bootという関数が定義されていて、その中で、指定した名前空間を web.phpに適用する処理が記述されています。
public function boot()
{
//省略
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
});
}
以下は補足です。
middleware(ミドルウェア)とは、要求されたリクエストに対してページを表示するなどの処理を返す前に、そのリクエストに対して処理を行うためのメソッドです。
ここでは、middleware(‘web’)として、予めwebで定義されているクラスをミドルウェアとしてまとめて適用しています。
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
]
];
セッションを開始したり、CSRFのトークンを有効化したりといった処理をしています。
(参考)Laravel公式 ミドルウェア
次に、namespaceメソッドで名前空間を適用しています。ここで先ほどコメントアウトを外した、変数の値が入ります。
最後にこのミドルウェアの処理と名前空間を routes/web.php に適用しています。
base_pathヘルパは完全なルートパスを返します。例えば、base_path(‘routes/web.php’) であれば、以下のような文字列が返ります。
"/Users/projects/laravel/app/routes/web.php"
パターン2: Laravel8の記述のエラーの原因と対処法
Laravel8の記述のエラーの原因
Route::get('/user/', [ UserController::class, 'index'] );
これはLaravel8の公式ドキュメントに記載されているルーティングの記述を使用したものです。
クラス名の後ろに「::class」がついています。これは、指定したクラスの完全なクラス名を文字列として返すものです。
(参考)【PHP】::classとは何か?名前解決や完全修飾名とは何かの意味と使い方も解説
名前空間の指定がない場合、クラス名は UserController となります。ですが、処理を記載しているのは、App\Http\Controllers\UserController なので、クラスが見つからず Target class [UserController] does not exist というエラが出てしまいます。
また、この配列して指定する記述方法は、app/Providers/RouteServiceProvider.php で適用した名前空間が効かないため、名前空間を宣言するときは、 web.phpのファイルで別途use宣言をする必要があります。
Laravel8の記述のエラーの対処法
エラーの対処法は web.phpの中で対象のコントローラを完全なクラス名でuse宣言することです。
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Http;
//追記
use App\Http\Controllers\UserController;
Route::get('/user/', [ UserController::class, 'index'] );
もしくは、Routeの中で完全なクラス名を記述することでも対応できます。
「::class」は完全なクラス名を文字列として返す処理なので、以下の2つは同じになります。
Route::get('/user/', [ App\Http\Controllers\UserController::class, 'index'] );
Route::get('/user/', [ 'App\Http\Controllers\UserController', 'index'] );
Target class [○○Controller] does not exist というエラーが発生したら、焦らず、指定しているコントローラの名前空間を記述するればOKです。