DEV Community

Cover image for Api-platform : filtrer les résultats uniquement sur l'utilisateur connecté
Aymeric Ratinaud
Aymeric Ratinaud

Posted on

Api-platform : filtrer les résultats uniquement sur l'utilisateur connecté

Imaginez avoir dans votre projet plusieurs entités avec la relation author comme par exemple ces deux entités suivantes :

<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use App\Repository\CourierFavoriteRepository;
use Doctrine\ORM\Mapping as ORM; 

#[ORM\Entity(repositoryClass: CourierFavoriteRepository::class)]
#[ApiResource()]
class Book
{ 
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\ManyToOne]
    #[ORM\JoinColumn(nullable: false)] 
    private ?User $author = null; 

    #[ORM\Column(length: 255)] 
    private ?string $title = null;

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

    public function getAuthor(): ?User
    {
        return $this->author;
    }

    public function setAuthor(?User $author): static
    {
        $this->author = $author;

        return $this;
    } 

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(string $title): self
    {
        $this->title = $title;

        return $this;
    }
}
Enter fullscreen mode Exit fullscreen mode
<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use App\Repository\TodoRepository; 
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: TodoRepository::class)]
#[ApiResource()]
class Todo
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\ManyToOne]
    #[ORM\JoinColumn(nullable: false)]
    private ?User $author = null;

    #[ORM\Column(type: Types::TEXT)]
    private ?string $content = null;

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

    public function getAuthor(): ?User
    {
        return $this->author;
    }

    public function setAuthor(?User $author): static
    {
        $this->author = $author;

        return $this;
    }

    public function getContent(): ?string
    {
        return $this->content;
    }

    public function setContent(string $content): static
    {
        $this->content = $content;

        return $this;
    }
}
Enter fullscreen mode Exit fullscreen mode

Nous voulons verrouiller les résultats des GET item et collection uniquement sur l'user connecté.

Nous allons créer une interface que nous implémenterons sur nos entités

<?php

namespace App\Entity;

interface CurrentUserIsAuthorInterface
{
    public function setAuthor(?User $author): static;
}
Enter fullscreen mode Exit fullscreen mode

Maitenant nous allons faire une DoctrineExtension qui va ajouter la contrainte where dans le QueryBuilder sur l'utilisateur connecté.

<?php

namespace App\DoctrineExtension;

use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use App\Entity\CurrentUserIsAuthorInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bundle\SecurityBundle\Security;

class CurrentUserIsAuthorExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
    public function __construct(
        private Security $security,
    ) {
    }

    public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
    {
        $this->currentUserIsAuthor($resourceClass, $queryBuilder);
    }

    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, ?Operation $operation = null, array $context = []): void
    {
        $this->currentUserIsAuthor($resourceClass, $queryBuilder);
    }

    /**
     * @param string $resourceClass
     * @param QueryBuilder $queryBuilder
     * @return void
     * @throws ReflectionException
     */
    public function currentUserIsAuthor(string $resourceClass, QueryBuilder $queryBuilder): void
    {
        $reflectionClass = new \ReflectionClass($resourceClass);
        if ($reflectionClass->implementsInterface(CurrentUserIsAuthorInterface::class)) {
            $alias = $queryBuilder->getRootAliases()[0];
            $queryBuilder->andWhere("$alias.author = :current_author")
                ->setParameter('current_author', $this->security->getUser()->getId());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Maintenant il nous reste à implémenter notre interface sur nos entités :

<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use App\Repository\CourierFavoriteRepository;
use Doctrine\ORM\Mapping as ORM; 

#[ORM\Entity(repositoryClass: CourierFavoriteRepository::class)]
#[ApiResource()]
class Book implements CurrentUserIsAuthorInterface
{ 
    // ...
Enter fullscreen mode Exit fullscreen mode
<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use App\Repository\TodoRepository; 
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: TodoRepository::class)]
#[ApiResource()]
class Todo implements CurrentUserIsAuthorInterface
{
    // ...
Enter fullscreen mode Exit fullscreen mode

Et c'est tout. Pour chaque GET item ou collection vous aurez uniquement les entrées avec l'utilisateurs qui est connecté 🚀

Lisez aussi "comment enregistrer automatiquement l'utilisateur connecté" : https://dev.to/aratinau/automatisons-lenregistrement-du-user-sur-nimporte-quelle-entite-4f68

Top comments (0)