The laravel5.5 controller provides the ability to automatically inject according to the method parameter type. But sometimes it can be slightly inconvenient, reflected in the method parameter injection is not exactly according to the parameter name, if changing the order of incoming parameters will cause the type mismatch error. In this paper, the depth analysis of the principle of injection is solved.
First, the controller method parameter injection step design
1. Adding routes in/routes/web.php
Route::get ('/diary/show/{diary}/{page} ', ' diary\diarycontroller@list ');
2, write the controller file diarycontroller.php put under the/app/http/controllers/diary/path
<?phpnamespace 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 model \app\diary and install to database (slightly)
<?phpnamespace app;use illuminate\database\eloquent\model;class Diary extends model{ protected $table = ' Diary ' ; Public $timestamps = false;}
4. Access Controller method
Open Browser input: "HTTP://127.0.0.1//DIARY/SHOW/4/12"
The Title field value of id=4 in the output data table diary and 12
Second, injection parameter type description
Description: Laravel generates an instance object and injects it into the Controller method based on the type of method parameter required in the {diary} and {page} variables and controller methods that match the request route.
There are three scenarios for different parameter types:
1, if the parameter type implements the Urlroutable interface (that is, inherits from the Illuminate\database\eloquent\model), then finds the ID value in the table corresponding to the model object to match the parameter value in the route record, and constructs the model object;
2, if the parameter type is a custom type (no implementation of the Urlroutable interface), then Laravel constructs an object after injection;
3. If the parameter type is the underlying data type, and the name is the name defined in the routing parameter, the value is obtained from the route parameter;
4. If the parameter type is the underlying data type, but the name is not defined in the routing parameter, the default value is used if there is a default value, otherwise the system prompts for an error.
Analysis of existing problems in injection parameters
Referring to the Java Spring MVC Framework, Laravel's parameter type injection is still flawed, mainly in the absence of full parameter name injection.
1. If you change the order of the controller parameters, there will be parameter type passing errors, such as changing the order of the parameters of the Show method controlled by Diarycontroller, which will cause an error to occur:
<?phpnamespace 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, because the parameter type is the underlying data type (see two (3)), not by the name of the parameters injected, so the code is changed as follows, will also run normal
<?phpnamespace 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); } }
Four, laravel5.5 controller method parameter injection source code analysis
1. The parameter type of Urlroutable interface is implemented by routing middleware illuminate\routing\middleware\substitutebinding.
Public function handle ($request, Closure $next) { $this->router->substitutebindings ($route = $request- >route ()); $this->router->substituteimplicitbindings ($route); Return $next ($request); }
The Substituteimplicitbindings method of Illuminate\routing\router
Public Function substituteimplicitbindings ($route) { Implicitroutebinding::resolveforroute ($this container, $route); }
Implemented in the Resolveforroute method of illuminate\routing\implicitroutebinding
public static function Resolveforroute ($container, $route) {//Gets the parameter value from the route parameter, $parameters for [' Diary ': ' 4 ', ' page ': ' n '] $parameters = $route->parameters (); Gets a list of function arguments for the controller, passed in Urlroutable::class here, returning only the parameters that implement the Urlroutable interface $signatureParameters = $route->signatureparameters (Urlroutable::class); foreach ($signatureParameters as $parameter) {if (! $parameterName = Static::getparametername ($parameter->n Ame, $parameters)) {continue; } $parameterValue = $parameters [$parameterName]; if ($parameterValue instanceof urlroutable) {continue; }//Build an instance of the model (base from Illuminate\database\eloquent\model), here is app\diary $instance = $container->make ($par Ameter->getclass ()->name); Bind the parameter values to the model, and participate in the Illuminate\database\eloquent\model resolveroutebinding method if (! $model = $instance->resolverout Ebinding ($parameterValue)) {throw(New Modelnotfoundexception)->setmodel (Get_class ($instance)); }//Inject model instance according to parameter name $route->setparameter ($parameterName, $model); } }
Additional Instructions:
This calls the $route object (Illuminate\routing\route Type) Setparameter method, which describes the model parameter type (see two (1)) It is exactly the same as the parameter type and parameter name to inject the model instance
2. Other types of controller parameters are bound when running the routing controller
The key sections are implemented in the Illuminate\routing\controllerdispatcher dispatch method:
Public Function Dispatch (Route $route, $controller, $method) { //parameters of the resolution controller method $parameters = $this Resolveclassmethoddependencies ( $route->parameterswithoutnulls (), $controller, $method ); if (method_exists ($controller, ' callaction ')) { //Callaction Call Controller method via Illuminate\routing\controller Return $controller->callaction ($method, $parameters); } Call the Controller method directly return $controller->{$method} (... array_values ($parameters)); }
Calling the Resolveclassmethoddependencies method
Public function resolvemethoddependencies (array $parameters, Reflectionfunctionabstract $reflector) {$instanceCount = 0; $values = Array_values ($parameters); Method parameters are obtained by means of the method parameter foreach ($reflector->getparameters () as $key = + $parameter) {//default value is returned if there is a default value, if the custom method is Build instance returns $instance = $this->transformdependency ($parameter, $parameters); if (! Is_null ($instance)) {$instanceCount + +; Instance injection for custom type (not implemented Urlroutable interface) $this->spliceintoparameters ($parameters, $key, $instance); } elseif (! isset ($values [$key-$instanceCount]) && $parameter->isdefaultvalueavailable () {//is not defined in the route parameter but has a default value of parameter injection $this->spliceintoparameters ($parameters, $key, $parameter- Getdefaultvalue ()); }} return $parameters; }
Summary of issues:
1, the model parameters (see two (1)) and the name of the underlying type defined in the routing parameters (see two (3)) must be in the order defined in the route first pass the controller method, otherwise there will be a type mismatch error;
2, the custom type (see two (2)) and the name is not defined in the routing parameters of the underlying type parameters (see two (4)), in the Controller method in the order in which they are passed in.
Five, the problem repair
Opens the/vendor/laravel/framework/src/illuminate/routing/routedependencyresolvertrait.php file,
Modify the Resolvemethoddependencies method to the following code
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; }
After modification, the controller method parameters are injected exactly by name and type, and the code becomes more concise and powerful!
If the parameter is not defined in the route and no default value is provided, then NULL is injected.
Related recommendations:
An explanation of the method of automatic dependency injection based on reflection mechanism for PHP
What is dependency injection?
How can I use the Laravel 5.5 interface?