1

For a route versionning and a better separation between the entity and the api output I made a resource that is not an entity linked to that entity by a provider. A had like to add some filters and make a pagination but it seems I can't natively.

I have for example an entity:

<?php

namespace App\Entity;

use App\Repository\UserApiKeyRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Blameable\Traits\BlameableEntity;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\Timestampable\Traits\TimestampableEntity;

#[ORM\Entity(repositoryClass: TreasureRepository::class)]
#[Gedmo\SoftDeleteable(fieldName: 'deletedAt', timeAware: true, hardDelete: false)]
class Treasure
{
use BlameableEntity;

use TimestampableEntity;

#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private int $id;

#[ORM\ManyToOne(inversedBy: 'treasures')]
#[ORM\JoinColumn(referencedColumnName: 'owner_id', nullable: false)]
private Owner $owner;

#[ORM\Column(length: 255)]
private string $name;

#[ORM\Column(type: Types::DECIMAL, precision: 10, scale: 2)]
private string $value;

#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
private ?\DateTimeInterface $deletedAt = null;

I made a resource:

<?php

namespace App\ApiResource\V1;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Post;
use App\Entity\Owner;

#[ApiResource(
    uriTemplate: '/owners/{owner_id}/treasures/{id}.{_format}',
    shortName: 'Treasures',
    operations: [
        new Get(),
        new Delete(
            processor: TreasureProcessor::class
        )
    ],
    uriVariables: [
        'owner_id' => new Link(
            fromProperty: 'treasures',
            fromClass: Owner::class
        ),
        'id' => new Link(
            fromClass: self::class
        )
    ],
    provider: TreasureProvider::class
)]
#[ApiResource(
    uriTemplate: '/owners/{owner_id}/treasures.{_format}',
    shortName: 'Treasures',
    operations: [
        new GetCollection(
            normalizationContext: ['groups' => 'list']
        ),
        new Post(
            denormalizationContext: ['groups' => 'create'],
            processor: TreasureProcessor::class,
        )
    ],
    uriVariables: [
        'owner_id' => new Link(
            fromProperty: 'treasures',
            fromClass: Owner::class
        )
    ],
    provider: TreasureProvider::class
)]
class TreasureResource
{
    #[Groups(['list'])]
    private ?int $id;
    #[Groups(['list', 'create'])]
    private string $name;
    #[Groups(['list', 'create'])]
    private string $value;
    private ?Owner $owner;

    public function __construct(
        string $name,
        string $value,
        ?Owner $owner = null,
        ?int $id = null
    )
    {
        $this->id = $id;
        $this->name = $name;
        $this->value = $value;
        $this->owner = $owner;
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getValue(): string
    {
        return $this->value;
    }

    public function getOwner(): ?Owner
    {
        return $this->owner;
    }
}

Like that I do not have pagination neither filters. If I add the filter attribute on name for example

#[ApiFilter(SearchFilter::class, strategy: 'partial')]

I have an error

Call to a member function getClassMetadata() on null

Also, I tried to add a pagination into my Provider that is not working well, I do not have the real result of "hydra:totalItems" that show the total of the current page and not all the results, and I do not have the previous, next, total pages informations provided by json-ld. I did not see a way to set the ApiPlatform pagination with data.

Here is a sample of my provider:

$owner = $this->ownerRepository->find($uriVariables['owner_id']);

/** ApiPlatform\State\Pagination\Pagination $this->pagination */
$page = $this->pagination->getPage($context);
$itemsPerPage = $this->pagination->getLimit($operation, $context);

$treasures = $this->treasureRepository->findByOwnerPaginated($owner, $page, $itemsPerPage);

return $this->collectionToResources($treasures); //returns an array

Anyone ran into theses issue and were able to solve or bypass them? I wish I could use ApiPlatform attributes out of the box even with this way of coding

1 Answer 1

0

I was looking for the same thing and I found it's now available since v3.1.8. To achieve it you've to use the stateOptions:

#[ApiResource(
    operations: [
        new Get(
            uriTemplate: '/entityClassAndCustomProviderResources/{id}',
            uriVariables: ['id']
        ),
        new GetCollection(
            uriTemplate: '/entityClassAndCustomProviderResources'
        ),
    ],
    provider: EntityClassAndCustomProviderResourceProvider::class,
    stateOptions: new Options(entityClass: SeparatedEntity::class)
)]
class EntityClassAndCustomProviderResource

Source code: https://github.com/api-platform/core/pull/5550/files#diff-15a5e5dac807773e03253202f2a43d4e695aa7ac2f43874116741a6d9b4c69ca

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.