Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.57% covered (warning)
86.57%
58 / 67
72.73% covered (warning)
72.73%
8 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
UnionTypeMetadata
86.57% covered (warning)
86.57%
58 / 67
72.73% covered (warning)
72.73%
8 / 11
39.14
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDisplayName
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 toClass
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
5.01
 toSkipNull
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 getTypes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHashmap
57.14% covered (warning)
57.14%
4 / 7
0.00% covered (danger)
0.00%
0 / 1
5.26
 getRequiredFields
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 allowsNull
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 toScalarType
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 getArrayItemType
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 getValueOptions
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2namespace Apie\Core\Metadata;
3
4use Apie\Core\Context\ApieContext;
5use Apie\Core\Context\MetadataFieldHashmap;
6use Apie\Core\Enums\ScalarType;
7use Apie\Core\Lists\StringList;
8use Apie\Core\Lists\ValueOptionList;
9use Apie\Core\Metadata\Fields\OptionalField;
10use ReflectionClass;
11
12class UnionTypeMetadata implements NullableMetadataInterface
13{
14    /**
15     * @var MetadataInterface[] $metadata
16     */
17    private array $metadata;
18
19    public function __construct(MetadataInterface... $metadata)
20    {
21        $this->metadata = $metadata;
22    }
23
24    public function getDisplayName(): string
25    {
26        return implode(
27            '|',
28            array_map(
29                fn (MetadataInterface $meta) => $meta->getDisplayName(),
30                $this->metadata
31            )
32        );
33    }
34
35    public function toClass(): ?ReflectionClass
36    {
37        $first = true;
38        $class = null;
39        foreach ($this->metadata as $metadata) {
40            $current = $metadata->toClass();
41            if (null === $current) {
42                return null;
43            }
44            if ($first) {
45                $class = $metadata->toClass();
46                $first = false;
47            }
48            if ($current->name !== $class->name) {
49                return null;
50            }
51        }
52        return null;
53    }
54
55    public function toSkipNull(): MetadataInterface
56    {
57        $metadata = [];
58        foreach ($this->metadata as $submetadata) {
59            if ($submetadata instanceof ScalarMetadata && $submetadata->toScalarType() === ScalarType::NULLVALUE) {
60                continue;
61            }
62            $metadata[] = $submetadata;
63        }
64        if (count($metadata) === 1) {
65            return $metadata[0];
66        }
67        return new UnionTypeMetadata(...$metadata);
68    }
69
70    /**
71     * @return MetadataInterface[]
72     */
73    public function getTypes(): array
74    {
75        return $this->metadata;
76    }
77
78    public function getHashmap(): MetadataFieldHashmap
79    {
80        $map = [];
81        foreach ($this->metadata as $objectData) {
82            foreach ($objectData->getHashmap() as $key => $value) {
83                if (isset($map[$key])) {
84                    $value = new OptionalField($value, $map[$key]);
85                }
86                $map[$key] = $value;
87            }
88        }
89        return new MetadataFieldHashmap($map);
90    }
91    
92    public function getRequiredFields(): StringList
93    {
94        $requiredFields = [];
95        foreach ($this->metadata as $objectData) {
96            $required = $objectData->getRequiredFields()->toArray();
97            $requiredFields[] = array_combine($required, $required);
98        }
99        return new StringList($requiredFields ? array_intersect_key(...$requiredFields) : []);
100    }
101
102    public function allowsNull(): bool
103    {
104        foreach ($this->metadata as $objectData) {
105            if ($objectData->toScalarType() === ScalarType::NULLVALUE) {
106                return true;
107            }
108        }
109        return false;
110    }
111
112    public function toScalarType(bool $ignoreNull = false): ScalarType
113    {
114        $current = null;
115        foreach ($this->metadata as $objectData) {
116            $type = $objectData->toScalarType($ignoreNull);
117            if ($ignoreNull && $type === ScalarType::NULLVALUE) {
118                continue;
119            }
120            if ($current === null) {
121                $current = $type;
122            } elseif ($type !== $current) {
123                return ScalarType::MIXED;
124            }
125        }
126        return $current ?? ScalarType::MIXED;
127    }
128
129    public function getArrayItemType(): ?MetadataInterface
130    {
131        foreach ($this->metadata as $objectData) {
132            $arrayPrototype = $objectData->getArrayItemType();
133            if (!isset($arrayType) || $arrayType === $arrayPrototype) {
134                $arrayType = $arrayPrototype;
135            }
136        }
137        return $arrayType ?? null;
138    }
139
140    public function getValueOptions(ApieContext $context, bool $runtimeFilter = false): ?ValueOptionList
141    {
142        $result = [];
143        foreach ($this->metadata as $objectData) {
144            $valueOptions = $objectData->getValueOptions($context, $runtimeFilter);
145            if ($valueOptions === null) {
146                return null;
147            }
148            $result = [...$result, ...$valueOptions];
149        }
150        return new ValueOptionList($result);
151    }
152}