Skip to content

Commit

Permalink
[DoctrineBridge] Add argument to EntityValueResolver to set type aliases
Browse files Browse the repository at this point in the history
This allows for fixing symfony#51765; with a consequential Doctrine bundle update, the resolve_target_entities configuration can be injected similarly to ResolveTargetEntityListener in the Doctrine codebase.

Alternatively the config and ValueResolver can be injected using a compiler pass in the Symfony core code, however the value resolver seems to be configured in the Doctrine bundle already.
  • Loading branch information
NanoSector committed Apr 10, 2024
1 parent 9b323c6 commit 8918690
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public function __construct(
private ManagerRegistry $registry,
private ?ExpressionLanguage $expressionLanguage = null,
private MapEntity $defaults = new MapEntity(),
/** @var array<class-string, class-string> */
private array $typeAliases = [],
) {
}

Expand All @@ -50,30 +52,35 @@ public function resolve(Request $request, ArgumentMetadata $argument): array
if (!$options->class || $options->disabled) {
return [];
}
if (!$manager = $this->getManager($options->objectManager, $options->class)) {

$class = array_key_exists($options->class, $this->typeAliases)
? $this->typeAliases[$options->class]
: $options->class;

if (!$manager = $this->getManager($options->objectManager, $class)) {
return [];
}

$message = '';
if (null !== $options->expr) {
if (null === $object = $this->findViaExpression($manager, $request, $options)) {
if (null === $object = $this->findViaExpression($manager, $request, $options, $class)) {
$message = sprintf(' The expression "%s" returned null.', $options->expr);
}
// find by identifier?
} elseif (false === $object = $this->find($manager, $request, $options, $argument->getName())) {
} elseif (false === $object = $this->find($manager, $request, $options, $argument->getName(), $class)) {
// find by criteria
if (!$criteria = $this->getCriteria($request, $options, $manager)) {
if (!$criteria = $this->getCriteria($request, $options, $manager, $class)) {
return [];
}
try {
$object = $manager->getRepository($options->class)->findOneBy($criteria);
$object = $manager->getRepository($class)->findOneBy($criteria);
} catch (NoResultException|ConversionException) {
$object = null;
}
}

if (null === $object && !$argument->isNullable()) {
throw new NotFoundHttpException($options->message ?? (sprintf('"%s" object not found by "%s".', $options->class, self::class).$message));
throw new NotFoundHttpException($options->message ?? (sprintf('"%s" object not found by "%s".', $class, self::class).$message));
}

return [$object];
Expand All @@ -94,7 +101,7 @@ private function getManager(?string $name, string $class): ?ObjectManager
return $manager->getMetadataFactory()->isTransient($class) ? null : $manager;
}

private function find(ObjectManager $manager, Request $request, MapEntity $options, string $name): false|object|null
private function find(ObjectManager $manager, Request $request, MapEntity $options, string $name, string $class): false|object|null
{
if ($options->mapping || $options->exclude) {
return false;
Expand All @@ -107,13 +114,13 @@ private function find(ObjectManager $manager, Request $request, MapEntity $optio

if ($options->evictCache && $manager instanceof EntityManagerInterface) {
$cacheProvider = $manager->getCache();
if ($cacheProvider && $cacheProvider->containsEntity($options->class, $id)) {
$cacheProvider->evictEntity($options->class, $id);
if ($cacheProvider && $cacheProvider->containsEntity($class, $id)) {
$cacheProvider->evictEntity($class, $id);
}
}

try {
return $manager->getRepository($options->class)->find($id);
return $manager->getRepository($class)->find($id);
} catch (NoResultException|ConversionException) {
return null;
}
Expand Down Expand Up @@ -150,7 +157,7 @@ private function getIdentifier(Request $request, MapEntity $options, string $nam
return false;
}

private function getCriteria(Request $request, MapEntity $options, ObjectManager $manager): array
private function getCriteria(Request $request, MapEntity $options, ObjectManager $manager, string $class): array
{
if (null === $mapping = $options->mapping) {
$mapping = $request->attributes->keys();
Expand All @@ -175,7 +182,7 @@ private function getCriteria(Request $request, MapEntity $options, ObjectManager
}

$criteria = [];
$metadata = $manager->getClassMetadata($options->class);
$metadata = $manager->getClassMetadata($class);

foreach ($mapping as $attribute => $field) {
if (!$metadata->hasField($field) && (!$metadata->hasAssociation($field) || !$metadata->isSingleValuedAssociation($field))) {
Expand All @@ -192,13 +199,13 @@ private function getCriteria(Request $request, MapEntity $options, ObjectManager
return $criteria;
}

private function findViaExpression(ObjectManager $manager, Request $request, MapEntity $options): ?object
private function findViaExpression(ObjectManager $manager, Request $request, MapEntity $options, string $class): ?object
{
if (!$this->expressionLanguage) {
throw new \LogicException(sprintf('You cannot use the "%s" if the ExpressionLanguage component is not available. Try running "composer require symfony/expression-language".', __CLASS__));
}

$repository = $manager->getRepository($options->class);
$repository = $manager->getRepository($class);
$variables = array_merge($request->attributes->all(), [
'repository' => $repository,
'request' => $request,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,38 @@ public function testResolveWithId(string|int $id)
$this->assertSame([$object], $resolver->resolve($request, $argument));
}

/**
* @dataProvider idsProvider
*/
public function testResolveWithIdAndTypeAlias(string|int $id)
{
$manager = $this->getMockBuilder(ObjectManager::class)->getMock();
$registry = $this->createRegistry($manager);
$resolver = new EntityValueResolver($registry,
null,
new MapEntity(),
['stdClassAlias' => 'stdClass'],
);

$request = new Request();
$request->attributes->set('id', $id);

$argument = $this->createArgument('stdClassAlias', new MapEntity(id: 'id'));

$repository = $this->getMockBuilder(ObjectRepository::class)->getMock();
$repository->expects($this->once())
->method('find')
->with($id)
->willReturn($object = new \stdClass());

$manager->expects($this->once())
->method('getRepository')
->with('stdClass')
->willReturn($repository);

$this->assertSame([$object], $resolver->resolve($request, $argument));
}

public function testResolveWithNullId()
{
$manager = $this->getMockBuilder(ObjectManager::class)->getMock();
Expand Down

0 comments on commit 8918690

Please sign in to comment.