ホームページ >バックエンド開発 >PHPチュートリアル >Laravel5.5コントローラーパラメータの受け渡し順序の問題と解決策
laravel 5.5のコントローラーは、メソッドパラメータの型に基づいて自動的に注入する機能を提供します。ただし、メソッド パラメーターの注入が完全にパラメーター名に基づいていないという事実に反映され、受信パラメーターの順序が変更されると、型の不一致エラーが発生するため、少し不便になる場合があります。この記事では、その注入原理の詳細な分析から問題を解決します。
1. コントローラーメソッドのパラメーター挿入ステップの設計
1. /routes/web.php
Route::get('/diary/show/{diary}/{page?}','Diary\DiaryController@list');にルートを追加します。2. コントローラーファイル DiaryController.php を作成し、パス /app/Http/Controllers/Diary/ に配置します。
<?php namespace App\Http\Controllers\Diary; use App\Http\Controllers\Controller; class DiaryController extends Controller { public function show(\App\Diary $diary,$page=11){ var_dump($diary->title,$page); } }3. AppDiary モデルを構築し、データベースにインストールします (省略)
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Diary extends Model { protected $table='diary'; public $timestamps = false; }4. コントローラー メソッドにアクセスします ブラウザを開いて、「http://127.0.0.1//diary/show/4」と入力します。 /12"
このとき、データテーブル日記のid=4と12のtitleフィールドの値が出力されます2. インジェクションパラメータの型説明注: laravelは、{diary}変数と{page}変数に一致しますリクエストのルーティングとコントローラー メソッド内で、必要なメソッド パラメーター タイプが生成され、コントローラー メソッドに挿入されます。 さまざまなパラメーター タイプには 3 つの状況があります。 1. パラメーター タイプが UrlRoutable インターフェイスを実装している場合 (つまり、継承されている場合)。 IlluminateDatabaseEloquentModel から)、オブジェクトに対応するテーブルで、ID 値がルート内の一致するパラメーター値であるレコードを検索し、パラメーター タイプがカスタム タイプの場合、モデル オブジェクトを構築します。 UrlRoutable インターフェイスは実装されていません)、laravel はオブジェクトを構築してそれを注入します ;3. パラメーターの型が基本データ型で、名前がルーティング パラメーターで定義されている名前である場合、値はルーティングから取得されます。パラメータ; 4. パラメータ タイプが基本データ タイプであるが、その名前がルーティング パラメータ定義にない場合、デフォルト値がある場合はデフォルト値を使用します。それ以外の場合は、システムがエラーを表示します。 3. 注入されたパラメータに関する現在の問題の分析 Java の Spring MVC フレームワークを参照すると、laravel のパラメータ型の注入にはまだ欠陥があり、主にパラメータ名に従って完全に注入されないという事実に反映されています。 1. コントローラーのパラメーターの順序を変更すると、パラメーターの型の転送エラーが発生します。たとえば、DiaryController によって制御される show メソッドのパラメーターの順序を変更すると、エラーが発生します:
<?php namespace App\Http\Controllers\Diary; use App\Http\Controllers\Controller; class DiaryController extends Controller { public function show($page,\App\Diary $diary){ var_dump($diary->title,$page); } }2.パラメータの型は基本的なデータ型(2(3)参照)なので、名前の通りパラメータを注入するものではないので、以下のようにコードを変更すると、Laravel5.5コントローラーでも正常に動作します。メソッドパラメータインジェクションのソースコード分析1. UrlRoutableインターフェイスを実装しました。パラメータの型は、ルーティングミドルウェアIlluminateRoutingMiddlewareSubstituteBinding実装によって構築されます
<?php namespace App\Http\Controllers\Diary; use App\Http\Controllers\Controller; class DiaryController extends Controller { public function show(\App\Diary $diary,$pag){ var_dump($diary->title,$pag); } }IlluminateRoutingRouterのsubstituteImplicitBindingsメソッド
public function handle($request, Closure $next) { $this->router->substituteBindings($route = $request->route()); $this->router->substituteImplicitBindings($route); return $next($request); }は、IlluminateRoutingImplicitのresolveForRouteメソッドに実装されています。 RouteBinding
public function substituteImplicitBindings($route) { ImplicitRouteBinding::resolveForRoute($this->container, $route); }追加メモ:ここでは $route オブジェクト (Illu) を呼び出します minateRoutingRoute 型) モデルを記述する setParameter メソッド パラメーター型 (2(1) 参照) は、パラメーター型とパラメーター名を同時に照合することでモデル インスタンスに注入されます2。他のタイプのコントローラー パラメーターは、ルーティング コントローラーの実行時にバインドされます 重要な部分は、IlluminateRoutingControllerDispatcher のディスパッチ メソッド内にあります。 実装場所:
public static function resolveForRoute($container, $route) { //从路由参数中获取参数值,$parameters为 ['diary':'4','page':'12'] $parameters = $route->parameters(); //获取控制器的函数参数列表,此处传入UrlRoutable::class,只返回实现UrlRoutable接口的参数 $signatureParameters = $route->signatureParameters(UrlRoutable::class); foreach ($signatureParameters as $parameter) { if (! $parameterName = static::getParameterName($parameter->name, $parameters)) { continue; } $parameterValue = $parameters[$parameterName]; if ($parameterValue instanceof UrlRoutable) { continue; } //构建模型的实例(基础自Illuminate\Database\Eloquent\Model),此处为App\Diary $instance = $container->make($parameter->getClass()->name); //将参数值绑定到模型,参加Illuminate\Database\Eloquent\Model的resolveRouteBinding方法 if (! $model = $instance->resolveRouteBinding($parameterValue)) { throw (new ModelNotFoundException)->setModel(get_class($instance)); } //根据参数名称注入模型实例 $route->setParameter($parameterName, $model); } }replaceClassMethodDependency メソッドを呼び出す
public function dispatch(Route $route, $controller, $method) { //解析控制器方法的参数 $parameters = $this->resolveClassMethodDependencies( $route->parametersWithoutNulls(), $controller, $method ); if (method_exists($controller, 'callAction')) { //通过Illuminate\Routing\Controller的callAction调用控制器方法 return $controller->callAction($method, $parameters); } //直接调用控制器方法 return $controller->{$method}(...array_values($parameters)); }問題の概要: 1. 2 (1) を参照)、ルーティング パラメーター (2 (3) を参照) で名前が定義されている基本型は、ルーティングに従っている必要があります。 で定義された順序が最初にコントローラー メソッドに渡されます。そうでない場合は、型不一致エラーが発生します。 2. ルーティング パラメーターで名前が定義されていないカスタム型 (2 (2) を参照) および基本型パラメーター (2 (2) 4) を参照) は、コントローラー メソッドで出現順に渡します。 5. 問題の修正 /vendor/laravel/framework/src/Illuminate/Routing/RouteDependencyResolverTrait.php ファイルを開き、resolveMethodDependency メソッドを次のコードに変更します
public function resolveMethodDependencies(array $parameters, ReflectionFunctionAbstract $reflector) { $instanceCount = 0; $values = array_values($parameters); //通过方法反射获取方法参数 foreach ($reflector->getParameters() as $key => $parameter) { //如果有默认值则返回默认值,如果是自定义方法则构建实例返回 $instance = $this->transformDependency( $parameter, $parameters ); if (! is_null($instance)) { $instanceCount++; //自定义类型(未实现UrlRoutable接口)的实例注入 $this->spliceIntoParameters($parameters, $key, $instance); } elseif (! isset($values[$key - $instanceCount]) && $parameter->isDefaultValueAvailable()) { //未在路由参数中定义,但有默认值的参数注入 $this->spliceIntoParameters($parameters, $key, $parameter->getDefaultValue()); } } return $parameters; }変更後、コントローラー メソッドを完全に挿入しますパラメータの名前と型を変更すると、コードがより簡潔になり、関数がより強力になります。 パラメータがルートで定義されておらず、デフォルト値が指定されていない場合は、null として挿入されます。
関連する推奨事項:
PHP がリフレクションメカニズムに基づいて自動依存関係注入を実装する方法の詳細な説明
Laravel 5.5の対応するインターフェースを使用するにはどうすればよいですか?
以上がLaravel5.5コントローラーパラメータの受け渡し順序の問題と解決策の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。