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

api-platform / core / 17054099799

18 Aug 2025 10:28PM UTC coverage: 22.386% (-0.2%) from 22.612%
17054099799

Pull #7150

github

web-flow
Merge 973c71211 into 2d501b315
Pull Request #7150: fix: array shape in ProviderInterface

11062 of 49414 relevant lines covered (22.39%)

11.75 hits per line

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

0.0
/src/Laravel/Eloquent/Metadata/Factory/Property/EloquentPropertyMetadataFactory.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\Eloquent\Metadata\Factory\Property;
15

16
use ApiPlatform\Laravel\Eloquent\Metadata\ModelMetadata;
17
use ApiPlatform\Metadata\ApiProperty;
18
use ApiPlatform\Metadata\Exception\PropertyNotFoundException;
19
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
20
use Illuminate\Database\Eloquent\Model;
21
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
22
use Illuminate\Database\Eloquent\Relations\HasMany;
23
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
24
use Illuminate\Database\Eloquent\Relations\MorphMany;
25
use Illuminate\Database\Eloquent\Relations\MorphToMany;
26
use Illuminate\Support\Collection;
27
use Symfony\Component\PropertyInfo\Type;
28

29
/**
30
 * Uses Eloquent metadata to populate the identifier property.
31
 *
32
 * @author Kévin Dunglas <dunglas@gmail.com>
33
 */
34
final class EloquentPropertyMetadataFactory implements PropertyMetadataFactoryInterface
35
{
36
    public function __construct(
37
        private readonly ModelMetadata $modelMetadata,
38
        private readonly ?PropertyMetadataFactoryInterface $decorated = null,
39
    ) {
40
    }
×
41

42
    /**
43
     * {@inheritdoc}
44
     *
45
     * @param class-string $resourceClass
46
     */
47
    public function create(string $resourceClass, string $property, array $options = []): ApiProperty
48
    {
49
        if (!is_a($resourceClass, Model::class, true)) {
×
50
            return $this->decorated?->create($resourceClass, $property, $options) ?? new ApiProperty();
×
51
        }
52

53
        try {
54
            $refl = new \ReflectionClass($resourceClass);
×
55
            $model = $refl->newInstanceWithoutConstructor();
×
56
        } catch (\ReflectionException) {
×
57
            return $this->decorated?->create($resourceClass, $property, $options) ?? new ApiProperty();
×
58
        }
59

60
        try {
61
            $propertyMetadata = $this->decorated?->create($resourceClass, $property, $options) ?? new ApiProperty();
×
62
        } catch (PropertyNotFoundException) {
×
63
            $propertyMetadata = new ApiProperty();
×
64
        }
65

66
        if ($model->getKeyName() === $property) {
×
67
            $propertyMetadata = $propertyMetadata->withIdentifier(true);
×
68
        }
69

70
        foreach ($this->modelMetadata->getAttributes($model) as $p) {
×
71
            if ($p['name'] !== $property) {
×
72
                continue;
×
73
            }
74

75
            // see https://laravel.com/docs/11.x/eloquent-mutators#attribute-casting
76
            $builtinType = $p['cast'] ?? $p['type'];
×
77
            $type = match ($builtinType) {
×
78
                'integer' => new Type(Type::BUILTIN_TYPE_INT, $p['nullable']),
×
79
                'double', 'real' => new Type(Type::BUILTIN_TYPE_FLOAT, $p['nullable']),
×
80
                'boolean', 'bool' => new Type(Type::BUILTIN_TYPE_BOOL, $p['nullable']),
×
81
                'datetime', 'date', 'timestamp' => new Type(Type::BUILTIN_TYPE_OBJECT, $p['nullable'], \DateTime::class),
×
82
                'immutable_datetime', 'immutable_date' => new Type(Type::BUILTIN_TYPE_OBJECT, $p['nullable'], \DateTimeImmutable::class),
×
83
                'collection', 'encrypted:collection' => new Type(Type::BUILTIN_TYPE_ITERABLE, $p['nullable'], Collection::class, true),
×
84
                'encrypted:array' => new Type(Type::BUILTIN_TYPE_ARRAY, $p['nullable']),
×
85
                'encrypted:object' => new Type(Type::BUILTIN_TYPE_OBJECT, $p['nullable']),
×
86
                default => new Type(\in_array($builtinType, Type::$builtinTypes, true) ? $builtinType : Type::BUILTIN_TYPE_STRING, $p['nullable'] ?? true),
×
87
            };
×
88

89
            $propertyMetadata = $propertyMetadata
×
90
                ->withBuiltinTypes([$type]);
×
91

92
            return $propertyMetadata;
×
93
        }
94

95
        foreach ($this->modelMetadata->getRelations($model) as $relation) {
×
96
            if ($relation['name'] !== $property) {
×
97
                continue;
×
98
            }
99

100
            $collection = match ($relation['type']) {
×
101
                HasMany::class,
×
102
                HasManyThrough::class,
×
103
                BelongsToMany::class,
×
104
                MorphMany::class,
×
105
                MorphToMany::class => true,
×
106
                default => false,
×
107
            };
×
108

109
            $type = new Type($collection ? Type::BUILTIN_TYPE_ITERABLE : Type::BUILTIN_TYPE_OBJECT, false, $relation['related'], $collection, collectionValueType: new Type(Type::BUILTIN_TYPE_OBJECT, false, $relation['related']));
×
110

111
            return $propertyMetadata
×
112
                ->withBuiltinTypes([$type])
×
113
                ->withExtraProperties(['eloquent_relation' => $relation] + $propertyMetadata->getExtraProperties());
×
114
        }
115

116
        return $propertyMetadata;
×
117
    }
118
}
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