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

api-platform / core / 14132025063

28 Mar 2025 03:10PM UTC coverage: 8.52% (-1.0%) from 9.544%
14132025063

push

github

soyuka
Merge 4.1

26 of 353 new or added lines in 22 files covered. (7.37%)

984 existing lines in 65 files now uncovered.

13394 of 157212 relevant lines covered (8.52%)

22.94 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\Eloquent\Extension\FilterQueryExtension;
26
use ApiPlatform\Laravel\Eloquent\Extension\QueryExtensionInterface;
27
use ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter;
28
use ApiPlatform\Laravel\Eloquent\Filter\DateFilter;
29
use ApiPlatform\Laravel\Eloquent\Filter\EndSearchFilter;
30
use ApiPlatform\Laravel\Eloquent\Filter\EqualsFilter;
31
use ApiPlatform\Laravel\Eloquent\Filter\FilterInterface as EloquentFilterInterface;
32
use ApiPlatform\Laravel\Eloquent\Filter\JsonApi\SortFilter;
33
use ApiPlatform\Laravel\Eloquent\Filter\JsonApi\SortFilterParameterProvider;
34
use ApiPlatform\Laravel\Eloquent\Filter\OrderFilter;
35
use ApiPlatform\Laravel\Eloquent\Filter\PartialSearchFilter;
36
use ApiPlatform\Laravel\Eloquent\Filter\RangeFilter;
37
use ApiPlatform\Laravel\Eloquent\Filter\StartSearchFilter;
38
use ApiPlatform\Laravel\Eloquent\Metadata\Factory\Resource\EloquentResourceCollectionMetadataFactory;
39
use ApiPlatform\Laravel\Eloquent\State\CollectionProvider;
40
use ApiPlatform\Laravel\Eloquent\State\ItemProvider;
41
use ApiPlatform\Laravel\Eloquent\State\LinksHandler;
42
use ApiPlatform\Laravel\Eloquent\State\LinksHandlerInterface;
43
use ApiPlatform\Laravel\Eloquent\State\PersistProcessor;
44
use ApiPlatform\Laravel\Eloquent\State\RemoveProcessor;
45
use ApiPlatform\Laravel\Metadata\CacheResourceCollectionMetadataFactory;
46
use ApiPlatform\Laravel\Metadata\ParameterValidationResourceMetadataCollectionFactory;
47
use ApiPlatform\Laravel\State\ParameterValidatorProvider;
48
use ApiPlatform\Laravel\State\SwaggerUiProcessor;
49
use ApiPlatform\Metadata\InflectorInterface;
50
use ApiPlatform\Metadata\Operation\PathSegmentNameGeneratorInterface;
51
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
52
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
53
use ApiPlatform\Metadata\Resource\Factory\AlternateUriResourceMetadataCollectionFactory;
54
use ApiPlatform\Metadata\Resource\Factory\AttributesResourceMetadataCollectionFactory;
55
use ApiPlatform\Metadata\Resource\Factory\ConcernsResourceMetadataCollectionFactory;
56
use ApiPlatform\Metadata\Resource\Factory\FiltersResourceMetadataCollectionFactory;
57
use ApiPlatform\Metadata\Resource\Factory\FormatsResourceMetadataCollectionFactory;
58
use ApiPlatform\Metadata\Resource\Factory\InputOutputResourceMetadataCollectionFactory;
59
use ApiPlatform\Metadata\Resource\Factory\LinkFactoryInterface;
60
use ApiPlatform\Metadata\Resource\Factory\LinkResourceMetadataCollectionFactory;
61
use ApiPlatform\Metadata\Resource\Factory\NotExposedOperationResourceMetadataCollectionFactory;
62
use ApiPlatform\Metadata\Resource\Factory\OperationNameResourceMetadataCollectionFactory;
63
use ApiPlatform\Metadata\Resource\Factory\ParameterResourceMetadataCollectionFactory;
64
use ApiPlatform\Metadata\Resource\Factory\PhpDocResourceMetadataCollectionFactory;
65
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
66
use ApiPlatform\Metadata\Resource\Factory\UriTemplateResourceMetadataCollectionFactory;
67
use ApiPlatform\Metadata\ResourceAccessCheckerInterface;
68
use ApiPlatform\Metadata\ResourceClassResolverInterface;
69
use ApiPlatform\Metadata\Util\ReflectionClassRecursiveIterator;
70
use ApiPlatform\Serializer\Filter\FilterInterface as SerializerFilterInterface;
71
use ApiPlatform\Serializer\Filter\PropertyFilter;
72
use ApiPlatform\Serializer\Parameter\SerializerFilterParameterProvider;
73
use ApiPlatform\State\CallableProcessor;
74
use ApiPlatform\State\CallableProvider;
75
use ApiPlatform\State\ErrorProvider;
76
use ApiPlatform\State\Pagination\Pagination;
77
use ApiPlatform\State\ParameterProviderInterface;
78
use ApiPlatform\State\ProcessorInterface;
79
use ApiPlatform\State\Provider\DeserializeProvider;
80
use ApiPlatform\State\Provider\ParameterProvider;
81
use ApiPlatform\State\Provider\SecurityParameterProvider;
82
use ApiPlatform\State\ProviderInterface;
83
use Illuminate\Contracts\Foundation\Application;
84
use Illuminate\Contracts\Support\DeferrableProvider;
85
use Illuminate\Support\ServiceProvider;
86
use Psr\Log\LoggerInterface;
87
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
88
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
89

90
class ApiPlatformDeferredProvider extends ServiceProvider implements DeferrableProvider
91
{
92
    /**
93
     * Register any application services.
94
     */
95
    public function register(): void
96
    {
NEW
97
        $directory = app_path();
×
NEW
98
        $classes = ReflectionClassRecursiveIterator::getReflectionClassesFromDirectories([$directory]);
×
99

NEW
100
        $this->autoconfigure($classes, QueryExtensionInterface::class, [FilterQueryExtension::class]);
×
NEW
101
        $this->app->singleton(ItemProvider::class, function (Application $app) {
×
NEW
102
            $tagged = iterator_to_array($app->tagged(LinksHandlerInterface::class));
×
103

NEW
104
            return new ItemProvider(new LinksHandler($app, $app->make(ResourceMetadataCollectionFactoryInterface::class)), new ServiceLocator($tagged), $app->tagged(QueryExtensionInterface::class));
×
NEW
105
        });
×
106

NEW
107
        $this->app->singleton(CollectionProvider::class, function (Application $app) {
×
NEW
108
            $tagged = iterator_to_array($app->tagged(LinksHandlerInterface::class));
×
109

NEW
110
            return new CollectionProvider($app->make(Pagination::class), new LinksHandler($app, $app->make(ResourceMetadataCollectionFactoryInterface::class)), $app->tagged(QueryExtensionInterface::class), new ServiceLocator($tagged));
×
NEW
111
        });
×
112

NEW
113
        $this->app->singleton(SerializerFilterParameterProvider::class, function (Application $app) {
×
NEW
114
            $tagged = iterator_to_array($app->tagged(SerializerFilterInterface::class));
×
115

NEW
116
            return new SerializerFilterParameterProvider(new ServiceLocator($tagged));
×
NEW
117
        });
×
NEW
118
        $this->app->alias(SerializerFilterParameterProvider::class, 'api_platform.serializer.filter_parameter_provider');
×
119

NEW
120
        $this->app->singleton('filters', function (Application $app) {
×
NEW
121
            return new ServiceLocator(array_merge(
×
NEW
122
                iterator_to_array($app->tagged(SerializerFilterInterface::class)),
×
NEW
123
                iterator_to_array($app->tagged(EloquentFilterInterface::class))
×
NEW
124
            ));
×
NEW
125
        });
×
126

NEW
127
        $this->autoconfigure($classes, SerializerFilterInterface::class, [PropertyFilter::class]);
×
128

NEW
129
        $this->app->singleton(ParameterProvider::class, function (Application $app) {
×
NEW
130
            $tagged = iterator_to_array($app->tagged(ParameterProviderInterface::class));
×
NEW
131
            $tagged['api_platform.serializer.filter_parameter_provider'] = $app->make(SerializerFilterParameterProvider::class);
×
132

NEW
133
            return new ParameterProvider(
×
NEW
134
                new ParameterValidatorProvider(
×
NEW
135
                    new SecurityParameterProvider(
×
NEW
136
                        $app->make(DeserializeProvider::class),
×
NEW
137
                        $app->make(ResourceAccessCheckerInterface::class)
×
NEW
138
                    ),
×
NEW
139
                ),
×
NEW
140
                new ServiceLocator($tagged)
×
NEW
141
            );
×
NEW
142
        });
×
143

NEW
144
        $this->autoconfigure($classes, ParameterProviderInterface::class, [SerializerFilterParameterProvider::class, SortFilterParameterProvider::class, SparseFieldsetParameterProvider::class]);
×
145

NEW
146
        $this->app->bind(FilterQueryExtension::class, function (Application $app) {
×
NEW
147
            $tagged = iterator_to_array($app->tagged(EloquentFilterInterface::class));
×
148

NEW
149
            return new FilterQueryExtension(new ServiceLocator($tagged));
×
NEW
150
        });
×
151

NEW
152
        $this->autoconfigure($classes, EloquentFilterInterface::class, [
×
NEW
153
            BooleanFilter::class,
×
NEW
154
            DateFilter::class,
×
NEW
155
            EndSearchFilter::class,
×
NEW
156
            EqualsFilter::class,
×
NEW
157
            OrderFilter::class,
×
NEW
158
            PartialSearchFilter::class,
×
NEW
159
            RangeFilter::class,
×
NEW
160
            StartSearchFilter::class,
×
NEW
161
            SortFilter::class,
×
NEW
162
            SparseFieldset::class,
×
NEW
163
        ]);
×
164

NEW
165
        $this->app->singleton(CallableProcessor::class, function (Application $app) {
×
166
            /** @var ConfigRepository */
NEW
167
            $config = $app['config'];
×
NEW
168
            $tagged = iterator_to_array($app->tagged(ProcessorInterface::class));
×
169

NEW
170
            if ($config->get('api-platform.swagger_ui.enabled', false)) {
×
171
                // TODO: tag SwaggerUiProcessor instead?
NEW
172
                $tagged['api_platform.swagger_ui.processor'] = $app->make(SwaggerUiProcessor::class);
×
173
            }
174

NEW
175
            return new CallableProcessor(new ServiceLocator($tagged));
×
NEW
176
        });
×
177

NEW
178
        $this->autoconfigure($classes, ProcessorInterface::class, [RemoveProcessor::class, PersistProcessor::class]);
×
179

NEW
180
        $this->app->singleton(CallableProvider::class, function (Application $app) {
×
NEW
181
            $tagged = iterator_to_array($app->tagged(ProviderInterface::class));
×
182

NEW
183
            return new CallableProvider(new ServiceLocator($tagged));
×
NEW
184
        });
×
185

NEW
186
        $this->autoconfigure($classes, ProviderInterface::class, [ItemProvider::class, CollectionProvider::class, ErrorProvider::class]);
×
187

NEW
188
        $this->app->singleton(ResourceMetadataCollectionFactoryInterface::class, function (Application $app) {
×
189
            /** @var ConfigRepository $config */
NEW
190
            $config = $app['config'];
×
NEW
191
            $formats = $config->get('api-platform.formats');
×
192

NEW
193
            if ($config->get('api-platform.swagger_ui.enabled', false) && !isset($formats['html'])) {
×
NEW
194
                $formats['html'] = ['text/html'];
×
195
            }
196

NEW
197
            return new CacheResourceCollectionMetadataFactory(
×
NEW
198
                new EloquentResourceCollectionMetadataFactory(
×
NEW
199
                    new ParameterValidationResourceMetadataCollectionFactory(
×
NEW
200
                        new ParameterResourceMetadataCollectionFactory(
×
NEW
201
                            $this->app->make(PropertyNameCollectionFactoryInterface::class),
×
NEW
202
                            $this->app->make(PropertyMetadataFactoryInterface::class),
×
NEW
203
                            new AlternateUriResourceMetadataCollectionFactory(
×
NEW
204
                                new FiltersResourceMetadataCollectionFactory(
×
NEW
205
                                    new FormatsResourceMetadataCollectionFactory(
×
NEW
206
                                        new InputOutputResourceMetadataCollectionFactory(
×
NEW
207
                                            new PhpDocResourceMetadataCollectionFactory(
×
NEW
208
                                                new OperationNameResourceMetadataCollectionFactory(
×
NEW
209
                                                    new LinkResourceMetadataCollectionFactory(
×
NEW
210
                                                        $app->make(LinkFactoryInterface::class),
×
NEW
211
                                                        new UriTemplateResourceMetadataCollectionFactory(
×
NEW
212
                                                            $app->make(LinkFactoryInterface::class),
×
NEW
213
                                                            $app->make(PathSegmentNameGeneratorInterface::class),
×
NEW
214
                                                            new NotExposedOperationResourceMetadataCollectionFactory(
×
NEW
215
                                                                $app->make(LinkFactoryInterface::class),
×
NEW
216
                                                                new AttributesResourceMetadataCollectionFactory(
×
NEW
217
                                                                    new ConcernsResourceMetadataCollectionFactory(
×
NEW
218
                                                                        null,
×
NEW
219
                                                                        $app->make(LoggerInterface::class),
×
NEW
220
                                                                        $config->get('api-platform.defaults', []),
×
NEW
221
                                                                        $config->get('api-platform.graphql.enabled'),
×
NEW
222
                                                                    ),
×
NEW
223
                                                                    $app->make(LoggerInterface::class),
×
NEW
224
                                                                    $config->get('api-platform.defaults', []),
×
NEW
225
                                                                    $config->get('api-platform.graphql.enabled'),
×
NEW
226
                                                                ),
×
NEW
227
                                                            )
×
NEW
228
                                                        ),
×
NEW
229
                                                        $config->get('api-platform.graphql.enabled')
×
NEW
230
                                                    )
×
NEW
231
                                                )
×
NEW
232
                                            )
×
NEW
233
                                        ),
×
NEW
234
                                        $formats,
×
NEW
235
                                        $config->get('api-platform.patch_formats'),
×
NEW
236
                                    )
×
NEW
237
                                )
×
NEW
238
                            ),
×
NEW
239
                            $app->make('filters'),
×
NEW
240
                            $app->make(CamelCaseToSnakeCaseNameConverter::class),
×
NEW
241
                            $this->app->make(LoggerInterface::class)
×
NEW
242
                        ),
×
NEW
243
                        $app->make('filters')
×
NEW
244
                    )
×
NEW
245
                ),
×
NEW
246
                true === $config->get('app.debug') ? 'array' : $config->get('api-platform.cache', 'file')
×
NEW
247
            );
×
NEW
248
        });
×
249

NEW
250
        if (interface_exists(FieldsBuilderEnumInterface::class)) {
×
NEW
251
            $this->registerGraphQl();
×
252
        }
253
    }
254

255
    private function registerGraphQl(): void
256
    {
NEW
257
        $this->app->singleton('api_platform.graphql.state_provider.parameter', function (Application $app) {
×
NEW
258
            $tagged = iterator_to_array($app->tagged(ParameterProviderInterface::class));
×
NEW
259
            $tagged['api_platform.serializer.filter_parameter_provider'] = $app->make(SerializerFilterParameterProvider::class);
×
260

NEW
261
            return new ParameterProvider(
×
NEW
262
                new ParameterValidatorProvider(
×
NEW
263
                    new SecurityParameterProvider(
×
NEW
264
                        $app->make(GraphQlDenormalizeProvider::class),
×
NEW
265
                        $app->make(ResourceAccessCheckerInterface::class)
×
NEW
266
                    ),
×
NEW
267
                ),
×
NEW
268
                new ServiceLocator($tagged)
×
NEW
269
            );
×
NEW
270
        });
×
271

NEW
272
        $this->app->singleton(FieldsBuilderEnumInterface::class, function (Application $app) {
×
273
            /** @var ConfigRepository */
NEW
274
            $config = $app['config'];
×
275

NEW
276
            return new FieldsBuilder(
×
NEW
277
                $app->make(PropertyNameCollectionFactoryInterface::class),
×
NEW
278
                $app->make(PropertyMetadataFactoryInterface::class),
×
NEW
279
                $app->make(ResourceMetadataCollectionFactoryInterface::class),
×
NEW
280
                $app->make(ResourceClassResolverInterface::class),
×
NEW
281
                $app->make(TypesContainerInterface::class),
×
NEW
282
                $app->make(ContextAwareTypeBuilderInterface::class),
×
NEW
283
                $app->make(TypeConverterInterface::class),
×
NEW
284
                $app->make(ResolverFactoryInterface::class),
×
NEW
285
                $app->make('filters'),
×
NEW
286
                $app->make(Pagination::class),
×
NEW
287
                $app->make(NameConverterInterface::class),
×
NEW
288
                $config->get('api-platform.graphql.nesting_separator') ?? '__',
×
NEW
289
                $app->make(InflectorInterface::class)
×
NEW
290
            );
×
NEW
291
        });
×
292
    }
293

294
    /**
295
     * @param array<class-string, \ReflectionClass> $classes
296
     * @param class-string                          $interface
297
     * @param array<int, class-string>              $apiPlatformProviders
298
     */
299
    private function autoconfigure(array $classes, string $interface, array $apiPlatformProviders): void
300
    {
NEW
301
        $m = $apiPlatformProviders;
×
NEW
302
        foreach ($classes as $className => $refl) {
×
NEW
303
            if ($refl->implementsInterface($interface)) {
×
NEW
304
                $m[] = $className;
×
305
            }
306
        }
307

NEW
308
        if ($m) {
×
NEW
309
            $this->app->tag($m, $interface);
×
310
        }
311
    }
312

313
    /**
314
     * Get the services provided by the provider.
315
     *
316
     * @return array<int, string>
317
     */
318
    public function provides(): array
319
    {
NEW
320
        return [
×
NEW
321
            CallableProvider::class,
×
NEW
322
            CallableProcessor::class,
×
NEW
323
            ItemProvider::class,
×
NEW
324
            CollectionProvider::class,
×
NEW
325
            SerializerFilterParameterProvider::class,
×
NEW
326
            ParameterProvider::class,
×
NEW
327
            FilterQueryExtension::class,
×
NEW
328
            'filters',
×
NEW
329
            ResourceMetadataCollectionFactoryInterface::class,
×
NEW
330
            'api_platform.graphql.state_provider.parameter',
×
NEW
331
            FieldsBuilderEnumInterface::class,
×
NEW
332
        ];
×
333
    }
334
}
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