• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 13200284839

07 Feb 2025 12:56PM UTC coverage: 0.0% (-8.2%) from 8.164%
13200284839

Pull #6952

github

web-flow
Merge 519fbf8cc into 62377f880
Pull Request #6952: fix: errors retrieval and documentation

0 of 206 new or added lines in 17 files covered. (0.0%)

10757 existing lines in 366 files now uncovered.

0 of 47781 relevant lines covered (0.0%)

0.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Laravel/Routing/IriConverter.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Laravel\Routing;
15

16
use ApiPlatform\Metadata\CollectionOperationInterface;
17
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
18
use ApiPlatform\Metadata\Exception\ItemNotFoundException;
19
use ApiPlatform\Metadata\Exception\OperationNotFoundException;
20
use ApiPlatform\Metadata\Exception\RuntimeException;
21
use ApiPlatform\Metadata\Get;
22
use ApiPlatform\Metadata\GetCollection;
23
use ApiPlatform\Metadata\HttpOperation;
24
use ApiPlatform\Metadata\IdentifiersExtractorInterface;
25
use ApiPlatform\Metadata\IriConverterInterface;
26
use ApiPlatform\Metadata\Operation;
27
use ApiPlatform\Metadata\Operation\Factory\OperationMetadataFactoryInterface;
28
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
29
use ApiPlatform\Metadata\ResourceClassResolverInterface;
30
use ApiPlatform\Metadata\UrlGeneratorInterface;
31
use ApiPlatform\Metadata\Util\ClassInfoTrait;
32
use ApiPlatform\Metadata\Util\ResourceClassInfoTrait;
33
use ApiPlatform\State\ProviderInterface;
34
use Illuminate\Database\Eloquent\Relations\Relation;
35
// use Illuminate\Routing\Router;
36
use Symfony\Component\Routing\Exception\ExceptionInterface as RoutingExceptionInterface;
37
use Symfony\Component\Routing\RouterInterface;
38

39
class IriConverter implements IriConverterInterface
40
{
41
    use ClassInfoTrait;
42
    use ResourceClassInfoTrait;
43
    // use UriVariablesResolverTrait;
44

45
    /**
46
     * @var array<string, Operation>
47
     */
48
    private array $localOperationCache = [];
49

50
    /**
51
     * @var array<string, Operation>
52
     */
53
    private array $localIdentifiersExtractorOperationCache = [];
54

55
    // , UriVariablesConverterInterface $uriVariablesConverter = null TODO
56
    /**
57
     * @param ProviderInterface<object> $provider
58
     */
59
    public function __construct(private readonly ProviderInterface $provider, private readonly OperationMetadataFactoryInterface $operationMetadataFactory, private readonly RouterInterface $router, private readonly IdentifiersExtractorInterface $identifiersExtractor, ResourceClassResolverInterface $resourceClassResolver, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ?IriConverterInterface $decorated = null)
60
    {
61
        $this->resourceClassResolver = $resourceClassResolver;
×
62
    }
63

64
    /**
65
     * @param array<string, mixed> $context
66
     */
67
    public function getResourceFromIri(string $iri, array $context = [], ?Operation $operation = null): object
68
    {
69
        $parameters = $this->router->match($iri);
×
70
        if (!isset($parameters['_api_resource_class'], $parameters['_api_operation_name'], $parameters['uri_variables'])) {
×
71
            throw new InvalidArgumentException(\sprintf('No resource associated to "%s".', $iri));
×
72
        }
73

74
        $operation = $this->resourceMetadataCollectionFactory->create($parameters['_api_resource_class'])->getOperation($parameters['_api_operation_name']);
×
75

76
        if ($operation instanceof CollectionOperationInterface) {
×
77
            throw new InvalidArgumentException(\sprintf('The iri "%s" references a collection not an item.', $iri));
×
78
        }
79

80
        if (!$operation instanceof HttpOperation) {
×
81
            throw new RuntimeException(\sprintf('The iri "%s" does not reference an HTTP operation.', $iri));
×
82
        }
83

84
        if ($item = $this->provider->provide($operation, $parameters['uri_variables'], $context)) {
×
85
            return $item; // @phpstan-ignore-line
×
86
        }
87

88
        throw new ItemNotFoundException(\sprintf('Item not found for "%s".', $iri));
×
89
    }
90

91
    /**
92
     * @param array<string, mixed> $context
93
     */
94
    public function getIriFromResource(object|string $resource, int $referenceType = UrlGeneratorInterface::ABS_PATH, ?Operation $operation = null, array $context = []): ?string
95
    {
96
        $resourceClass = $context['force_resource_class'] ?? (\is_string($resource) ? $resource : $this->getObjectClass($resource));
×
97
        if ($resource instanceof Relation) {
×
98
            $resourceClass = $this->getObjectClass($resource->getRelated());
×
99
        }
100

101
        if (isset($context['item_uri_template'])) {
×
102
            $operation = $this->operationMetadataFactory->create($context['item_uri_template']);
×
103
        }
104

105
        $localOperationCacheKey = ($operation?->getName() ?? '').$resourceClass.(\is_string($resource) ? '_c' : '_i');
×
106
        if ($operation && isset($this->localOperationCache[$localOperationCacheKey])) {
×
107
            return $this->generateRoute($resource, $referenceType, $this->localOperationCache[$localOperationCacheKey], $context, $this->localIdentifiersExtractorOperationCache[$localOperationCacheKey] ?? null);
×
108
        }
109

110
        if (!$this->resourceClassResolver->isResourceClass($resourceClass)) {
×
111
            return $this->generateSkolemIri($resource, $referenceType, $operation, $context, $resourceClass);
×
112
        }
113

114
        // This is only for when a class (that is not a resource) extends another one that is a resource, we should remove this behavior
115
        if (!\is_string($resource) && !isset($context['force_resource_class'])) {
×
116
            $resourceClass = $this->getResourceClass($resource, true);
×
117
        }
118

119
        if (!$operation) {
×
120
            $operation = (new Get())->withClass($resourceClass);
×
121
        }
122

123
        if ($operation instanceof HttpOperation && 301 === $operation->getStatus()) {
×
124
            $operation = ($operation instanceof CollectionOperationInterface ? new GetCollection() : new Get())->withClass($operation->getClass());
×
125
            unset($context['uri_variables']);
×
126
        }
127

128
        $identifiersExtractorOperation = $operation;
×
129
        // In symfony the operation name is the route name, try to find one if none provided
130
        if (
131
            !$operation->getName()
×
132
            || ($operation instanceof HttpOperation && 'POST' === $operation->getMethod())
×
133
        ) {
134
            $forceCollection = $operation instanceof CollectionOperationInterface;
×
135
            try {
136
                $operation = $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation(null, $forceCollection, true);
×
137
                $identifiersExtractorOperation = $operation;
×
138
            } catch (OperationNotFoundException) {
×
139
            }
140
        }
141

142
        if (!$operation->getName() || ($operation instanceof HttpOperation && $operation->getUriTemplate() && str_starts_with($operation->getUriTemplate(), SkolemIriConverter::SKOLEM_URI_TEMPLATE))) {
×
143
            return $this->generateSkolemIri($resource, $referenceType, $operation, $context, $resourceClass);
×
144
        }
145

146
        $this->localOperationCache[$localOperationCacheKey] = $operation;
×
147
        $this->localIdentifiersExtractorOperationCache[$localOperationCacheKey] = $identifiersExtractorOperation;
×
148

149
        return $this->generateRoute($resource, $referenceType, $operation, $context, $identifiersExtractorOperation);
×
150
    }
151

152
    /**
153
     * @param array<string,mixed> $context
154
     */
155
    private function generateRoute(object|string $resource, int $referenceType = UrlGeneratorInterface::ABS_PATH, ?Operation $operation = null, array $context = [], ?Operation $identifiersExtractorOperation = null): string
156
    {
157
        $identifiers = $context['uri_variables'] ?? [];
×
158

159
        if (\is_object($resource)) {
×
160
            try {
161
                $identifiers = $this->identifiersExtractor->getIdentifiersFromItem($resource, $identifiersExtractorOperation, $context);
×
162
            } catch (RuntimeException $e) {
×
163
                // We can try using context uri variables if any
164
                if (!$identifiers) {
×
165
                    throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getClass()), $e->getCode(), $e);
×
166
                }
167
            }
168
        }
169

170
        try {
NEW
171
            $routeName = $operation instanceof HttpOperation ? ($operation->getRouteName() ?? $operation->getName()) : $operation->getName();
×
172

NEW
173
            return $this->router->generate($routeName, $identifiers, $operation->getUrlGenerationStrategy() ?? $referenceType);
×
174
        } catch (RoutingExceptionInterface $e) {
×
175
            throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getClass()), $e->getCode(), $e);
×
176
        }
177
    }
178

179
    /**
180
     * @param object|class-string  $resource
181
     * @param array<string, mixed> $context
182
     */
183
    private function generateSkolemIri(object|string $resource, int $referenceType = UrlGeneratorInterface::ABS_PATH, ?Operation $operation = null, array $context = [], ?string $resourceClass = null): string
184
    {
185
        if (!$this->decorated) {
×
186
            throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $resourceClass));
×
187
        }
188

189
        // Use a skolem iri, the route is defined in genid.xml
190
        return $this->decorated->getIriFromResource($resource, $referenceType, $operation, $context);
×
191
    }
192
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc