API Platform version(s) affected: 2.5.5
Description
SearchFilter subresource with changed identifiers works with identifier but not with IRI identifier
How to reproduce
Add 2 classe like in https://api-platform.com/docs/core/identifiers/#changing-identifier-in-a-doctrine-entity , Person and Process
in class Person add
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Process", inversedBy="persons")
*/
private $process;
NOT working
http://localhost/api/person?process=%2Fapi%2Fprocesses%2F65b810c4-9db0-11ea-a2bd-a683e78c6b85
with error:
The identifier id is missing for a query of App\\Entity\\Process
Possible Solution
The first request hits vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php line 487 where the entity has no code set as identifier, only the id
Update 1:
Ohh my gosh, the SQL is formed around the id of the relation, that's why filtering by UUID only will give an empty array. Must be a filter of the field of subresource `process.uuid'. There is any way to use the IRI for it?
I am encountering the same issue when filtering on entities that have both numeric ID + uuid fields.
Here's a sandbox project reproducing the bug via the api-platform template with two entities: https://github.com/alexdo/api-platform-uuid-relation
Fixtures included; so make sure you run those when trying to reproduce ;)
In the example, a Greeting has many Greeters and Greeters have many Greetings.
The uuid-Fields of both entities are marked as @ApiProperty(identifier=true).
There's an @ApiFilter(SearchFilter::class, properties={"greeters": "exact"}) annotation on the Greeting class.
I'd expect GET https://localhost:8443/greetings?greeters=[some greeter IRI] to return a collection of greetings present in the many-to-many relation but instead see the same exception as described above: Pastebin link to the full stacktrace
Interestingly, GET https://localhost:8443/greetings?greeters=[some greeter UUID] is working fine. So @ApiProperty(identifier=true) resolution appears to work correctly but IRI resolution + custom identifier doesn't (pure speculation tbh).
It appears that it all begins in https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Orm/Filter/SearchFilter.php#L143
That's calling getIdFromValue() on SearchFilterTrait
https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Common/Filter/SearchFilterTrait.php#L120-L130
which, in turn, tries to resolve the IRI => ID by hitting the IriConverter->getItemFromIri($value, ['fetch_data' => false])
But, as fetch_data is set to false, the ItemDataProvider will try to create a Reference:
https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Orm/ItemDataProvider.php#L81-L84
Call with arguments written out:
$manager->getReference(Greeter::class, ["uuid" => (uuid of my greeter)]);
However, the Doctrine ORM will, of course, only look at the @ORM\Id property - and not at the API one ;)
This isn't a problem when using UUIDs directly, as IriConverter->getItemFromIri just bails out when looking for a matching Route ;)
I'd really like to provide a PR for this but don't really know where to start.
An option could be to prevent resolving the Itemdata in this problematic case (when doctrine id field !== api platform id), just resolve the IRI route attributes and adjust the final query to use uuid instead of the primary key.
In other words:
Doctrine\ORM\ORMException: The identifier id is missing for a query of X somehowThe best way of implementing this could possibly be splitting IriConverter->getItemFromIri into two methods: One that does raw route lookups and another one that uses these lookups + creates the reference.
WDYT?
Most helpful comment
I am encountering the same issue when filtering on entities that have both numeric ID + uuid fields.
Reproduction
Here's a sandbox project reproducing the bug via the api-platform template with two entities: https://github.com/alexdo/api-platform-uuid-relation
Fixtures included; so make sure you run those when trying to reproduce ;)
In the example, a Greeting has many Greeters and Greeters have many Greetings.
The uuid-Fields of both entities are marked as
@ApiProperty(identifier=true).There's an
@ApiFilter(SearchFilter::class, properties={"greeters": "exact"})annotation on the Greeting class.I'd expect
GET https://localhost:8443/greetings?greeters=[some greeter IRI]to return a collection of greetings present in the many-to-many relation but instead see the same exception as described above: Pastebin link to the full stacktraceInterestingly,
GET https://localhost:8443/greetings?greeters=[some greeter UUID]is working fine. So@ApiProperty(identifier=true)resolution appears to work correctly but IRI resolution + custom identifier doesn't (pure speculation tbh).Tracking it down
It appears that it all begins in https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Orm/Filter/SearchFilter.php#L143
That's calling getIdFromValue() on SearchFilterTrait
https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Common/Filter/SearchFilterTrait.php#L120-L130
which, in turn, tries to resolve the IRI => ID by hitting the IriConverter->getItemFromIri($value, ['fetch_data' => false])
But, as fetch_data is set to false, the ItemDataProvider will try to create a Reference:
https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Orm/ItemDataProvider.php#L81-L84
Call with arguments written out:
However, the Doctrine ORM will, of course, only look at the
@ORM\Idproperty - and not at the API one ;)This isn't a problem when using UUIDs directly, as IriConverter->getItemFromIri just bails out when looking for a matching Route ;)
Fixing this - where to begin?
I'd really like to provide a PR for this but don't really know where to start.
An option could be to prevent resolving the Itemdata in this problematic case (when doctrine id field !== api platform id), just resolve the IRI route attributes and adjust the final query to use uuid instead of the primary key.
In other words:
Doctrine\ORM\ORMException: The identifier id is missing for a query of XsomehowThe best way of implementing this could possibly be splitting IriConverter->getItemFromIri into two methods: One that does raw route lookups and another one that uses these lookups + creates the reference.
WDYT?