Skip to content

feat: allow to extend link handling #6061

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/Doctrine/Common/State/LinksHandlerLocatorTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Doctrine\Common\State;

use ApiPlatform\Doctrine\Orm\State\Options;
use ApiPlatform\Metadata\Exception\RuntimeException;
use ApiPlatform\Metadata\Operation;
use Psr\Container\ContainerInterface;

/**
* @internal
*/
trait LinksHandlerLocatorTrait
{
private ?ContainerInterface $handleLinksLocator;

private function getLinksHandler(Operation $operation): ?callable
{
if (!($options = $operation->getStateOptions()) || !$options instanceof Options) {
return null;
}

$handleLinks = $options->getHandleLinks();
if (\is_callable($handleLinks)) {
return $handleLinks;
}

if ($this->handleLinksLocator && \is_string($handleLinks) && $this->handleLinksLocator->has($handleLinks)) {
return [$this->handleLinksLocator->get($handleLinks), 'handleLinks'];
}

throw new RuntimeException(sprintf('Could not find handleLinks service "%s"', $handleLinks));
}
}
33 changes: 10 additions & 23 deletions src/Doctrine/Common/State/LinksHandlerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,17 @@
use ApiPlatform\Metadata\GraphQl\Operation as GraphQlOperation;
use ApiPlatform\Metadata\GraphQl\Query;
use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use Psr\Container\ContainerInterface;

trait LinksHandlerTrait
{
private ?ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory;
private ?ContainerInterface $handleLinksLocator;

/**
* @return Link[]
* @param array{linkClass?: string, linkProperty?: string}&array<string, mixed> $context
*
* @return \ApiPlatform\Metadata\Link[]
*/
private function getLinks(string $resourceClass, Operation $operation, array $context): array
{
Expand Down Expand Up @@ -90,9 +89,12 @@ private function getLinks(string $resourceClass, Operation $operation, array $co
return [$newLink];
}

/**
* @param array<int|string,mixed> $identifiers
*/
private function getIdentifierValue(array &$identifiers, string $name = null): mixed
{
if (isset($identifiers[$name])) {
if (null !== $name && isset($identifiers[$name])) {
$value = $identifiers[$name];
unset($identifiers[$name]);

Expand All @@ -102,6 +104,9 @@ private function getIdentifierValue(array &$identifiers, string $name = null): m
return array_shift($identifiers);
}

/**
* @return \ApiPlatform\Metadata\Link[]|array
*/
private function getOperationLinks(Operation $operation = null): array
{
if ($operation instanceof GraphQlOperation) {
Expand All @@ -114,22 +119,4 @@ private function getOperationLinks(Operation $operation = null): array

return [];
}

private function getLinksHandler(Operation $operation): ?callable
{
if (!($options = $operation->getStateOptions()) || !method_exists($options, 'getHandleLinks') || null === $options->getHandleLinks()) {
return null;
}

$handleLinks = $options->getHandleLinks(); // @phpstan-ignore-line method_exists called above
if (\is_callable($handleLinks)) {
return $handleLinks;
}

if ($this->handleLinksLocator && \is_string($handleLinks) && $this->handleLinksLocator->has($handleLinks)) {
return [$this->handleLinksLocator->get($handleLinks), 'handleLinks'];
}

throw new RuntimeException(sprintf('Could not find handleLinks service "%s"', $handleLinks));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ public function create(string $resourceClass): ResourceMetadataCollection

private function addDefaults(Operation $operation): Operation
{
$options = $operation->getStateOptions() ?: new Options();
if ($options instanceof Options && null === $options->getHandleLinks()) {
$options = $options->withHandleLinks('api_platform.doctrine.odm.links_handler');
$operation = $operation->withStateOptions($options);
}

if (null === $operation->getProvider()) {
$operation = $operation->withProvider($this->getProvider($operation));
}
Expand Down
5 changes: 4 additions & 1 deletion src/Doctrine/Odm/State/CollectionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace ApiPlatform\Doctrine\Odm\State;

use ApiPlatform\Doctrine\Common\State\LinksHandlerLocatorTrait;
use ApiPlatform\Doctrine\Odm\Extension\AggregationCollectionExtensionInterface;
use ApiPlatform\Doctrine\Odm\Extension\AggregationResultCollectionExtensionInterface;
use ApiPlatform\Exception\RuntimeException;
Expand All @@ -29,15 +30,17 @@
*/
final class CollectionProvider implements ProviderInterface
{
use LinksHandlerLocatorTrait;
use LinksHandlerTrait;

/**
* @param AggregationCollectionExtensionInterface[] $collectionExtensions
*/
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ManagerRegistry $managerRegistry, private readonly iterable $collectionExtensions = [], ContainerInterface $handleLinksLocator = null)
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, ManagerRegistry $managerRegistry, private readonly iterable $collectionExtensions = [], ContainerInterface $handleLinksLocator = null)
{
$this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory;
$this->handleLinksLocator = $handleLinksLocator;
$this->managerRegistry = $managerRegistry;
}

public function provide(Operation $operation, array $uriVariables = [], array $context = []): iterable
Expand Down
5 changes: 4 additions & 1 deletion src/Doctrine/Odm/State/ItemProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace ApiPlatform\Doctrine\Odm\State;

use ApiPlatform\Doctrine\Common\State\LinksHandlerLocatorTrait;
use ApiPlatform\Doctrine\Odm\Extension\AggregationItemExtensionInterface;
use ApiPlatform\Doctrine\Odm\Extension\AggregationResultItemExtensionInterface;
use ApiPlatform\Exception\RuntimeException;
Expand All @@ -32,15 +33,17 @@
*/
final class ItemProvider implements ProviderInterface
{
use LinksHandlerLocatorTrait;
use LinksHandlerTrait;

/**
* @param AggregationItemExtensionInterface[] $itemExtensions
*/
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ManagerRegistry $managerRegistry, private readonly iterable $itemExtensions = [], ContainerInterface $handleLinksLocator = null)
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, ManagerRegistry $managerRegistry, private readonly iterable $itemExtensions = [], ContainerInterface $handleLinksLocator = null)
{
$this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory;
$this->handleLinksLocator = $handleLinksLocator;
$this->managerRegistry = $managerRegistry;
}

public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object
Expand Down
3 changes: 3 additions & 0 deletions src/Doctrine/Odm/State/LinksHandlerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Doctrine\ODM\MongoDB\Aggregation\Builder;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\Persistence\ManagerRegistry;

/**
* @internal
Expand All @@ -28,6 +29,8 @@ trait LinksHandlerTrait
{
use CommonLinksHandlerTrait;

private ManagerRegistry $managerRegistry;

private function handleLinks(Builder $aggregationBuilder, array $identifiers, array $context, string $resourceClass, Operation $operation = null): void
{
if (!$identifiers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ private function addDefaults(Operation $operation): Operation
$operation = $operation->withProvider($this->getProvider($operation));
}

$options = $operation->getStateOptions() ?: new Options();
if ($options instanceof Options && null === $options->getHandleLinks()) {
$options = $options->withHandleLinks('api_platform.doctrine.orm.links_handler');
$operation = $operation->withStateOptions($options);
}

if (null === $operation->getProcessor()) {
$operation = $operation->withProcessor($this->getProcessor($operation));
}
Expand Down
5 changes: 4 additions & 1 deletion src/Doctrine/Orm/State/CollectionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace ApiPlatform\Doctrine\Orm\State;

use ApiPlatform\Doctrine\Common\State\LinksHandlerLocatorTrait;
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryResultCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
Expand All @@ -32,15 +33,17 @@
*/
final class CollectionProvider implements ProviderInterface
{
use LinksHandlerLocatorTrait;
use LinksHandlerTrait;

/**
* @param QueryCollectionExtensionInterface[] $collectionExtensions
*/
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ManagerRegistry $managerRegistry, private readonly iterable $collectionExtensions = [], ContainerInterface $handleLinksLocator = null)
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, ManagerRegistry $managerRegistry, private readonly iterable $collectionExtensions = [], ContainerInterface $handleLinksLocator = null)
{
$this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory;
$this->handleLinksLocator = $handleLinksLocator;
$this->managerRegistry = $managerRegistry;
}

public function provide(Operation $operation, array $uriVariables = [], array $context = []): iterable
Expand Down
5 changes: 4 additions & 1 deletion src/Doctrine/Orm/State/ItemProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace ApiPlatform\Doctrine\Orm\State;

use ApiPlatform\Doctrine\Common\State\LinksHandlerLocatorTrait;
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryResultItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
Expand All @@ -32,15 +33,17 @@
*/
final class ItemProvider implements ProviderInterface
{
use LinksHandlerLocatorTrait;
use LinksHandlerTrait;

/**
* @param QueryItemExtensionInterface[] $itemExtensions
*/
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ManagerRegistry $managerRegistry, private readonly iterable $itemExtensions = [], ContainerInterface $handleLinksLocator = null)
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, ManagerRegistry $managerRegistry, private readonly iterable $itemExtensions = [], ContainerInterface $handleLinksLocator = null)
{
$this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory;
$this->handleLinksLocator = $handleLinksLocator;
$this->managerRegistry = $managerRegistry;
}

public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object
Expand Down
37 changes: 37 additions & 0 deletions src/Doctrine/Orm/State/LinksHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Doctrine\Orm\State;

use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;

final class LinksHandler implements LinksHandlerInterface
{
use LinksHandlerTrait {
handleLinks as private handle;
}

public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, ManagerRegistry $managerRegistry)
{
$this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory;
$this->managerRegistry = $managerRegistry;
}

public function handleLinks(QueryBuilder $queryBuilder, array $uriVariables, QueryNameGeneratorInterface $queryNameGenerator, array $context): void
{
$this->handle($queryBuilder, $uriVariables, $queryNameGenerator, $context, $context['entityClass'], $context['operation']);
}
}
7 changes: 5 additions & 2 deletions src/Doctrine/Orm/State/LinksHandlerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
namespace ApiPlatform\Doctrine\Orm\State;

use ApiPlatform\Doctrine\Common\State\LinksHandlerTrait as CommonLinksHandlerTrait;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Operation;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;

/**
* @internal
Expand All @@ -27,7 +28,9 @@ trait LinksHandlerTrait
{
use CommonLinksHandlerTrait;

private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, QueryNameGenerator $queryNameGenerator, array $context, string $entityClass, Operation $operation): void
private ManagerRegistry $managerRegistry;

private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, QueryNameGeneratorInterface $queryNameGenerator, array $context, string $entityClass, Operation $operation): void
{
if (!$identifiers) {
return;
Expand Down
7 changes: 7 additions & 0 deletions src/Symfony/Bundle/Resources/config/doctrine_mongodb_odm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@
<argument type="service" id="doctrine_mongodb" />
<argument type="service" id="api_platform.doctrine.odm.metadata.resource.metadata_collection_factory.inner" />
</service>

<service id="api_platform.doctrine.odm.links_handler" class="ApiPlatform\Doctrine\Odm\State\LinksHandler">
<argument type="service" id="api_platform.metadata.resource.metadata_collection_factory" />
<argument type="service" id="doctrine_mongodb" />

<tag name="api_platform.doctrine.odm.links_handler" key="api_platform.doctrine.odm.links_handler" />
</service>
</services>

</container>
6 changes: 6 additions & 0 deletions src/Symfony/Bundle/Resources/config/doctrine_orm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@
<argument type="service" id="api_platform.doctrine.orm.metadata.resource.link_factory.inner" />
</service>

<service id="api_platform.doctrine.orm.links_handler" class="ApiPlatform\Doctrine\Orm\State\LinksHandler">
<argument type="service" id="api_platform.metadata.resource.metadata_collection_factory" />
<argument type="service" id="doctrine" />

<tag name="api_platform.doctrine.orm.links_handler" key="api_platform.doctrine.orm.links_handler" />
</service>
</services>

</container>