-
-
Notifications
You must be signed in to change notification settings - Fork 932
Description
API Platform version(s) affected: 3.1.12
Description
I have in my data model the User entity which presents the InheritanceType annotation in order to apply inheritance, then I have a child class called Patient which extends from User, when I perform a graphql query to the users table with a patient id it gives me the following error "Resolve only handles items of class User but retrieved item is of class Patient.".
How to reproduce
Step 1: Create model
#[ApiResource(
normalizationContext: ['groups' => ['user_read', 'read'], 'enabled_max_depth' => true],
denormalizationContext: ['groups' => ['user_write']],
forceEager: false,
paginationType: 'page',
)]
#[ORM\Entity]
#[ORM\InheritanceType('JOINED')]
#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
#[ORM\DiscriminatorMap(['user' => User::class, 'patient' => Patient::class])]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: Types::INTEGER)]
protected ?int $id = null;
#[ORM\Column(type: Types::STRING, length: 50, nullable: true)]
#[Groups(['user_read', 'user_write'])]
private ?string $name;
#[ORM\Column(type: Types::STRING, length: 255, nullable: true)]
#[Groups(['user_write'])]
private ?string $password;
public function getId(): ?int
{
return $this->id;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(?string $password): self
{
$this->password = $password;
return $this;
}
public function eraseCredentials(): void
{
}
public function getRoles(): array
{
return ['ROLE_USER'];
}
}
#[ApiResource(
normalizationContext: ['groups' => ['patient_read', 'read'], 'enabled_max_depth' => true],
denormalizationContext: ['groups' => ['patient_write']],
forceEager: false,
paginationType: 'page',
)]
#[ORM\Entity]
class Patient extends User
{
...patient fields
public function getRoles(): array
{
return ['ROLE_PATIENT'];
}
}
Step 2: Create one user record & one patient record in BD. As we can see, there is a relationship between the patient and user ids.
Step 3: Attempting to get user data given a patient id, in this case the id = 2 belongs to the created patient.
query findUser {
user(id: "/api/users/2") {
id
name
}
}
Possible Solution
ApiPlatform\GraphQl\Resolver\Factory\ItemResolverFactor:109
Current:
if ($resourceClass !== $itemClass) {
throw new \UnexpectedValueException(sprintf($errorMessage, (new \ReflectionClass($resourceClass))->getShortName(), (new \ReflectionClass($itemClass))->getShortName()));
}
Solution:
//if the classes of the item and the resource differ then we check that the found item is also not an instance of $resourceClass
if ($resourceClass !== $itemClass && !$item instanceof $resourceClass) {
throw new \UnexpectedValueException(sprintf($errorMessage, (new \ReflectionClass($resourceClass))->getShortName(), (new \ReflectionClass($itemClass))->getShortName()));
}
Additional Context
Error example:
More info:
In my case, I could search directly in the patient table, but since I only want to show the typical user data, not the patient-specific ones, it is more convenient to search in their source, in addition to making the logic more complex in case of having more instances that inherit from the User class.
Benefits: I can obtain an existing user instance given a user IRI with the id of a patient as we see in the Step 3, and I can also search for a user with the IRI of a patient class. In the end patient is also a user instance.
Example
query findUser {
user(id: "/api/patients/2") {
id
name
}
}