Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
PolymorphicEntityStrategy
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
6 / 6
28
100.00% covered (success)
100.00%
1 / 1
 supports
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModificationMetadata
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
7
 getCreationMetadata
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
8
 getResultMetadata
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
8
 mergeChildClass
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2namespace Apie\Core\Metadata\Strategy;
3
4use Apie\Core\Context\ApieContext;
5use Apie\Core\Context\MetadataFieldHashmap;
6use Apie\Core\Entities\PolymorphicEntityInterface;
7use Apie\Core\Metadata\CompositeMetadata;
8use Apie\Core\Metadata\Fields\DiscriminatorColumn;
9use Apie\Core\Metadata\Fields\FieldInterface;
10use Apie\Core\Metadata\Fields\OptionalField;
11use Apie\Core\Metadata\Fields\StaticDiscriminatorColumn;
12use Apie\Core\Metadata\StrategyInterface;
13use Apie\Core\Other\DiscriminatorConfig;
14use Apie\Core\Other\DiscriminatorMapping;
15use Apie\Core\Utils\EntityUtils;
16use ReflectionClass;
17
18final class PolymorphicEntityStrategy implements StrategyInterface
19{
20    public static function supports(ReflectionClass $class): bool
21    {
22        return EntityUtils::isPolymorphicEntity($class);
23    }
24
25    /**
26     * @param ReflectionClass<PolymorphicEntityInterface> $class
27     */
28    public function __construct(private ReflectionClass $class)
29    {
30    }
31
32    public function getModificationMetadata(ApieContext $context): CompositeMetadata
33    {
34        $list = [];
35        
36        $class = $this->class;
37
38        while ($class) {
39            $method = $class->getMethod('getDiscriminatorMapping');
40            if (!$method->isAbstract() && $method->getDeclaringClass()->name === $class->name) {
41                /** @var DiscriminatorMapping $mapping */
42                $mapping = $method->invoke(null);
43                foreach ($mapping->getConfigs() as $config) {
44                    if ($method->getDeclaringClass()->name === $this->class->name || $config->getClassName() === $this->class->name) {
45                        $this->mergeChildClass($context, $config, $list, 'getModificationMetadata');
46                    }
47                }
48            }
49            $class = $class->getParentClass();
50        }
51
52        return new CompositeMetadata(new MetadataFieldHashmap($list), $this->class);
53    }
54
55    public function getCreationMetadata(ApieContext $context): CompositeMetadata
56    {
57        /** @var array<string, FieldInterface> $list */
58        $list = [];
59        
60        $class = $this->class;
61
62        while ($class) {
63            $method = $class->getMethod('getDiscriminatorMapping');
64            if (!$method->isAbstract() && $method->getDeclaringClass()->name === $class->name) {
65                /** @var DiscriminatorMapping $mapping */
66                $mapping = $method->invoke(null);
67                $list[$mapping->getPropertyName()] = new DiscriminatorColumn($mapping);
68                foreach ($mapping->getConfigs() as $config) {
69                    if ($method->getDeclaringClass()->name === $this->class->name || $config->getClassName() === $this->class->name) {
70                        $this->mergeChildClass($context, $config, $list, 'getCreationMetadata');
71                    }
72                }
73            }
74            $class = $class->getParentClass();
75        }
76        foreach (EntityUtils::getDiscriminatorValues($this->class) as $propertyName => $discriminatorValue) {
77            $list[$propertyName] = new StaticDiscriminatorColumn($discriminatorValue);
78        }
79
80        return new CompositeMetadata(new MetadataFieldHashmap($list), $this->class);
81    }
82
83    public function getResultMetadata(ApieContext $context): CompositeMetadata
84    {
85        /** @var array<string, FieldInterface> $list */
86        $list = [];
87        
88        $class = $this->class;
89
90        while ($class) {
91            $method = $class->getMethod('getDiscriminatorMapping');
92            if (!$method->isAbstract() && $method->getDeclaringClass()->name === $class->name) {
93                /** @var DiscriminatorMapping $mapping */
94                $mapping = $method->invoke(null);
95                $list[$mapping->getPropertyName()] = new DiscriminatorColumn($mapping);
96                foreach ($mapping->getConfigs() as $config) {
97                    if ($method->getDeclaringClass()->name === $this->class->name || $config->getClassName() === $this->class->name) {
98                        $this->mergeChildClass($context, $config, $list, 'getResultMetadata');
99                    }
100                }
101            }
102            $class = $class->getParentClass();
103        }
104        foreach (EntityUtils::getDiscriminatorValues($this->class) as $propertyName => $discriminatorValue) {
105            $list[$propertyName] = new StaticDiscriminatorColumn($discriminatorValue);
106        }
107
108        return new CompositeMetadata(new MetadataFieldHashmap($list), $this->class);
109    }
110
111    /**
112     * @param array<string, FieldInterface> $list
113     */
114    private function mergeChildClass(
115        ApieContext $context,
116        DiscriminatorConfig $config,
117        array& $list,
118        string $method
119    ): void {
120        $refl = new ReflectionClass($config->getClassName());
121        $tmp = new RegularObjectStrategy($refl);
122        $mapping = $tmp->$method($context);
123        foreach ($mapping->getHashmap() as $propertyName => $declaration) {
124            /** @var string $propertyName */
125            if (isset($list[$propertyName])) {
126                $list[$propertyName] = new OptionalField($declaration, $list[$propertyName]);
127            } else {
128                $list[$propertyName] = new OptionalField($declaration);
129            }
130        }
131    }
132}