Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Rotario's avatar

API Nested shallow resource policy not registering correctly?

Hi All, I've got an API nested resource like so:

api.php

Route::apiResource('sites.machines', 'API\MainMachineController')->shallow();

and the controller uses: API/MainMachineController.php

public function __construct()
    {
        $this->authorizeResource(MainMachine::class);
    }

to register its policy. Now the POST to /sites/1/machines is authorized as expected, but when I instead try to PUT to /machines/1 to update the resource, I get a forbidden error, even if I just stick a return true; in the corresponding update method in the policy function like so: MainMachinePolicy.php

    public function update(User $user, MainMachine $mainMachine)
    {
        return true;
    }

Now looking at php artisan route:list It seems as if the model isn't registered correctly on the policy on the non-nested routes. I think this may be a bug?

|        | POST      | api/sites/{site}/machines   | sites.machines.store | App\Http\Controllers\API\MainMachineController@store                   | api,auth:sanctum,can:create,*App\MainMachine*  |
|        | GET|HEAD  | api/sites/{site}/machines   | sites.machines.index | App\Http\Controllers\API\MainMachineController@index                   | api,auth:sanctum,can:viewAny,*App\MainMachine*

|        | PUT|PATCH | api/machines/{machine}      | machines.update      | App\Http\Controllers\API\MainMachineController@update                  | api,auth:sanctum,can:update,*main_machine*     |
|        | GET|HEAD  | api/machines/{machine}      | machines.show        | App\Http\Controllers\API\MainMachineController@show                    | api,auth:sanctum,can:view,*main_machine*       |
|        | DELETE    | api/machines/{machine}      | machines.destroy     | App\Http\Controllers\API\MainMachineController@destroy                 | api,auth:sanctum,can:delete,*main_machine*     |
0 likes
4 replies
Tippin's avatar

@rotario Route model bindings must match the type hinted variable name, and in this case it expects MainMachine to be $machine instead of $mainMachine. Try

    public function update(User $user, MainMachine $machine)
    {
        return true;
    }

Be sure the type hints also match in your controller methods

Rotario's avatar

Hi Tippin, Yeah I spotted that once I'd posted this, have changed those - to no avail! Is there any way to debug whether the policy is even being called? I can't see it in the stack trace.

[
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php",
    "line": 198,
    "function": "prepareException",
    "class": "Illuminate\Foundation\Exceptions\Handler",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/app/Exceptions/Handler.php",
    "line": 57,
    "function": "render",
    "class": "Illuminate\Foundation\Exceptions\Handler",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php",
    "line": 51,
    "function": "render",
    "class": "App\Exceptions\Handler",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 172,
    "function": "handleException",
    "class": "Illuminate\Routing\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php",
    "line": 41,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Routing\Middleware\SubstituteBindings",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",
    "line": 59,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Routing\Middleware\ThrottleRequests",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php",
    "line": 44,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Auth\Middleware\Authenticate",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php",
    "line": 32,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 128,
    "function": "Laravel\Sanctum\Http\Middleware\{closure}",
    "class": "Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php",
    "line": 76,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Foundation\Http\Middleware\VerifyCsrfToken",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
    "line": 116,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
    "line": 62,
    "function": "handleStatefulRequest",
    "class": "Illuminate\Session\Middleware\StartSession",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Session\Middleware\StartSession",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php",
    "line": 37,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php",
    "line": 66,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Cookie\Middleware\EncryptCookies",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php",
    "line": 25,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 149,
    "function": "Laravel\Sanctum\Http\Middleware\{closure}",
    "class": "Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 103,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php",
    "line": 33,
    "function": "then",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 103,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
    "line": 687,
    "function": "then",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
    "line": 662,
    "function": "runRouteWithinStack",
    "class": "Illuminate\Routing\Router",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
    "line": 628,
    "function": "runRoute",
    "class": "Illuminate\Routing\Router",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
    "line": 617,
    "function": "dispatchToRoute",
    "class": "Illuminate\Routing\Router",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
    "line": 165,
    "function": "dispatch",
    "class": "Illuminate\Routing\Router",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 128,
    "function": "Illuminate\Foundation\Http\{closure}",
    "class": "Illuminate\Foundation\Http\Kernel",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
    "line": 21,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Foundation\Http\Middleware\TransformsRequest",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
    "line": 21,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Foundation\Http\Middleware\TransformsRequest",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php",
    "line": 27,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Foundation\Http\Middleware\ValidatePostSize",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php",
    "line": 63,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/fruitcake/laravel-cors/src/HandleCors.php",
    "line": 37,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Fruitcake\Cors\HandleCors",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/fideloper/proxy/src/TrustProxies.php",
    "line": 57,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 167,
    "function": "handle",
    "class": "Fideloper\Proxy\TrustProxies",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
    "line": 103,
    "function": "Illuminate\Pipeline\{closure}",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
    "line": 140,
    "function": "then",
    "class": "Illuminate\Pipeline\Pipeline",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
    "line": 109,
    "function": "sendRequestThroughRouter",
    "class": "Illuminate\Foundation\Http\Kernel",
    "type": "->"
  },
  {
    "file": "/home/vagrant/code/machinedown/public/index.php",
    "line": 55,
    "function": "handle",
    "class": "Illuminate\Foundation\Http\Kernel",
    "type": "->"
  }
]

R

Rotario's avatar

Ah - brill thanks! That sorted it

Please or to participate in this conversation.