Laravel Invokable Single Motion Controllers – How Do They Actually Work?
Are Laravel’s invokable controllers using the usual PHP __invoke() technique? If they’re, how does it work? What is the magic behind the __invoke technique anyway?
That is for many who are curious. When you’re within the underlying mechanics, preserve studying!
In a Laravel software, there are numerous methods to outline route actions. Nevertheless, on this article, I will not delve into that. There are many glorious assets obtainable on-line masking such subjects intimately — merely seek for them!
As a substitute, I am going to concentrate on Single Motion Controllers (SACs) and purpose to unravel the inside workings of this intriguing idea that has been obtainable to us since model 5.3, I consider.
In its awesomeness, Laravel permits builders to outline lean controllers – controllers with only a single technique known as __invoke, which the framework can mechanically parse and affiliate with its outlined route. You’ll be able to study extra here. To date so good!
Nicely, as you already know PHP comes bundled with plenty of helpful magic methods which are mechanically executed at particular factors through the execution life cycle.
A kind of strategies occurs to be known as __invoke. In keeping with the documentation
The __invoke() technique is known as when a script tries to name an object as a perform.
With that definition, I used to be curious.
- Are these two strategies the identical factor?
- At what stage does Laravel really initialise my Controller and name it as a perform?
- Does this imply the framework now has a special route motion mapping to know/fear about?
That’s the scope of this text. To strive go underneath the hood, enhance understanding and get some solutions!
Laravel goes by means of plenty of steps to load and match/affiliate routes with their particular actions or route handlers in case you come from a special language.
This course of begins on the RouteServiceProvider and ends at IlluminateRoutingRouteAction particularly on the parse technique.
The parse technique is particulary attention-grabbing as that is the place the suitable motion is outlined and affiliate with a given route.
On the very backside of this technique, it’s best to see one thing just like this snippet beneath (some code take away for readability)
// ... IlluminateRoutingRouteAction
public static perform parse($uri, $motion){
// ... checks for different motion sorts
if (! static::containsSerializedClosure($motion) && is_string($motion['uses']) && ! str_contains($motion['uses'], '@')) {
$motion['uses'] = static::makeInvokable($motion['uses']);
}
return $motion;
}
That is the purpose the place Laravel is checking the chance that the present route’s motion may very well be an invokable motion.
A eager eye might spot one thing attention-grabbing already!
If it’s assigning the results of the test to the usual $motion[‘uses’] assortment — is __invoke simply a normal class technique like create, retailer and so forth?
If we bounce to the makeInvokable technique we see
// ... IlluminateRoutingRouteAction
protected static perform makeInvokable($motion)
{
if (! method_exists($motion, '__invoke')) {
throw new UnexpectedValueException("Invalid route action: [{$action}].");
}
return $motion.'@__invoke';
}
Let’s again just a little! It’s necessary to grasp what’s really happening right here.
The $motion variable simply holds your customary controller’s namespace title e.g
AppHttpControllersMyInvokableController.
What this technique does is solely reflecting on this controller’s metadata and test if it comprises a way named __invoke. If not, it throws an exception. Commonplace stuff!
If the controller has such a way, it then appends the tactic title to namespace to construct a full motion path for the route. So the tip outcome will look one thing like
AppHttpControllersMyInvokableController@__invoke
However wait a minute, that is how we usually outline route actions within the first place! When you take a normal route, say Person registration, right here is how we might outline it within the routes/auth.php file.
Route::get('register', [RegisteredUserController::class, 'create']);
And this will likely be parsed to
AppHttpControllersAuthRegisteredUserController@create
If we evaluate these two outcomes
# With Invokable/Single Motion Controller AppHttpControllersMyInvokableController@__invoke
# Commonplace Route Controller - consumer register
AppHttpControllersAuthRegisteredUserController@create
The construction of the tip outcome (parsed motion string) appears fairly the identical. The “invokable” controller appears to simply be a glorified customary controller with one technique in it. It simply occurred to be a way that Laravel (not PHP) recognise!
It additionally solutions one in all our earlier questions concerning route-action mapping. No, there isn’t a new idea to know/fear about underneath the hood in the case of route-action mathing.
Additional extra, there may be actually nothing distinctive or magical concerning the __invoke technique. With only a bit of labor overwriting the RouteAction::makeInvokable($motion) technique, this technique might as effectively be known as __execute, __launch, __dance and so forth.. you get the gist!
Right here is my tough twist of the makeInvokable technique — (I’ll publish an article about extending core lessons sooner or later)
// IlluminateRoutingRouteAction
#[Override]protected static perform makeInvokable($motion)
{
$technique =self::resolveInvokableMethod($motion);
if (empty($technique)) {
throw new UnexpectedValueException("Invalid route action: [{$action}].");
}
return $motion . '@' . $technique;
}
# A attainable resolver
personal static perform resolveInvokableMethod($motion) : string
{
foreach (["__invoke", "__execute", "__dance"] as $worth) {
if (method_exists($motion, $worth)) {
return $worth;
}
}
return "";
}
Now in my controller I can have one thing just like the code beneath and it ought to work simply advantageous
declare(strict_types=1);
namespace AppHttpControllers;
class MyInvokableController
{
# as a substitute of __invoke!
public perform __execute()
{
return 'Yiiiipe --- It additionally works!!! ' . PHP_EOL;
}
}
As now we have seen, the __invoke technique in these Single Motion Controllers usually are not in any means associated to the PHP magic technique __invoke.
The thought stands out as the identical however one will likely be excused in pondering they’re the identical factor.
The PHP __invoke magic is simply invoked when the article is “invoked” or known as as a way.
For instance, take our imaginary Single Motion Contoller above, to implement it with a pure PHP magic __invoke technique the code would have look one thing like
# First get the article of the controller class $controller = new AppHttpControllersMyInvokableController()
# Then invoke the PHP's magic __invoke()
$controller();
And there can be no means of adjusting that technique title to one thing else aside from __invoke.
So, to summarise
- The __invoke technique in Laravel Single Motion Controllers has nothing to do with the usual PHP’s __invoke magic technique
- With only a bit of labor, we will add any variety of “invokable” strategies as we please or change it to one thing else like __execute, __launch and so forth as a substitute of __invoke
Hope you will have discovered one thing attention-grabbing! Keep curious, Laravel eternally! 🙂