Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
81 / 81 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
PolymorphicEntitySchemaProvider | |
100.00% |
81 / 81 |
|
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% |
35 / 35 |
|
100.00% |
1 / 1 |
3 | |||
addCreationSchemaFor | |
100.00% |
35 / 35 |
|
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 | foreach ($discriminatorMapping->getConfigs() as $config) { |
53 | $key = $config->getDiscriminator(); |
54 | $value = $componentsBuilder->addDisplaySchemaFor($config->getClassName(), $discriminatorMapping->getPropertyName(), nullable: $nullable); |
55 | assert($value instanceof Reference); |
56 | $relations[$key] = $value; |
57 | $schema = $componentsBuilder->getSchemaForReference($value); |
58 | if ($schema) { |
59 | $this->fillInDiscriminator($schema, $discriminatorMapping->getPropertyName(), $config->getDiscriminator()); |
60 | } |
61 | } |
62 | $schema = new Schema([ |
63 | 'type' => 'object', |
64 | 'oneOf' => array_values($relations), |
65 | 'discriminator' => new Discriminator([ |
66 | 'propertyName' => $discriminatorMapping->getPropertyName(), |
67 | 'mapping' => array_map( |
68 | function (Reference $ref) { |
69 | return $ref->getReference(); |
70 | }, |
71 | $relations |
72 | ), |
73 | ]), |
74 | ]); |
75 | ComponentsBuilder::addDescriptionOfObject($schema, $class); |
76 | MetadataSchemaProvider::applyPropertiesToSchema( |
77 | $schema, |
78 | $componentsBuilder, |
79 | MetadataFactory::getResultMetadata($class, new ApieContext()), |
80 | true, |
81 | $nullable |
82 | ); |
83 | |
84 | $schema->required = [$discriminatorMapping->getPropertyName()]; |
85 | |
86 | $componentsBuilder->setSchema($componentIdentifier, $schema); |
87 | return $componentsBuilder->getComponents(); |
88 | } |
89 | |
90 | public function addCreationSchemaFor( |
91 | ComponentsBuilder $componentsBuilder, |
92 | string $componentIdentifier, |
93 | ReflectionClass $class, |
94 | bool $nullable = false |
95 | ): Components { |
96 | $relations = []; |
97 | $method = $class->getMethod('getDiscriminatorMapping'); |
98 | /** @var DiscriminatorMapping */ |
99 | $discriminatorMapping = $method->invoke(null); |
100 | foreach ($discriminatorMapping->getConfigs() as $config) { |
101 | $key = $config->getDiscriminator(); |
102 | $value = $componentsBuilder->addCreationSchemaFor($config->getClassName(), $discriminatorMapping->getPropertyName()); |
103 | assert($value instanceof Reference); |
104 | $relations[$key] = $value; |
105 | $schema = $componentsBuilder->getSchemaForReference($value); |
106 | if ($schema) { |
107 | $this->fillInDiscriminator($schema, $discriminatorMapping->getPropertyName(), $config->getDiscriminator()); |
108 | } |
109 | } |
110 | $schema = new Schema([ |
111 | 'type' => 'object', |
112 | 'oneOf' => array_values($relations), |
113 | 'discriminator' => new Discriminator([ |
114 | 'propertyName' => $discriminatorMapping->getPropertyName(), |
115 | 'mapping' => array_map( |
116 | function (Reference $ref) { |
117 | return $ref->getReference(); |
118 | }, |
119 | $relations |
120 | ), |
121 | ]), |
122 | ]); |
123 | ComponentsBuilder::addDescriptionOfObject($schema, $class); |
124 | MetadataSchemaProvider::applyPropertiesToSchema( |
125 | $schema, |
126 | $componentsBuilder, |
127 | MetadataFactory::getCreationMetadata($class, new ApieContext()), |
128 | false, |
129 | $nullable |
130 | ); |
131 | $schema->required = [$discriminatorMapping->getPropertyName()]; |
132 | |
133 | $componentsBuilder->setSchema($componentIdentifier, $schema); |
134 | return $componentsBuilder->getComponents(); |
135 | } |
136 | } |