-
-
Notifications
You must be signed in to change notification settings - Fork 960
Description
API Platform version(s) affected: 4.3.1
Description
While trying to set up a FreeTextQueryFilter on my doctrine ORM entities, I discovered that Embedded objects did not seem to work with PartialSearchFilter.
In effect, trying to use the filter in a call will result in a doctrine error : Association name expected, 'address' is not an association. because the filter added a join to the QueryBuilder on a class that's not an association.
How to reproduce
Here is a simple entity with an embedded item and a failing QueryParameter :
<?php
# PostCard.php
namespace App\Entity;
use ApiPlatform\Doctrine\Orm\Filter\FreeTextQueryFilter;
use ApiPlatform\Doctrine\Orm\Filter\PartialSearchFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\QueryParameter;
use Doctrine\ORM\Mapping as ORM;
#[QueryParameter(
key: 'free_search',
filter: new FreeTextQueryFilter(new PartialSearchFilter()),
properties: [
'address.city',
],
)]
#[ApiResource()]
#[ORM\Entity]
class PostCard
{
#[ORM\Id]
#[ORM\Column(type: 'integer')]
#[ORM\GeneratedValue(strategy: 'SEQUENCE')]
private ?int $id = null;
public function __construct(
#[ORM\Embedded(class: Address::class)]
public readonly Address $address
) {
throw new \Exception('Not implemented');
}
public function getId(): ?int
{
return $this->id;
}
}
# Address.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Embeddable]
class Address
{
public function __construct(
#[ORM\Column]
public readonly int $city,
#[ORM\Column]
public readonly string $street,
#[ORM\Column]
public readonly string $zipCode,
) {}
}Calling https://localhost/post_cards?free_search=test will result in the aforementioned error
Possible Solution
In PartialSearchFilter.php:46, addNestedParameterJoins(), is always called, regardless of wether the property is acually a relation, and the method itself just checks extraProperties['nested_properties_info'], which is provided for Embedded.
In SearchFilter, isPropertyNested() is called first, to ensure the property is a relation, which prevents the join.
This method looks for the relation in doctrine metadatas, which prevents mistaking Embedded for relations : return $this->getClassMetadata($resourceClass)->hasAssociation(substr($property, 0, $pos))