Skip to content

Laravel: Using a form request with the rules option of ApiResource makes GET operations fail with a paginator error #7280

Open
@PicassoHouessou

Description

@PicassoHouessou

API Platform version(s) affected: 4.1.17

Description

When we use a form request to apply validation rules using the rules option of the ApiResource, like in this code:

#[ApiResource(
    rules: AddressTypeRequest::class,
    operations: [
        new GetCollection(),
        new Post(),
        new Put(),
    ])]
class AddressType extends Model

When you try to test GetCollection on the API, it fails. here is the error

{
  "trace": [
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php",
      "line": 256,
      "function": "handleError",
      "class": "Illuminate\\Foundation\\Bootstrap\\HandleExceptions",
      "type": "->"
    },
    {
      "file": "/app/app/Http/Requests/AddressTypeRequest.php",
      "line": 24,
      "function": "Illuminate\\Foundation\\Bootstrap\\{closure}",
      "class": "Illuminate\\Foundation\\Bootstrap\\HandleExceptions",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php",
      "line": 36,
      "function": "rules",
      "class": "App\\Http\\Requests\\AddressTypeRequest",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/Util.php",
      "line": 43,
      "function": "Illuminate\\Container\\{closure}",
      "class": "Illuminate\\Container\\BoundMethod",
      "type": "::"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php",
      "line": 96,
      "function": "unwrapIfClosure",
      "class": "Illuminate\\Container\\Util",
      "type": "::"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php",
      "line": 35,
      "function": "callBoundMethod",
      "class": "Illuminate\\Container\\BoundMethod",
      "type": "::"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/Container.php",
      "line": 754,
      "function": "call",
      "class": "Illuminate\\Container\\BoundMethod",
      "type": "::"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php",
      "line": 153,
      "function": "call",
      "class": "Illuminate\\Container\\Container",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php",
      "line": 118,
      "function": "validationRules",
      "class": "Illuminate\\Foundation\\Http\\FormRequest",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php",
      "line": 91,
      "function": "createDefaultValidator",
      "class": "Illuminate\\Foundation\\Http\\FormRequest",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Validation/ValidatesWhenResolvedTrait.php",
      "line": 25,
      "function": "getValidatorInstance",
      "class": "Illuminate\\Foundation\\Http\\FormRequest",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Providers/FormRequestServiceProvider.php",
      "line": 30,
      "function": "validateResolved",
      "class": "Illuminate\\Foundation\\Http\\FormRequest",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/Container.php",
      "line": 1495,
      "function": "Illuminate\\Foundation\\Providers\\{closure}",
      "class": "Illuminate\\Foundation\\Providers\\FormRequestServiceProvider",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/Container.php",
      "line": 1431,
      "function": "fireCallbackArray",
      "class": "Illuminate\\Container\\Container",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/Container.php",
      "line": 1417,
      "function": "fireAfterResolvingCallbacks",
      "class": "Illuminate\\Container\\Container",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/Container.php",
      "line": 908,
      "function": "fireResolvingCallbacks",
      "class": "Illuminate\\Container\\Container",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php",
      "line": 1077,
      "function": "resolve",
      "class": "Illuminate\\Container\\Container",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Container/Container.php",
      "line": 821,
      "function": "resolve",
      "class": "Illuminate\\Foundation\\Application",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php",
      "line": 1057,
      "function": "make",
      "class": "Illuminate\\Container\\Container",
      "type": "->"
    },
    {
      "file": "/app/vendor/api-platform/laravel/State/ValidateProvider.php",
      "line": 64,
      "function": "make",
      "class": "Illuminate\\Foundation\\Application",
      "type": "->"
    },
    {
      "file": "/app/vendor/api-platform/state/Provider/SecurityParameterProvider.php",
      "line": 50,
      "function": "provide",
      "class": "ApiPlatform\\Laravel\\State\\ValidateProvider",
      "type": "->"
    },
    {
      "file": "/app/vendor/api-platform/laravel/State/ParameterValidatorProvider.php",
      "line": 87,
      "function": "provide",
      "class": "ApiPlatform\\State\\Provider\\SecurityParameterProvider",
      "type": "->"
    },
    {
      "file": "/app/vendor/api-platform/state/Provider/ParameterProvider.php",
      "line": 99,
      "function": "provide",
      "class": "ApiPlatform\\Laravel\\State\\ParameterValidatorProvider",
      "type": "->"
    },
    {
      "file": "/app/vendor/api-platform/laravel/State/AccessCheckerProvider.php",
      "line": 45,
      "function": "provide",
      "class": "ApiPlatform\\State\\Provider\\ParameterProvider",
      "type": "->"
    },
    {
      "file": "/app/vendor/api-platform/state/Provider/ContentNegotiationProvider.php",
      "line": 51,
      "function": "provide",
      "class": "ApiPlatform\\Laravel\\State\\AccessCheckerProvider",
      "type": "->"
    },
    {
      "file": "/app/vendor/api-platform/laravel/Controller/ApiPlatformController.php",
      "line": 77,
      "function": "provide",
      "class": "ApiPlatform\\State\\Provider\\ContentNegotiationProvider",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Controller.php",
      "line": 54,
      "function": "__invoke",
      "class": "ApiPlatform\\Laravel\\Controller\\ApiPlatformController",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
      "line": 43,
      "function": "callAction",
      "class": "Illuminate\\Routing\\Controller",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
      "line": 265,
      "function": "dispatch",
      "class": "Illuminate\\Routing\\ControllerDispatcher",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
      "line": 211,
      "function": "runController",
      "class": "Illuminate\\Routing\\Route",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 808,
      "function": "run",
      "class": "Illuminate\\Routing\\Route",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 169,
      "function": "Illuminate\\Routing\\{closure}",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/app/vendor/api-platform/laravel/ApiPlatformMiddleware.php",
      "line": 47,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "ApiPlatform\\Laravel\\ApiPlatformMiddleware",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 126,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 807,
      "function": "then",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 786,
      "function": "runRouteWithinStack",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 750,
      "function": "runRoute",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
      "line": 739,
      "function": "dispatchToRoute",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
      "line": 200,
      "function": "dispatch",
      "class": "Illuminate\\Routing\\Router",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 169,
      "function": "Illuminate\\Foundation\\Http\\{closure}",
      "class": "Illuminate\\Foundation\\Http\\Kernel",
      "type": "->"
    },
    {
      "file": "/app/vendor/barryvdh/laravel-debugbar/src/Middleware/InjectDebugbar.php",
      "line": 66,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "Barryvdh\\Debugbar\\Middleware\\InjectDebugbar",
      "type": "->"
    },
    {
      "file": "/app/app/Http/Middleware/LocalizeApi.php",
      "line": 25,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "App\\Http\\Middleware\\LocalizeApi",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
      "line": 21,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",
      "line": 31,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
      "line": 21,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",
      "line": 51,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php",
      "line": 27,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "Illuminate\\Http\\Middleware\\ValidatePostSize",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
      "line": 109,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php",
      "line": 61,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "Illuminate\\Http\\Middleware\\HandleCors",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php",
      "line": 58,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "Illuminate\\Http\\Middleware\\TrustProxies",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/InvokeDeferredCallbacks.php",
      "line": 22,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Middleware\\InvokeDeferredCallbacks",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePathEncoding.php",
      "line": 26,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 208,
      "function": "handle",
      "class": "Illuminate\\Http\\Middleware\\ValidatePathEncoding",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
      "line": 126,
      "function": "Illuminate\\Pipeline\\{closure}",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
      "line": 175,
      "function": "then",
      "class": "Illuminate\\Pipeline\\Pipeline",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
      "line": 144,
      "function": "sendRequestThroughRouter",
      "class": "Illuminate\\Foundation\\Http\\Kernel",
      "type": "->"
    },
    {
      "file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php",
      "line": 1219,
      "function": "handle",
      "class": "Illuminate\\Foundation\\Http\\Kernel",
      "type": "->"
    },
    {
      "file": "/app/public/index.php",
      "line": 19,
      "function": "handleRequest",
      "class": "Illuminate\\Foundation\\Application",
      "type": "->"
    }
  ],
  "type": "/errors/500",
  "title": "An error occurred",
  "status": 500,
  "detail": "Undefined property: ApiPlatform\\Laravel\\Eloquent\\Paginator::$id"
}

However, using the rules option on a specific operation works. For example, this code works:

#[ApiResource(
    operations: [
        new GetCollection(),
        new Post( rules: AddressTypeRequest::class),
        new Put( rules: AddressTypeRequest::class),
    ])]
class AddressType extends Model

How to reproduce

1️⃣ Create a Form Request.
2️⃣ Add the Form Request to the ApiResource using the rules option:

#[ApiResource(
    rules: YourFormRequest::class,
)]
class YourModel extends Model

3️⃣ Try to fetch GetCollection data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions