Skip to content

Commit a2824ff

Browse files
authored
fix(laravel): defer autoconfiguration (#7040)
fixes #7033
1 parent 77ffc38 commit a2824ff

10 files changed

+300
-157
lines changed
+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
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;
15+
16+
use ApiPlatform\JsonApi\Filter\SparseFieldset;
17+
use ApiPlatform\JsonApi\Filter\SparseFieldsetParameterProvider;
18+
use ApiPlatform\Laravel\Eloquent\Extension\FilterQueryExtension;
19+
use ApiPlatform\Laravel\Eloquent\Extension\QueryExtensionInterface;
20+
use ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter;
21+
use ApiPlatform\Laravel\Eloquent\Filter\DateFilter;
22+
use ApiPlatform\Laravel\Eloquent\Filter\EqualsFilter;
23+
use ApiPlatform\Laravel\Eloquent\Filter\FilterInterface as EloquentFilterInterface;
24+
use ApiPlatform\Laravel\Eloquent\Filter\JsonApi\SortFilter;
25+
use ApiPlatform\Laravel\Eloquent\Filter\JsonApi\SortFilterParameterProvider;
26+
use ApiPlatform\Laravel\Eloquent\Filter\OrderFilter;
27+
use ApiPlatform\Laravel\Eloquent\Filter\PartialSearchFilter;
28+
use ApiPlatform\Laravel\Eloquent\Filter\RangeFilter;
29+
use ApiPlatform\Laravel\Eloquent\State\CollectionProvider;
30+
use ApiPlatform\Laravel\Eloquent\State\ItemProvider;
31+
use ApiPlatform\Laravel\Eloquent\State\LinksHandler;
32+
use ApiPlatform\Laravel\Eloquent\State\LinksHandlerInterface;
33+
use ApiPlatform\Laravel\Eloquent\State\PersistProcessor;
34+
use ApiPlatform\Laravel\Eloquent\State\RemoveProcessor;
35+
use ApiPlatform\Laravel\State\ParameterValidatorProvider;
36+
use ApiPlatform\Laravel\State\SwaggerUiProcessor;
37+
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
38+
use ApiPlatform\Metadata\ResourceAccessCheckerInterface;
39+
use ApiPlatform\Metadata\Util\ReflectionClassRecursiveIterator;
40+
use ApiPlatform\Serializer\Filter\FilterInterface as SerializerFilterInterface;
41+
use ApiPlatform\Serializer\Filter\PropertyFilter;
42+
use ApiPlatform\Serializer\Parameter\SerializerFilterParameterProvider;
43+
use ApiPlatform\State\CallableProcessor;
44+
use ApiPlatform\State\CallableProvider;
45+
use ApiPlatform\State\ErrorProvider;
46+
use ApiPlatform\State\Pagination\Pagination;
47+
use ApiPlatform\State\ParameterProviderInterface;
48+
use ApiPlatform\State\ProcessorInterface;
49+
use ApiPlatform\State\Provider\DeserializeProvider;
50+
use ApiPlatform\State\Provider\ParameterProvider;
51+
use ApiPlatform\State\Provider\SecurityParameterProvider;
52+
use ApiPlatform\State\ProviderInterface;
53+
use Illuminate\Contracts\Foundation\Application;
54+
use Illuminate\Contracts\Support\DeferrableProvider;
55+
use Illuminate\Support\ServiceProvider;
56+
57+
class ApiPlatformDeferredProvider extends ServiceProvider implements DeferrableProvider
58+
{
59+
/**
60+
* Register any application services.
61+
*/
62+
public function register(): void
63+
{
64+
$directory = app_path();
65+
$classes = ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories([$directory]);
66+
67+
$this->autoconfigure($classes, QueryExtensionInterface::class, [FilterQueryExtension::class]);
68+
$this->app->singleton(ItemProvider::class, function (Application $app) {
69+
$tagged = iterator_to_array($app->tagged(LinksHandlerInterface::class));
70+
71+
return new ItemProvider(new LinksHandler($app, $app->make(ResourceMetadataCollectionFactoryInterface::class)), new ServiceLocator($tagged), $app->tagged(QueryExtensionInterface::class));
72+
});
73+
74+
$this->app->singleton(CollectionProvider::class, function (Application $app) {
75+
$tagged = iterator_to_array($app->tagged(LinksHandlerInterface::class));
76+
77+
return new CollectionProvider($app->make(Pagination::class), new LinksHandler($app, $app->make(ResourceMetadataCollectionFactoryInterface::class)), $app->tagged(QueryExtensionInterface::class), new ServiceLocator($tagged));
78+
});
79+
80+
$this->app->singleton(SerializerFilterParameterProvider::class, function (Application $app) {
81+
$tagged = iterator_to_array($app->tagged(SerializerFilterInterface::class));
82+
83+
return new SerializerFilterParameterProvider(new ServiceLocator($tagged));
84+
});
85+
$this->app->alias(SerializerFilterParameterProvider::class, 'api_platform.serializer.filter_parameter_provider');
86+
87+
$this->app->singleton('filters', function (Application $app) {
88+
return new ServiceLocator(array_merge(
89+
iterator_to_array($app->tagged(SerializerFilterInterface::class)),
90+
iterator_to_array($app->tagged(EloquentFilterInterface::class))
91+
));
92+
});
93+
94+
$this->autoconfigure($classes, SerializerFilterInterface::class, [PropertyFilter::class]);
95+
96+
$this->app->singleton(ParameterProvider::class, function (Application $app) {
97+
$tagged = iterator_to_array($app->tagged(ParameterProviderInterface::class));
98+
$tagged['api_platform.serializer.filter_parameter_provider'] = $app->make(SerializerFilterParameterProvider::class);
99+
100+
return new ParameterProvider(
101+
new ParameterValidatorProvider(
102+
new SecurityParameterProvider(
103+
$app->make(DeserializeProvider::class),
104+
$app->make(ResourceAccessCheckerInterface::class)
105+
),
106+
),
107+
new ServiceLocator($tagged)
108+
);
109+
});
110+
111+
$this->autoconfigure($classes, ParameterProviderInterface::class, [SerializerFilterParameterProvider::class, SortFilterParameterProvider::class, SparseFieldsetParameterProvider::class]);
112+
113+
$this->app->bind(FilterQueryExtension::class, function (Application $app) {
114+
$tagged = iterator_to_array($app->tagged(EloquentFilterInterface::class));
115+
116+
return new FilterQueryExtension(new ServiceLocator($tagged));
117+
});
118+
119+
$this->autoconfigure($classes, EloquentFilterInterface::class, [
120+
BooleanFilter::class,
121+
EqualsFilter::class,
122+
PartialSearchFilter::class,
123+
DateFilter::class,
124+
OrderFilter::class,
125+
RangeFilter::class,
126+
SortFilter::class,
127+
SparseFieldset::class,
128+
]);
129+
130+
$this->app->singleton(CallableProcessor::class, function (Application $app) {
131+
/** @var ConfigRepository */
132+
$config = $app['config'];
133+
$tagged = iterator_to_array($app->tagged(ProcessorInterface::class));
134+
135+
if ($config->get('api-platform.swagger_ui.enabled', false)) {
136+
// TODO: tag SwaggerUiProcessor instead?
137+
$tagged['api_platform.swagger_ui.processor'] = $app->make(SwaggerUiProcessor::class);
138+
}
139+
140+
return new CallableProcessor(new ServiceLocator($tagged));
141+
});
142+
143+
$this->autoconfigure($classes, ProcessorInterface::class, [RemoveProcessor::class, PersistProcessor::class]);
144+
145+
$this->app->singleton(CallableProvider::class, function (Application $app) {
146+
$tagged = iterator_to_array($app->tagged(ProviderInterface::class));
147+
148+
return new CallableProvider(new ServiceLocator($tagged));
149+
});
150+
151+
$this->autoconfigure($classes, ProviderInterface::class, [ItemProvider::class, CollectionProvider::class, ErrorProvider::class]);
152+
}
153+
154+
/**
155+
* @param array<class-string, \ReflectionClass> $classes
156+
* @param class-string $interface
157+
* @param array<int, class-string> $apiPlatformProviders
158+
*/
159+
private function autoconfigure(array $classes, string $interface, array $apiPlatformProviders): void
160+
{
161+
$m = $apiPlatformProviders;
162+
foreach ($classes as $className => $refl) {
163+
if ($refl->implementsInterface($interface)) {
164+
$m[] = $className;
165+
}
166+
}
167+
168+
if ($m) {
169+
$this->app->tag($m, $interface);
170+
}
171+
}
172+
173+
/**
174+
* Get the services provided by the provider.
175+
*
176+
* @return array<int, string>
177+
*/
178+
public function provides(): array
179+
{
180+
return [
181+
CallableProvider::class,
182+
CallableProcessor::class,
183+
ItemProvider::class,
184+
CollectionProvider::class,
185+
SerializerFilterParameterProvider::class,
186+
ParameterProvider::class,
187+
FilterQueryExtension::class,
188+
'filters',
189+
];
190+
}
191+
}

0 commit comments

Comments
 (0)