Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
99 / 99 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
PolymorphicEntitySchemaProvider | |
100.00% |
99 / 99 |
|
100.00% |
4 / 4 |
10 | |
100.00% |
1 / 1 |
supports | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
fillInDiscriminator | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
addDisplaySchemaFor | |
100.00% |
44 / 44 |
|
100.00% |
1 / 1 |
3 | |||
addCreationSchemaFor | |
100.00% |
44 / 44 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | namespace Apie\SchemaGenerator\SchemaProviders; |
3 | |
4 | use Apie\Core\Context\ApieContext; |
5 | use Apie\Core\Entities\PolymorphicEntityInterface; |
6 | use Apie\Core\Metadata\MetadataFactory; |
7 | use Apie\Core\Other\DiscriminatorMapping; |
8 | use Apie\SchemaGenerator\Builders\ComponentsBuilder; |
9 | use Apie\SchemaGenerator\Interfaces\SchemaProvider; |
10 | use cebe\openapi\spec\Components; |
11 | use cebe\openapi\spec\Discriminator; |
12 | use cebe\openapi\spec\Reference; |
13 | use cebe\openapi\spec\Schema; |
14 | use ReflectionClass; |
15 | |
16 | /** |
17 | * @implements SchemaProvider<PolymorphicEntityInterface> |
18 | */ |
19 | class PolymorphicEntitySchemaProvider implements SchemaProvider |
20 | { |
21 | public function supports(ReflectionClass $class): bool |
22 | { |
23 | if (!$class->implementsInterface(PolymorphicEntityInterface::class)) { |
24 | return false; |
25 | } |
26 | $method = $class->getMethod('getDiscriminatorMapping'); |
27 | return $method->getDeclaringClass()->name === $class->name && !$method->isAbstract(); |
28 | } |
29 | |
30 | private function fillInDiscriminator(Schema $schema, string $propertyName, string $propertyValue): void |
31 | { |
32 | $properties = $schema->properties ?? []; |
33 | $properties[$propertyName] = new Schema([ |
34 | 'type' => 'string', |
35 | 'enum' => [$propertyValue], |
36 | 'nullable' => false, |
37 | ]); |
38 | $schema->properties = $properties; |
39 | } |
40 | |
41 | public function addDisplaySchemaFor( |
42 | ComponentsBuilder $componentsBuilder, |
43 | string $componentIdentifier, |
44 | ReflectionClass $class, |
45 | bool $nullable = false |
46 | ): Components { |
47 | $relations = []; |
48 | $method = $class->getMethod('getDiscriminatorMapping'); |
49 | /** @var DiscriminatorMapping */ |
50 | $discriminatorMapping = $method->invoke(null); |
51 | |
52 | $mapping = []; |
53 | foreach ($discriminatorMapping->getConfigs() as $config) { |
54 | $key = $config->getDiscriminator(); |
55 | $value = $componentsBuilder->addDisplaySchemaFor($config->getClassName(), $discriminatorMapping->getPropertyName(), nullable: $nullable); |
56 | assert($value instanceof Reference); |
57 | $relations[$key] = $value; |
58 | $mapping[] = $config->getDiscriminator(); |
59 | $schema = $componentsBuilder->getSchemaForReference($value); |
60 | if ($schema) { |
61 | $this->fillInDiscriminator($schema, $discriminatorMapping->getPropertyName(), $config->getDiscriminator()); |
62 | } |
63 | } |
64 | $schema = new Schema([ |
65 | 'type' => 'object', |
66 | 'oneOf' => array_values($relations), |
67 | 'discriminator' => new Discriminator([ |
68 | 'propertyName' => $discriminatorMapping->getPropertyName(), |
69 | 'mapping' => array_map( |
70 | function (Reference $ref) { |
71 | return $ref->getReference(); |
72 | }, |
73 | $relations |
74 | ), |
75 | ]), |
76 | ]); |
77 | ComponentsBuilder::addDescriptionOfObject($schema, $class); |
78 | MetadataSchemaProvider::applyPropertiesToSchema( |
79 | $schema, |
80 | $componentsBuilder, |
81 | MetadataFactory::getResultMetadata($class, new ApieContext()), |
82 | true, |
83 | $nullable |
84 | ); |
85 | $properties = $schema->properties ?? []; |
86 | $properties[$discriminatorMapping->getPropertyName()] = new Schema([ |
87 | 'type' => 'string', |
88 | 'enum' => $mapping, |
89 | 'nullable' => false, |
90 | ]); |
91 | $schema->properties = $properties; |
92 | $schema->required = [$discriminatorMapping->getPropertyName()]; |
93 | |
94 | $componentsBuilder->setSchema($componentIdentifier, $schema); |
95 | return $componentsBuilder->getComponents(); |
96 | } |
97 | |
98 | public function addCreationSchemaFor( |
99 | ComponentsBuilder $componentsBuilder, |
100 | string $componentIdentifier, |
101 | ReflectionClass $class, |
102 | bool $nullable = false |
103 | ): Components { |
104 | |
105 | $relations = []; |
106 | $method = $class->getMethod('getDiscriminatorMapping'); |
107 | /** @var DiscriminatorMapping */ |
108 | $discriminatorMapping = $method->invoke(null); |
109 | $mapping = []; |
110 | foreach ($discriminatorMapping->getConfigs() as $config) { |
111 | $key = $config->getDiscriminator(); |
112 | $value = $componentsBuilder->addCreationSchemaFor($config->getClassName(), $discriminatorMapping->getPropertyName()); |
113 | assert($value instanceof Reference); |
114 | $relations[$key] = $value; |
115 | $mapping[] = $config->getDiscriminator(); |
116 | $schema = $componentsBuilder->getSchemaForReference($value); |
117 | if ($schema) { |
118 | $this->fillInDiscriminator($schema, $discriminatorMapping->getPropertyName(), $config->getDiscriminator()); |
119 | } |
120 | } |
121 | $schema = new Schema([ |
122 | 'type' => 'object', |
123 | 'oneOf' => array_values($relations), |
124 | 'discriminator' => new Discriminator([ |
125 | 'propertyName' => $discriminatorMapping->getPropertyName(), |
126 | 'mapping' => array_map( |
127 | function (Reference $ref) { |
128 | return $ref->getReference(); |
129 | }, |
130 | $relations |
131 | ), |
132 | ]), |
133 | ]); |
134 | ComponentsBuilder::addDescriptionOfObject($schema, $class); |
135 | MetadataSchemaProvider::applyPropertiesToSchema( |
136 | $schema, |
137 | $componentsBuilder, |
138 | MetadataFactory::getCreationMetadata($class, new ApieContext()), |
139 | false, |
140 | $nullable |
141 | ); |
142 | $properties = $schema->properties ?? []; |
143 | $properties[$discriminatorMapping->getPropertyName()] = new Schema([ |
144 | 'type' => 'string', |
145 | 'enum' => $mapping, |
146 | 'nullable' => false, |
147 | ]); |
148 | $schema->properties = $properties; |
149 | $schema->required = [$discriminatorMapping->getPropertyName()]; |
150 | |
151 | $componentsBuilder->setSchema($componentIdentifier, $schema); |
152 | return $componentsBuilder->getComponents(); |
153 | } |
154 | } |