The controller of laravel5.5 provides the ability to automatically inject based on the method parameter type. But sometimes there is a slight inconvenience, which is reflected in the fact that the injection of method parameters is not entirely based on the parameter name. If the order of the incoming parameters is changed, it will cause a type mismatch error. This article will solve the problem from an in-depth analysis of its injection principle.
1. Controller method parameter injection step design
1. Add routing in /routes/web.php
Route::get('/diary/show/{diary}/{page?}','Diary\DiaryController@list');
2. Write the controller file DiaryController.php and put it in Go to the path of /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. Build the model\App\Diary and install it into the database (omitted)
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Diary extends Model { protected $table='diary'; public $timestamps = false; }
4. Access the controller method
Open the browser and enter: "http://127.0.0.1//diary/show/4/12"
At this time, the title field value and 12## of id=4 in the data table diary are output.
#2. Injection parameter type descriptionDescription: Laravel will generate and inject instance objects based on the matching {diary} and {page} variables in the request route and the method parameter types required in the controller method. In the controller method, There are three situations for different parameter types: 1. If the parameter type implements the UrlRoutable interface (that is, inherited from Illuminate\Database\Eloquent\Model), Then search the table corresponding to the model object for the record whose id value is the matching parameter value in the route, and build the model object; 2. If the parameter type is a custom type (the UrlRoutable interface is not implemented), then Laravel will construct an object and then inject it; 3. If the parameter type is a basic data type and the name is the name defined in the routing parameter, the value will be obtained from the routing parameter; 4. If the parameter type is a basic data type but the name is not defined in the routing parameter, use the default value if there is one, otherwise the system will prompt an error. 3. Analysis of current problems with injected parameters Referring to Java's Spring MVC framework, laravel's parameter type injection still has flaws, mainly reflected in the fact that it is not injected entirely according to the parameter name. 1. If you change the order of controller parameters, a parameter type transfer error will occur. If you change the order of the parameters of the show method controlled by DiaryController, an error will occur:<?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); } }
<?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); } }
public function handle($request, Closure $next) { $this->router->substituteBindings($route = $request->route()); $this->router->substituteImplicitBindings($route); return $next($request); }
public function substituteImplicitBindings($route) { ImplicitRouteBinding::resolveForRoute($this->container, $route); }
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); } }
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)); }
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; }
public function resolveMethodDependencies(array $parameters,ReflectionFunctionAbstract $reflector){ $methodParameters=[]; foreach($reflector->getParameters() as $key=>$parameter){ $name=$parameter->getName(); $instance=$this->transformDependency($parameter, $parameters); if(!is_null($instance)){ $methodParameters[]=$instance; }elseif(!isset($parameters[$name]) && $parameter->isDefaultValueAvailable()){ $methodParameters[]=$parameter->getDefaultValue(); }else{ $methodParameters[]=isset($parameters[$name]) ? $parameters[$name] : null; } } return $methodParameters; }
Related recommendations:
Detailed explanation of PHP's method of automatic dependency injection based on reflection mechanism
How to use the corresponding interface of Laravel 5.5?
The above is the detailed content of Laravel5.5 controller parameter passing order problem and solution. For more information, please follow other related articles on the PHP Chinese website!