Issue
I'm trying to pass an additional argument to Gate
from some controller.
An error and a part of stack trace I got are as follows:
testing.ERROR: App\Providers\AuthServiceProvider::App\Providers\{closure}(): Argument #2 ($request) must be of type Illuminate\Http\Request, string given, called in /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php on line 477 {"exception":"[object] (TypeError(code: 0): App\\Providers\\AuthServiceProvider::App\\Providers\\{closure}(): Argument #2 ($request) must be of type Illuminate\\Http\\Request, string given, called in /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php on line 477 at /var/www/html/app/Providers/AuthServiceProvider.php:74)
[stacktrace]
#0 /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php(477): App\\Providers\\AuthServiceProvider->App\\Providers\\{closure}(NULL, 'Illuminate\\\\Http...')
#1 /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php(372): Illuminate\\Auth\\Access\\Gate->callAuthCallback(NULL, 'foo', Array)
#2 /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php(337): Illuminate\\Auth\\Access\\Gate->raw('foo', Array)
#3 /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php(324): Illuminate\\Auth\\Access\\Gate->inspect('foo', Array)
#4 /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authorize.php(43): Illuminate\\Auth\\Access\\Gate->authorize('foo', Array)
#5 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Auth\\Middleware\\Authorize->handle(Object(Illuminate\\Http\\Request), Object(Closure), 'foo', 'Illuminate\\\\Http...')
#6 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#7 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#8 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(127): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#9 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(103): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequest(Object(Illuminate\\Http\\Request), Object(Closure), Array)
#10 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(55): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequestUsingNamedLimiter(Object(Illuminate\\Http\\Request), Object(Closure), 'api', Object(Closure))
The problem is that $request
(shown below) comes to Gate
as string, not Request
type. Using dd
function inside Gate
(and temporarily removing the type hint), I found the value of $request
was merely a useless text: "Illuminate\Http\Request"
.
How can I pass $request
to Gate
keeping Request
type?
SomeController.php
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
class SomeController extends Controller
{
public function fooEvents(Request $request): Response
{
/* some processes */
}
}
AuthServiceProvider.php
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
public function boot()
{
$this->registerPolicies();
/**
* @param \App\Models\User $user
* @param Illuminate\Http\Request $request
* @return bool
*/
Gate::define('foo', function (?User $user, Request $request) {
/**
* The problem is that the argument $request comes here as string type,
* while I want it as Illuminate\Http\Request type;
*/
/* some processes */
});
}
api.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\SomeController;
Route::post('/foo-events', [SomeController::class, 'fooEvents'])
->middleware('can:foo,Illuminate\Http\Request')
->name('foo-events');
Note:
If I directly use Gate
inside the controller (shown below), it works fine. However I have to use Gate
via api.php due to our team's rule. Do you have any idea?
SomeController.php (A working example)
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Gate;
class SomeController extends Controller
{
public function fooEvents(Request $request): Response
{
if (!Gate::allows('foo', $request)) {
return abort(404);
}
/* some processes */
}
}
AuthServiceProvider.php (A working example)
(The same as the previous one)
api.php (A working example)
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\SomeController;
Route::post('/foo-events', [SomeController::class, 'fooEvents'])
->name('foo-events');
Solution
I did some tests on the can
middleware that resolves to Illuminate\Auth\Middleware\Authorize
.
It seems it can return a class_name if a className is provided (route model binding), or a route parameter (eg: my_route/{my_param}
, you can define your Gate with can:foo,my_param
).
As you stated, you could pass the request as a parameter if your gate was called from your controller. But from the route definition using the "can" middleware, I wasn't able to get the entire request object as parameter.
Still, you can use the helper request()
in your gate method to access the entire request.
Some details here and reading the src/Illuminate/Auth/Middleware/Authorize.php
getGateArguments()
method.
Note that you could also attach a Policy to your gate, and use the request()
helper.
Answered By - Mtxz
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.