Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.13% |
105 / 107 |
|
71.43% |
5 / 7 |
CRAP | |
0.00% |
0 / 1 |
DomainToStorageConverter | |
98.13% |
105 / 107 |
|
71.43% |
5 / 7 |
15 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
createTypeConverter | |
100.00% |
29 / 29 |
|
100.00% |
1 / 1 |
1 | |||
injectExistingDomainObject | |
95.65% |
22 / 23 |
|
0.00% |
0 / 1 |
5 | |||
createDomainObject | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
createStorageObject | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
injectExistingStorageObject | |
95.65% |
22 / 23 |
|
0.00% |
0 / 1 |
5 | |||
create | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | namespace Apie\StorageMetadata; |
3 | |
4 | use Apie\Core\FileStorage\ChainedFileStorage; |
5 | use Apie\Core\Indexing\Indexer; |
6 | use Apie\Core\TypeConverters\ArrayToDoctrineCollection; |
7 | use Apie\Core\TypeConverters\DoctrineCollectionToArray; |
8 | use Apie\StorageMetadata\ClassInstantiators\ChainedClassInstantiator; |
9 | use Apie\StorageMetadata\ClassInstantiators\FromReflection; |
10 | use Apie\StorageMetadata\ClassInstantiators\FromStorage; |
11 | use Apie\StorageMetadata\ClassInstantiators\FromStoredFile; |
12 | use Apie\StorageMetadata\Converters\ApieListToArray; |
13 | use Apie\StorageMetadata\Converters\ArrayToItemHashmap; |
14 | use Apie\StorageMetadata\Converters\ArrayToItemList; |
15 | use Apie\StorageMetadata\Converters\ArrayToItemSet; |
16 | use Apie\StorageMetadata\Converters\AutoIncrementTableToInt; |
17 | use Apie\StorageMetadata\Converters\AutoIncrementTableToValueObject; |
18 | use Apie\StorageMetadata\Converters\DateTimeToString; |
19 | use Apie\StorageMetadata\Converters\EnumToString; |
20 | use Apie\StorageMetadata\Converters\IntToAutoIncrementTable; |
21 | use Apie\StorageMetadata\Converters\IntToValueObject; |
22 | use Apie\StorageMetadata\Converters\MixedStorageToObject; |
23 | use Apie\StorageMetadata\Converters\MixedToMixedStorage; |
24 | use Apie\StorageMetadata\Converters\StringToDateTime; |
25 | use Apie\StorageMetadata\Converters\StringToEnum; |
26 | use Apie\StorageMetadata\Converters\StringToSearchIndex; |
27 | use Apie\StorageMetadata\Converters\StringToUploadedFileInterface; |
28 | use Apie\StorageMetadata\Converters\StringToValueObject; |
29 | use Apie\StorageMetadata\Converters\UploadedFileInterfaceToString; |
30 | use Apie\StorageMetadata\Converters\ValueObjectToAutoIncrementTable; |
31 | use Apie\StorageMetadata\Converters\ValueObjectToFloat; |
32 | use Apie\StorageMetadata\Converters\ValueObjectToInt; |
33 | use Apie\StorageMetadata\Converters\ValueObjectToString; |
34 | use Apie\StorageMetadata\Interfaces\ClassInstantiatorInterface; |
35 | use Apie\StorageMetadata\Interfaces\PropertyConverterInterface; |
36 | use Apie\StorageMetadata\Interfaces\StorageDtoInterface; |
37 | use Apie\StorageMetadata\Mediators\DomainToStorageContext; |
38 | use Apie\StorageMetadata\PropertyConverters\AccessControlListAttributeConverter; |
39 | use Apie\StorageMetadata\PropertyConverters\DefaultValueAttributeConverter; |
40 | use Apie\StorageMetadata\PropertyConverters\DiscriminatorMappingAttributeConverter; |
41 | use Apie\StorageMetadata\PropertyConverters\GetSearchIndexAttributeConverter; |
42 | use Apie\StorageMetadata\PropertyConverters\ManyToOneAttributeConverter; |
43 | use Apie\StorageMetadata\PropertyConverters\MethodAttributeConverter; |
44 | use Apie\StorageMetadata\PropertyConverters\OneToManyAttributeConverter; |
45 | use Apie\StorageMetadata\PropertyConverters\OneToOneAttributeConverter; |
46 | use Apie\StorageMetadata\PropertyConverters\OrderAttributeConverter; |
47 | use Apie\StorageMetadata\PropertyConverters\ParentAttributeConverter; |
48 | use Apie\StorageMetadata\PropertyConverters\PropertyAttributeConverter; |
49 | use Apie\StorageMetadata\PropertyConverters\StorageMappingAttributeConverter; |
50 | use Apie\TypeConverter\Converters\ObjectToObjectConverter; |
51 | use Apie\TypeConverter\DefaultConvertersFactory; |
52 | use Apie\TypeConverter\TypeConverter; |
53 | use ReflectionClass; |
54 | use ReflectionProperty; |
55 | |
56 | class DomainToStorageConverter |
57 | { |
58 | /** @var array<int, PropertyConverterInterface> */ |
59 | private array $propertyConverters; |
60 | |
61 | public function __construct( |
62 | private readonly ClassInstantiatorInterface $classInstantiator, |
63 | private readonly ChainedFileStorage $fileStorage, |
64 | PropertyConverterInterface... $propertyConverters |
65 | ) { |
66 | $this->propertyConverters = $propertyConverters; |
67 | } |
68 | |
69 | private function createTypeConverter(): TypeConverter |
70 | { |
71 | return new TypeConverter( |
72 | new ObjectToObjectConverter(), |
73 | ...DefaultConvertersFactory::create( |
74 | new StringToUploadedFileInterface($this->fileStorage), |
75 | new UploadedFileInterfaceToString($this->fileStorage), |
76 | new ArrayToDoctrineCollection(), |
77 | new StringToSearchIndex(), |
78 | new DoctrineCollectionToArray(), |
79 | new ApieListToArray(), |
80 | new AutoIncrementTableToInt(), |
81 | new AutoIncrementTableToValueObject(), |
82 | new IntToAutoIncrementTable(), |
83 | new ValueObjectToAutoIncrementTable(), |
84 | new ValueObjectToInt(), |
85 | new IntToValueObject(), |
86 | new ValueObjectToFloat(), |
87 | new MixedStorageToObject(), |
88 | new MixedToMixedStorage(), |
89 | new ValueObjectToString(), |
90 | new EnumToString(), |
91 | new StringToDateTime(), |
92 | new DateTimeToString(), |
93 | new StringToValueObject(), |
94 | new StringToEnum(), |
95 | new ArrayToItemHashmap(), |
96 | new ArrayToItemList(), |
97 | new ArrayToItemSet(), |
98 | ) |
99 | ); |
100 | } |
101 | |
102 | /** |
103 | * @template T of object |
104 | * @param T $domainObject |
105 | * @return T |
106 | */ |
107 | public function injectExistingDomainObject( |
108 | object $domainObject, |
109 | StorageDtoInterface $storageObject, |
110 | ?DomainToStorageContext $context = null |
111 | ): object { |
112 | $domainClass = $storageObject::getClassReference(); |
113 | $typeConverter = $this->createTypeConverter(); |
114 | $context = DomainToStorageContext::createFromContext( |
115 | $this, |
116 | $typeConverter, |
117 | $storageObject, |
118 | $domainObject, |
119 | $this->fileStorage, |
120 | $domainClass, |
121 | $context |
122 | ); |
123 | $ptr = new ReflectionClass($storageObject); |
124 | $filters = null; |
125 | while ($ptr) { |
126 | foreach ($ptr->getProperties($filters) as $storageProperty) { |
127 | if ($storageProperty->isStatic()) { |
128 | continue; |
129 | } |
130 | $propertyContext = $context->withStorageProperty($storageProperty); |
131 | foreach ($this->propertyConverters as $propertyConverter) { |
132 | $propertyConverter->applyToDomain($propertyContext); |
133 | } |
134 | } |
135 | $ptr = $ptr->getParentClass(); |
136 | // parent classes only add private properties |
137 | $filters = ReflectionProperty::IS_PRIVATE; |
138 | } |
139 | |
140 | return $domainObject; |
141 | } |
142 | |
143 | public function createDomainObject(StorageDtoInterface $storageObject, ?DomainToStorageContext $context = null): object |
144 | { |
145 | $domainClass = $storageObject::getClassReference(); |
146 | |
147 | return $this->injectExistingDomainObject( |
148 | $this->classInstantiator->create($domainClass, $storageObject), |
149 | $storageObject, |
150 | $context |
151 | ); |
152 | } |
153 | |
154 | /** |
155 | * @template T of StorageDtoInterface |
156 | * @param ReflectionClass<T> $targetClass |
157 | * @return T |
158 | */ |
159 | public function createStorageObject( |
160 | object $input, |
161 | ReflectionClass $targetClass, |
162 | ?DomainToStorageContext $context = null |
163 | ): StorageDtoInterface { |
164 | return $this->injectExistingStorageObject( |
165 | $input, |
166 | $this->classInstantiator->create($targetClass), |
167 | $context |
168 | ); |
169 | } |
170 | |
171 | public function injectExistingStorageObject( |
172 | object $domainObject, |
173 | StorageDtoInterface $storageObject, |
174 | ?DomainToStorageContext $context = null |
175 | ): StorageDtoInterface { |
176 | $domainClass = $storageObject::getClassReference(); |
177 | $filters = null; |
178 | $ptr = new ReflectionClass($storageObject); |
179 | $typeConverter = $this->createTypeConverter(); |
180 | $context = DomainToStorageContext::createFromContext( |
181 | $this, |
182 | $typeConverter, |
183 | $storageObject, |
184 | $domainObject, |
185 | $this->fileStorage, |
186 | $domainClass, |
187 | $context |
188 | ); |
189 | while ($ptr) { |
190 | foreach ($ptr->getProperties($filters) as $storageProperty) { |
191 | if ($storageProperty->isStatic()) { |
192 | continue; |
193 | } |
194 | $propertyContext = $context->withStorageProperty($storageProperty); |
195 | foreach ($this->propertyConverters as $propertyConverter) { |
196 | $propertyConverter->applyToStorage($propertyContext); |
197 | } |
198 | } |
199 | $ptr = $ptr->getParentClass(); |
200 | // parent classes only add private properties |
201 | $filters = ReflectionProperty::IS_PRIVATE; |
202 | } |
203 | return $storageObject; |
204 | } |
205 | |
206 | public static function create(ChainedFileStorage $fileStorage, ?Indexer $indexer = null): self |
207 | { |
208 | return new self( |
209 | new ChainedClassInstantiator( |
210 | new FromStoredFile(), |
211 | new FromStorage(), |
212 | new FromReflection(), |
213 | ), |
214 | $fileStorage, |
215 | new DiscriminatorMappingAttributeConverter(), |
216 | new StorageMappingAttributeConverter(), |
217 | new ManyToOneAttributeConverter(), |
218 | new OneToOneAttributeConverter(), |
219 | new AccessControlListAttributeConverter(), |
220 | new OneToManyAttributeConverter(), |
221 | new PropertyAttributeConverter(), |
222 | new GetSearchIndexAttributeConverter($indexer ?? Indexer::create()), |
223 | new MethodAttributeConverter(), |
224 | new OrderAttributeConverter(), |
225 | new ParentAttributeConverter(), |
226 | new DefaultValueAttributeConverter(), |
227 | ); |
228 | } |
229 | } |