api-platform / core

The server component of API Platform: hypermedia and GraphQL APIs in minutes
https://api-platform.com
MIT License
2.44k stars 868 forks source link

Error with Laravel and DTO #6717

Open noweh opened 4 days ago

noweh commented 4 days ago

API Platform version(s) affected: api-platform/laravel 4.0.3

Description
When attempting to use Data Transfer Objects (DTOs) with Laravel, as described in the documentation, I encounter a 404 error on the newly added route:

{
  "@context": "/api/contexts/Error",
  "@id": "/api/errors/404.jsonld",
  "@type": "hydra:Error",
  "trace": [
    {
      "file": "XXXX\\project\\vendor\\api-platform\\laravel\\State\\SwaggerUiProvider.php",
      "line": 57,
      "function": "provide",
      "class": "ApiPlatform\\State\\Provider\\ReadProvider",
      "type": "->"
    },
   ....
}

How to reproduce

  1. Create a DTO resource class:
<?php

namespace App\ApiResource;

use ApiPlatform\Metadata\Get;
use App\State\CatProvider;

#[Get(uriTemplate: '/cats/{id}', provider: CatProvider::class)]
class Cat
{
    public string $id;
    public string $name;
    public int $age;
}
  1. Configure api-platform.php to include the DTO resource path:
// config/api-platform.php

// ...
return [
    'resources' => [
        app_path('ApiResource'),
        app_path('Models'),
    ],

    // ...
];
  1. Implement the CatProviderclass that fetches data from the model:
<?php

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Models\Animal as AnimalModel;

final class CatProvider implements ProviderInterface
{
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
    {
        $animal = AnimalModel::find($uriVariables['id']);

        return new AnimalModel([
            'id' => $animal->id,
            'name' => $animal->name,
            'age' => $animal->age
        ]);
    }
}
  1. Register the provider in the AppServiceProvider:
<?php

namespace App\Providers;

use App\State\CatProvider;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Foundation\Application;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        $this->app->singleton(CatProvider::class, function (Application $app) {
            return new CatProvider();
        });

        $this->app->tag([CatProvider::class], 'provider');
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        //
    }
}

Expected Behavior The GET /cats/{id} route should return the correct data for the Catresource using the CatProvider.

vinceAmstoutz commented 2 days ago

@noweh Thanks for your report.

There is an error in the following documentation. You need in your CatProvider to return the DTO ApiResource/Cat.php and not the model Models/Animal.php.

However, if you still want to do this you need to add the $fillable property to your Dto, like this:

<?php

namespace App\Models;

use ApiPlatform\Metadata\ApiResource;
use Illuminate\Database\Eloquent\Model;

#[ApiResource]
class Animal extends Model
{
    protected $fillable = ['id', 'name', 'age'];
}

This PR https://github.com/api-platform/docs/pull/2036 will solve the issue in the documentation