ApiFilter Attribute

The attribute can be used on a property or on a class.

If the attribute is given over a property, the filter will be configured on the property. For example, let's add a search filter on name and on the prop property of the colors relation:

<?php
// api/src/Entity/DummyCar.php
namespace App\Entity;
 
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\DummyCarColor;
 
#[ApiResource]
class DummyCar
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    private ?int $id = null;
 
    #[ORM\Column]
    #[ApiFilter(SearchFilter::class, strategy: 'partial')]
    public ?string $name = null;
 
    #[ORM\OneToMany(mappedBy: "car", targetEntity: DummyCarColor::class)]
    #[ApiFilter(SearchFilter::class, properties: ['colors.prop' => 'ipartial'])]
    public Collection $colors;
 
    public function __construct()
    {
        $this->colors = new ArrayCollection();
    }
 
    // ...
}
<?php
// api/src/Entity/DummyCar.php
namespace App\Entity;
 
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\DummyCarColor;
 
#[ApiResource]
class DummyCar
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    private ?int $id = null;
 
    #[ORM\Column]
    #[ApiFilter(SearchFilter::class, strategy: 'partial')]
    public ?string $name = null;
 
    #[ORM\OneToMany(mappedBy: "car", targetEntity: DummyCarColor::class)]
    #[ApiFilter(SearchFilter::class, properties: ['colors.prop' => 'ipartial'])]
    public Collection $colors;
 
    public function __construct()
    {
        $this->colors = new ArrayCollection();
    }
 
    // ...
}

On the first property, name, it's straightforward. The first attribute argument is the filter class, the second specifies options, here, the strategy:

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

In the second attribute, we specify properties on which the filter should apply. It's necessary here because we don't want to filter colors but the prop property of the colors association. Note that for each given property we specify the strategy:

#[ApiFilter(SearchFilter::class, properties: ['colors.prop' => 'ipartial'])]
#[ApiFilter(SearchFilter::class, properties: ['colors.prop' => 'ipartial'])]

The ApiFilter attribute can be set on the class as well. If you don't specify any properties, it'll act on every property of the class.

For example, let's define three data filters (DateFilter, SearchFilter and BooleanFilter) and two serialization filters (PropertyFilter and GroupFilter) on our DummyCar class:

<?php
// api/src/Entity/DummyCar.php
namespace App\Entity;
 
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Serializer\Filter\GroupFilter;
use ApiPlatform\Serializer\Filter\PropertyFilter;
use Doctrine\ORM\Mapping as ORM;
 
#[ApiResource]
#[ApiFilter(BooleanFilter::class)]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)]
#[ApiFilter(SearchFilter::class, properties: ['colors.prop' => 'ipartial', 'name' => 'partial'])]
#[ApiFilter(PropertyFilter::class, arguments: ['parameterName' => 'foobar'])]
#[ApiFilter(GroupFilter::class, arguments: ['parameterName' => 'foobargroups'])]
class DummyCar
{
    // ...
}
 
<?php
// api/src/Entity/DummyCar.php
namespace App\Entity;
 
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Serializer\Filter\GroupFilter;
use ApiPlatform\Serializer\Filter\PropertyFilter;
use Doctrine\ORM\Mapping as ORM;
 
#[ApiResource]
#[ApiFilter(BooleanFilter::class)]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)]
#[ApiFilter(SearchFilter::class, properties: ['colors.prop' => 'ipartial', 'name' => 'partial'])]
#[ApiFilter(PropertyFilter::class, arguments: ['parameterName' => 'foobar'])]
#[ApiFilter(GroupFilter::class, arguments: ['parameterName' => 'foobargroups'])]
class DummyCar
{
    // ...
}
 

The BooleanFilter is applied to every Boolean property of the class. Indeed, in each core filter we check the Doctrine type. It's written only by using the filter class:

#[ApiFilter(BooleanFilter::class)]
#[ApiFilter(BooleanFilter::class)]

The DateFilter given here will be applied to every Date property of the DummyCar class with the DateFilter::EXCLUDE_NULL strategy:

#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)]

The SearchFilter here adds properties. The result is the exact same as the example with attributes on properties:

#[ApiFilter(SearchFilter::class, properties: ['colors.prop' => 'ipartial', 'name' => 'partial'])]
#[ApiFilter(SearchFilter::class, properties: ['colors.prop' => 'ipartial', 'name' => 'partial'])]

Note that you can specify the properties argument on every filter.

The next filters are not related to how the data is fetched but rather to how the serialization is done on those. We can give an arguments option (see here for the available arguments):

#[ApiFilter(PropertyFilter::class, arguments: ['parameterName' => 'foobar'])]
#[ApiFilter(GroupFilter::class, arguments: ['parameterName' => 'foobargroups'])]
#[ApiFilter(PropertyFilter::class, arguments: ['parameterName' => 'foobar'])]
#[ApiFilter(GroupFilter::class, arguments: ['parameterName' => 'foobargroups'])]

Copyright © 2023 Kévin Dunglas

Sponsored by Les-Tilleuls.coop