Description
Symfony version(s) affected
6.3.x
Description
#[MapEntity]
attribute does not resolve an entity from its interface names, even it is implemented on the entity and aliased using doctrine.orm.resolve_target_entities
configuration.
Error screen says:
Controller "App\Controller::showUser" requires that you provide a value for the "$user" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or there is a non-optional argument after this one.
How to reproduce
config/packages/doctrine.yaml
doctrine:
orm:
resolve_target_entities:
App\Entity\UserInterface: App\Entity\User
src/Entity/UserInterface.php
interface UserInterface
{
public function getId(): ?int;
}
src/Entity/User.php
#[ORM\Entity]
class User implements UserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
public ?int $id = null;
}
src/Controller/Controller.php
class Controller
{
#[Route('/users/{id}', methods: ['GET']]
public function showUser(#[MapEntity] UserInterface $user): Response
{
return new JsonResponse($user);
}
}
Possible Solution
Problem 1: #[MapEntity]
does not accept interfaces
MapEntity checks the class existence by class_exists
. For interface names, the function will return false even if it exists. We can use class_exists($class) || interface_exists($class)
for resolve this.
Problem 2: EntityValueResolver uses ClassMetadataFactory::isTransient and it does not load metadata
To resolve entities aliased in resolve_target_entities
, we have to load their metadata before using. ClassMetadataFactory::isTransient
does not do so, then it will return true
. We should explicitly load metadata to resolve entities.
Additional Context
Specifying the actual entity as #[MapEntity(class: User::class)]
do a trick, but we actually separate the interface and the entity in different repository (integrated using Symfony Bundle system).