core: SearchFilter exact subresource on another identifier with IRI fails
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?
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 8
- Comments: 24 (7 by maintainers)
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?
There’s work that needs to be done on the SearchFilter regarding this issue (hardcoded id and things like this). The work we’ve done on 2.7-3.0 is meant to be able to fix these issues properly. Still, this is not a priority for now. Please create your own filter, it’ll be way easier then trying to work around ours. You can still use the SearchFitler on top of your own IdentityFilter.
Same here.
For now i’m forcing to use in entityA:
@ApiFilter(SearchFilter::class, properties={"entityB.uuid": "exact"})