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

api-platform / core / 20306196173

17 Dec 2025 02:27PM UTC coverage: 25.294% (-0.002%) from 25.296%
20306196173

push

github

web-flow
feat: enable to skip autoconfiguration with new `SkipAutoconfigure` attribute (#7467)

0 of 5 new or added lines in 1 file covered. (0.0%)

7803 existing lines in 234 files now uncovered.

14672 of 58006 relevant lines covered (25.29%)

29.35 hits per line

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

0.0
/src/Laravel/ApiPlatformDeferredProvider.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;
15

16
use ApiPlatform\GraphQl\Resolver\Factory\ResolverFactoryInterface;
17
use ApiPlatform\GraphQl\State\Provider\DenormalizeProvider as GraphQlDenormalizeProvider;
18
use ApiPlatform\GraphQl\Type\ContextAwareTypeBuilderInterface;
19
use ApiPlatform\GraphQl\Type\FieldsBuilder;
20
use ApiPlatform\GraphQl\Type\FieldsBuilderEnumInterface;
21
use ApiPlatform\GraphQl\Type\TypeConverterInterface;
22
use ApiPlatform\GraphQl\Type\TypesContainerInterface;
23
use ApiPlatform\JsonApi\Filter\SparseFieldset;
24
use ApiPlatform\JsonApi\Filter\SparseFieldsetParameterProvider;
25
use ApiPlatform\Laravel\Controller\ApiPlatformController;
26
use ApiPlatform\Laravel\Eloquent\Extension\FilterQueryExtension;
27
use ApiPlatform\Laravel\Eloquent\Extension\QueryExtensionInterface;
28
use ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter;
29
use ApiPlatform\Laravel\Eloquent\Filter\DateFilter;
30
use ApiPlatform\Laravel\Eloquent\Filter\EndSearchFilter;
31
use ApiPlatform\Laravel\Eloquent\Filter\EqualsFilter;
32
use ApiPlatform\Laravel\Eloquent\Filter\FilterInterface as EloquentFilterInterface;
33
use ApiPlatform\Laravel\Eloquent\Filter\JsonApi\SortFilter;
34
use ApiPlatform\Laravel\Eloquent\Filter\JsonApi\SortFilterParameterProvider;
35
use ApiPlatform\Laravel\Eloquent\Filter\OrderFilter;
36
use ApiPlatform\Laravel\Eloquent\Filter\PartialSearchFilter;
37
use ApiPlatform\Laravel\Eloquent\Filter\RangeFilter;
38
use ApiPlatform\Laravel\Eloquent\Filter\StartSearchFilter;
39
use ApiPlatform\Laravel\Eloquent\Metadata\Factory\Resource\EloquentResourceCollectionMetadataFactory;
40
use ApiPlatform\Laravel\Eloquent\State\CollectionProvider;
41
use ApiPlatform\Laravel\Eloquent\State\ItemProvider;
42
use ApiPlatform\Laravel\Eloquent\State\LinksHandler;
43
use ApiPlatform\Laravel\Eloquent\State\LinksHandlerInterface;
44
use ApiPlatform\Laravel\Eloquent\State\PersistProcessor;
45
use ApiPlatform\Laravel\Eloquent\State\RemoveProcessor;
46
use ApiPlatform\Laravel\Exception\ErrorHandler;
47
use ApiPlatform\Laravel\Metadata\CacheResourceCollectionMetadataFactory;
48
use ApiPlatform\Laravel\Metadata\ParameterValidationResourceMetadataCollectionFactory;
49
use ApiPlatform\Laravel\State\ParameterValidatorProvider;
50
use ApiPlatform\Laravel\State\SwaggerUiProcessor;
51
use ApiPlatform\Laravel\State\ValidateProvider;
52
use ApiPlatform\Metadata\IdentifiersExtractorInterface;
53
use ApiPlatform\Metadata\InflectorInterface;
54
use ApiPlatform\Metadata\Laravel\SkipAutoconfigure;
55
use ApiPlatform\Metadata\Operation\PathSegmentNameGeneratorInterface;
56
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
57
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
58
use ApiPlatform\Metadata\Resource\Factory\AlternateUriResourceMetadataCollectionFactory;
59
use ApiPlatform\Metadata\Resource\Factory\AttributesResourceMetadataCollectionFactory;
60
use ApiPlatform\Metadata\Resource\Factory\ConcernsResourceMetadataCollectionFactory;
61
use ApiPlatform\Metadata\Resource\Factory\FiltersResourceMetadataCollectionFactory;
62
use ApiPlatform\Metadata\Resource\Factory\FormatsResourceMetadataCollectionFactory;
63
use ApiPlatform\Metadata\Resource\Factory\InputOutputResourceMetadataCollectionFactory;
64
use ApiPlatform\Metadata\Resource\Factory\LinkFactoryInterface;
65
use ApiPlatform\Metadata\Resource\Factory\LinkResourceMetadataCollectionFactory;
66
use ApiPlatform\Metadata\Resource\Factory\NotExposedOperationResourceMetadataCollectionFactory;
67
use ApiPlatform\Metadata\Resource\Factory\OperationNameResourceMetadataCollectionFactory;
68
use ApiPlatform\Metadata\Resource\Factory\ParameterResourceMetadataCollectionFactory;
69
use ApiPlatform\Metadata\Resource\Factory\PhpDocResourceMetadataCollectionFactory;
70
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
71
use ApiPlatform\Metadata\Resource\Factory\UriTemplateResourceMetadataCollectionFactory;
72
use ApiPlatform\Metadata\ResourceAccessCheckerInterface;
73
use ApiPlatform\Metadata\ResourceClassResolverInterface;
74
use ApiPlatform\Metadata\Util\ReflectionClassRecursiveIterator;
75
use ApiPlatform\Serializer\Filter\FilterInterface as SerializerFilterInterface;
76
use ApiPlatform\Serializer\Filter\PropertyFilter;
77
use ApiPlatform\Serializer\Parameter\SerializerFilterParameterProvider;
78
use ApiPlatform\State\CallableProcessor;
79
use ApiPlatform\State\CallableProvider;
80
use ApiPlatform\State\ErrorProvider;
81
use ApiPlatform\State\Pagination\Pagination;
82
use ApiPlatform\State\ParameterProviderInterface;
83
use ApiPlatform\State\ProcessorInterface;
84
use ApiPlatform\State\Provider\ParameterProvider;
85
use ApiPlatform\State\Provider\SecurityParameterProvider;
86
use ApiPlatform\State\ProviderInterface;
87
use Illuminate\Contracts\Debug\ExceptionHandler;
88
use Illuminate\Contracts\Foundation\Application;
89
use Illuminate\Contracts\Support\DeferrableProvider;
90
use Illuminate\Support\ServiceProvider;
91
use Negotiation\Negotiator;
92
use Psr\Log\LoggerInterface;
93
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
94
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
95

96
class ApiPlatformDeferredProvider extends ServiceProvider implements DeferrableProvider
97
{
98
    /**
99
     * Register any application services.
100
     */
101
    public function register(): void
102
    {
103
        $directory = app_path();
×
104
        $classes = ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories([$directory], '(?!.*Test\.php$)');
×
105

NEW
106
        foreach ($classes as $className => $refl) {
×
NEW
107
            foreach ($refl->getAttributes() as $attribute) {
×
NEW
108
                if (SkipAutoconfigure::class === $attribute->getName()) {
×
NEW
109
                    unset($classes[$className]);
×
NEW
110
                    break;
×
111
                }
112
            }
113
        }
114

115
        $this->autoconfigure($classes, QueryExtensionInterface::class, [FilterQueryExtension::class]);
×
116
        $this->app->singleton(ItemProvider::class, function (Application $app) {
×
117
            $tagged = iterator_to_array($app->tagged(LinksHandlerInterface::class));
×
118

119
            return new ItemProvider(new LinksHandler($app, $app->make(ResourceMetadataCollectionFactoryInterface::class)), new ServiceLocator($tagged), $app->tagged(QueryExtensionInterface::class));
×
120
        });
×
121

122
        $this->app->singleton(CollectionProvider::class, function (Application $app) {
×
123
            $tagged = iterator_to_array($app->tagged(LinksHandlerInterface::class));
×
124

125
            return new CollectionProvider($app->make(Pagination::class), new LinksHandler($app, $app->make(ResourceMetadataCollectionFactoryInterface::class)), $app->tagged(QueryExtensionInterface::class), new ServiceLocator($tagged));
×
126
        });
×
127

128
        $this->app->singleton(SerializerFilterParameterProvider::class, function (Application $app) {
×
129
            $tagged = iterator_to_array($app->tagged(SerializerFilterInterface::class));
×
130

131
            return new SerializerFilterParameterProvider(new ServiceLocator($tagged));
×
132
        });
×
133
        $this->app->alias(SerializerFilterParameterProvider::class, 'api_platform.serializer.filter_parameter_provider');
×
134

135
        $this->app->singleton('filters', function (Application $app) {
×
136
            return new ServiceLocator(array_merge(
×
137
                iterator_to_array($app->tagged(SerializerFilterInterface::class)),
×
138
                iterator_to_array($app->tagged(EloquentFilterInterface::class))
×
139
            ));
×
140
        });
×
141

142
        $this->autoconfigure($classes, SerializerFilterInterface::class, [PropertyFilter::class]);
×
143

144
        $this->app->singleton(ParameterProvider::class, function (Application $app) {
×
145
            $tagged = iterator_to_array($app->tagged(ParameterProviderInterface::class));
×
146
            $tagged['api_platform.serializer.filter_parameter_provider'] = $app->make(SerializerFilterParameterProvider::class);
×
147

148
            return new ParameterProvider(
×
149
                new ParameterValidatorProvider(
×
150
                    new SecurityParameterProvider(
×
151
                        $app->make(ValidateProvider::class),
×
152
                        $app->make(ResourceAccessCheckerInterface::class)
×
153
                    ),
×
154
                ),
×
155
                new ServiceLocator($tagged)
×
156
            );
×
157
        });
×
158

159
        $this->autoconfigure($classes, ParameterProviderInterface::class, [SerializerFilterParameterProvider::class, SortFilterParameterProvider::class, SparseFieldsetParameterProvider::class]);
×
160

161
        $this->app->bind(FilterQueryExtension::class, function (Application $app) {
×
162
            $tagged = iterator_to_array($app->tagged(EloquentFilterInterface::class));
×
163

164
            return new FilterQueryExtension(new ServiceLocator($tagged));
×
165
        });
×
166

167
        $this->autoconfigure($classes, EloquentFilterInterface::class, [
×
168
            BooleanFilter::class,
×
169
            DateFilter::class,
×
170
            EndSearchFilter::class,
×
171
            EqualsFilter::class,
×
172
            OrderFilter::class,
×
173
            PartialSearchFilter::class,
×
174
            RangeFilter::class,
×
175
            StartSearchFilter::class,
×
176
            SortFilter::class,
×
177
            SparseFieldset::class,
×
178
        ]);
×
179

180
        $this->app->singleton(CallableProcessor::class, function (Application $app) {
×
181
            /** @var ConfigRepository */
182
            $config = $app['config'];
×
183
            $tagged = iterator_to_array($app->tagged(ProcessorInterface::class));
×
184

185
            if ($config->get('api-platform.swagger_ui.enabled', false)) {
×
186
                // TODO: tag SwaggerUiProcessor instead?
187
                $tagged['api_platform.swagger_ui.processor'] = $app->make(SwaggerUiProcessor::class);
×
188
            }
189

190
            return new CallableProcessor(new ServiceLocator($tagged));
×
191
        });
×
192

193
        $this->autoconfigure($classes, ProcessorInterface::class, [RemoveProcessor::class, PersistProcessor::class]);
×
194

195
        $this->app->singleton(CallableProvider::class, function (Application $app) {
×
196
            $tagged = iterator_to_array($app->tagged(ProviderInterface::class));
×
197

198
            return new CallableProvider(new ServiceLocator($tagged));
×
199
        });
×
200

201
        $this->autoconfigure($classes, ProviderInterface::class, [ItemProvider::class, CollectionProvider::class, ErrorProvider::class]);
×
202

203
        $this->app->singleton(ResourceMetadataCollectionFactoryInterface::class, function (Application $app) {
×
204
            /** @var ConfigRepository $config */
205
            $config = $app['config'];
×
206
            $formats = $config->get('api-platform.formats');
×
207

208
            if ($config->get('api-platform.swagger_ui.enabled', false) && !isset($formats['html'])) {
×
209
                $formats['html'] = ['text/html'];
×
210
            }
211

212
            return new CacheResourceCollectionMetadataFactory(
×
213
                new EloquentResourceCollectionMetadataFactory(
×
214
                    new ParameterValidationResourceMetadataCollectionFactory(
×
215
                        new ParameterResourceMetadataCollectionFactory(
×
216
                            $this->app->make(PropertyNameCollectionFactoryInterface::class),
×
217
                            $this->app->make(PropertyMetadataFactoryInterface::class),
×
218
                            new AlternateUriResourceMetadataCollectionFactory(
×
219
                                new FiltersResourceMetadataCollectionFactory(
×
220
                                    new FormatsResourceMetadataCollectionFactory(
×
221
                                        new InputOutputResourceMetadataCollectionFactory(
×
222
                                            new PhpDocResourceMetadataCollectionFactory(
×
223
                                                new OperationNameResourceMetadataCollectionFactory(
×
224
                                                    new LinkResourceMetadataCollectionFactory(
×
225
                                                        $app->make(LinkFactoryInterface::class),
×
226
                                                        new UriTemplateResourceMetadataCollectionFactory(
×
227
                                                            $app->make(LinkFactoryInterface::class),
×
228
                                                            $app->make(PathSegmentNameGeneratorInterface::class),
×
229
                                                            new NotExposedOperationResourceMetadataCollectionFactory(
×
230
                                                                $app->make(LinkFactoryInterface::class),
×
231
                                                                new AttributesResourceMetadataCollectionFactory(
×
232
                                                                    new ConcernsResourceMetadataCollectionFactory(
×
233
                                                                        null,
×
234
                                                                        $app->make(LoggerInterface::class),
×
235
                                                                        $config->get('api-platform.defaults', []),
×
236
                                                                        $config->get('api-platform.graphql.enabled'),
×
237
                                                                    ),
×
238
                                                                    $app->make(LoggerInterface::class),
×
239
                                                                    $config->get('api-platform.defaults', []),
×
240
                                                                    $config->get('api-platform.graphql.enabled'),
×
241
                                                                ),
×
242
                                                            )
×
243
                                                        ),
×
244
                                                        $config->get('api-platform.graphql.enabled')
×
245
                                                    )
×
246
                                                )
×
247
                                            )
×
248
                                        ),
×
249
                                        $formats,
×
250
                                        $config->get('api-platform.patch_formats'),
×
251
                                    )
×
252
                                )
×
253
                            ),
×
254
                            $app->make('filters'),
×
255
                            $app->make(CamelCaseToSnakeCaseNameConverter::class),
×
256
                            $this->app->make(LoggerInterface::class)
×
257
                        ),
×
258
                        $app->make('filters')
×
259
                    )
×
260
                ),
×
261
                true === $config->get('app.debug') ? 'array' : $config->get('api-platform.cache', 'file')
×
262
            );
×
263
        });
×
264

265
        $this->app->extend(
×
266
            ExceptionHandler::class,
×
267
            function (ExceptionHandler $decorated, Application $app) {
×
268
                /** @var ConfigRepository */
269
                $config = $app['config'];
×
270

271
                return new ErrorHandler(
×
272
                    $app,
×
273
                    $app->make(ResourceMetadataCollectionFactoryInterface::class),
×
274
                    $app->make(ApiPlatformController::class),
×
275
                    $app->make(IdentifiersExtractorInterface::class),
×
276
                    $app->make(ResourceClassResolverInterface::class),
×
277
                    $app->make(Negotiator::class),
×
278
                    $config->get('api-platform.exception_to_status'),
×
279
                    $config->get('app.debug'),
×
280
                    $config->get('api-platform.error_formats'),
×
281
                    $decorated
×
282
                );
×
283
            }
×
284
        );
×
285

286
        if (interface_exists(FieldsBuilderEnumInterface::class)) {
×
287
            $this->registerGraphQl();
×
288
        }
289
    }
290

291
    private function registerGraphQl(): void
292
    {
293
        $this->app->singleton('api_platform.graphql.state_provider.parameter', function (Application $app) {
×
294
            $tagged = iterator_to_array($app->tagged(ParameterProviderInterface::class));
×
295
            $tagged['api_platform.serializer.filter_parameter_provider'] = $app->make(SerializerFilterParameterProvider::class);
×
296

297
            return new ParameterProvider(
×
298
                new ParameterValidatorProvider(
×
299
                    new SecurityParameterProvider(
×
300
                        $app->make(GraphQlDenormalizeProvider::class),
×
301
                        $app->make(ResourceAccessCheckerInterface::class)
×
302
                    ),
×
303
                ),
×
304
                new ServiceLocator($tagged)
×
305
            );
×
306
        });
×
307

308
        $this->app->singleton(FieldsBuilderEnumInterface::class, function (Application $app) {
×
309
            /** @var ConfigRepository */
310
            $config = $app['config'];
×
311

312
            return new FieldsBuilder(
×
313
                $app->make(PropertyNameCollectionFactoryInterface::class),
×
314
                $app->make(PropertyMetadataFactoryInterface::class),
×
315
                $app->make(ResourceMetadataCollectionFactoryInterface::class),
×
316
                $app->make(ResourceClassResolverInterface::class),
×
317
                $app->make(TypesContainerInterface::class),
×
318
                $app->make(ContextAwareTypeBuilderInterface::class),
×
319
                $app->make(TypeConverterInterface::class),
×
320
                $app->make(ResolverFactoryInterface::class),
×
321
                $app->make('filters'),
×
322
                $app->make(Pagination::class),
×
323
                $app->make(NameConverterInterface::class),
×
324
                $config->get('api-platform.graphql.nesting_separator') ?? '__',
×
325
                $app->make(InflectorInterface::class)
×
326
            );
×
327
        });
×
328
    }
329

330
    /**
331
     * @param array<class-string, \ReflectionClass> $classes
332
     * @param class-string                          $interface
333
     * @param array<int, class-string>              $apiPlatformProviders
334
     */
335
    private function autoconfigure(array $classes, string $interface, array $apiPlatformProviders): void
336
    {
337
        $m = $apiPlatformProviders;
×
338
        foreach ($classes as $className => $refl) {
×
339
            if ($refl->implementsInterface($interface)) {
×
340
                $m[] = $className;
×
341
            }
342
        }
343

344
        if ($m) {
×
345
            $this->app->tag($m, $interface);
×
346
        }
347
    }
348

349
    /**
350
     * Get the services provided by the provider.
351
     *
352
     * @return array<int, string>
353
     */
354
    public function provides(): array
355
    {
356
        return [
×
357
            CallableProvider::class,
×
358
            CallableProcessor::class,
×
359
            ItemProvider::class,
×
360
            CollectionProvider::class,
×
361
            SerializerFilterParameterProvider::class,
×
362
            ParameterProvider::class,
×
363
            FilterQueryExtension::class,
×
364
            'filters',
×
365
            ResourceMetadataCollectionFactoryInterface::class,
×
366
            'api_platform.graphql.state_provider.parameter',
×
367
            FieldsBuilderEnumInterface::class,
×
368
            ExceptionHandlerInterface::class,
×
369
        ];
×
370
    }
371
}
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

© 2026 Coveralls, Inc