GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 0d82f1...4042bf )
by joseph
20s queued 14s
created

DtoFactory::addNestedRequiredDtos()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6.4859

Importance

Changes 0
Metric Value
cc 6
eloc 16
nc 5
nop 1
dl 0
loc 21
ccs 16
cts 21
cp 0.7619
crap 6.4859
rs 9.1111
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace EdmondsCommerce\DoctrineStaticMeta\Entity\DataTransferObjects;
6
7
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
8
use EdmondsCommerce\DoctrineStaticMeta\DoctrineStaticMeta;
9
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Factories\UuidFactory;
10
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Interfaces\PrimaryKey\UuidPrimaryKeyInterface;
11
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\DataTransferObjectInterface;
12
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityData;
13
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface;
14
use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException;
15
use LogicException;
16
use ReflectionException;
17
use TypeError;
18
19
/**
20
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
21
 */
22
class DtoFactory implements DtoFactoryInterface
23
{
24
    /**
25
     * @var NamespaceHelper
26
     */
27
    private $namespaceHelper;
28
    /**
29
     * @var UuidFactory
30
     */
31
    private $uuidFactory;
32
    /**
33
     * @var array
34
     */
35
    private $createdDtos = [];
36
37 2
    public function __construct(
38
        NamespaceHelper $namespaceHelper,
39
        UuidFactory $uuidFactory
40
    ) {
41 2
        $this->namespaceHelper = $namespaceHelper;
42 2
        $this->uuidFactory     = $uuidFactory;
43 2
    }
44
45
    /**
46
     * Pass in the FQN for an entity and get an empty DTO, including nested empty DTOs for required relations
47
     *
48
     * @param string $entityFqn
49
     *
50
     * @return mixed
51
     */
52 2
    public function createEmptyDtoFromEntityFqn(string $entityFqn)
53
    {
54 2
        $dtoFqn = $this->namespaceHelper->getEntityDtoFqnFromEntityFqn($entityFqn);
55
56 2
        $dto = new $dtoFqn();
57 2
        $this->resetCreationTransaction();
58 2
        $this->createdDtos[$dtoFqn] = $dto;
59 2
        $this->setId($dto);
60 2
        $this->addRequiredItemsToDto($dto);
61 2
        $this->resetCreationTransaction();
62
63 2
        return $dto;
64
    }
65
66
    /**
67
     * When creating DTOs, we keep track of created DTOs. When you start creating a new DTO, you should call this first
68
     * and then call again after you have finished.
69
     *
70
     * This is handled for you in ::createEmptyDtoFromEntityFqn
71
     *
72
     * @return DtoFactory
73
     */
74 2
    public function resetCreationTransaction(): self
75
    {
76 2
        $this->createdDtos = [];
77
78 2
        return $this;
79
    }
80
81
    /**
82
     * If the Entity that the DTO represents has a settable and buildable UUID, then we should set that at the point of
83
     * creating a DTO for a new Entity instance
84
     *
85
     * @param DataTransferObjectInterface $dto
86
     */
87 2
    private function setId(DataTransferObjectInterface $dto): void
88
    {
89 2
        $entityFqn  = $dto::getEntityFqn();
90 2
        $reflection = $this->getDsmFromEntityFqn($entityFqn)
91 2
                           ->getReflectionClass();
92 2
        if ($reflection->implementsInterface(UuidPrimaryKeyInterface::class)) {
93 2
            $dto->setId($entityFqn::buildUuid($this->uuidFactory));
0 ignored issues
show
Bug introduced by
The method setId() does not exist on EdmondsCommerce\Doctrine...TransferObjectInterface. It seems like you code against a sub-type of EdmondsCommerce\Doctrine...TransferObjectInterface such as EdmondsCommerce\Doctrine...AbstractEntityUpdateDto or TemplateNamespace\Entity...jects\TemplateEntityDto. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

93
            $dto->/** @scrutinizer ignore-call */ 
94
                  setId($entityFqn::buildUuid($this->uuidFactory));
Loading history...
94
        }
95 2
    }
96
97
    /**
98
     * Get the instance of DoctrineStaticMeta from the Entity by FQN
99
     *
100
     * @param string $entityFqn
101
     *
102
     * @return DoctrineStaticMeta
103
     */
104 2
    private function getDsmFromEntityFqn(string $entityFqn): DoctrineStaticMeta
105
    {
106 2
        return $entityFqn::getDoctrineStaticMeta();
107
    }
108
109 2
    private function addRequiredItemsToDto(DataTransferObjectInterface $dto): void
110
    {
111 2
        $this->addNestedRequiredDtos($dto);
112 2
        $this->addRequiredEmbeddableObjectsToDto($dto);
113 2
    }
114
115
    /**
116
     * Take the DTO for a defined EntityFqn and then parse the required relations and create nested DTOs for them
117
     *
118
     * Checks if the required relation is already set and if so, does nothing
119
     *
120
     * @param DataTransferObjectInterface $dto
121
     *
122
     * @throws ReflectionException
123
     * @throws DoctrineStaticMetaException
124
     */
125 2
    private function addNestedRequiredDtos(DataTransferObjectInterface $dto): void
126
    {
127 2
        $entityFqn         = $dto::getEntityFqn();
128 2
        $dsm               = $this->getDsmFromEntityFqn($entityFqn);
129 2
        $requiredRelations = $dsm->getRequiredRelationProperties();
130 2
        foreach ($requiredRelations as $propertyName => $requiredRelation) {
131 2
            $entityInterfaceFqn = $requiredRelation->getRelationEntityFqn();
132 2
            $getter = 'get' . $propertyName;
133 2
            if ($requiredRelation->isPluralRelation()) {
134 2
                if ($dto->$getter()->count() > 0) {
135
                    continue;
136
                }
137 2
                $this->addNestedDtoToCollection($dto, $propertyName, $entityInterfaceFqn);
138 2
                continue;
139
            }
140 2
            $issetAsDtoMethod    = 'isset' . $propertyName . 'AsDto';
141 2
            $issetAsEntityMethod = 'isset' . $propertyName . 'AsEntity';
142 2
            if (true === $dto->$issetAsDtoMethod() || true === $dto->$issetAsEntityMethod()) {
143 2
                continue;
144
            }
145 2
            $this->addNestedDto($dto, $propertyName, $entityInterfaceFqn);
146
        }
147 2
    }
148
149
    /**
150
     * Create and add a related DTO into the owning DTO collection property
151
     *
152
     * @param DataTransferObjectInterface $dto
153
     * @param string                      $propertyName
154
     * @param string                      $entityInterfaceFqn
155
     *
156
     * @throws DoctrineStaticMetaException
157
     * @throws ReflectionException
158
     */
159 2
    private function addNestedDtoToCollection(
160
        DataTransferObjectInterface $dto,
161
        string $propertyName,
162
        string $entityInterfaceFqn
163
    ): void {
164 2
        $collectionGetter = 'get' . $propertyName;
165 2
        $dto->$collectionGetter()->add(
166 2
            $this->createDtoRelatedToDto(
167 2
                $dto,
168 2
                $this->namespaceHelper->getEntityFqnFromEntityInterfaceFqn($entityInterfaceFqn)
169
            )
170
        );
171 2
    }
172
173
    /**
174
     * Create a DTO with a preset relation to the owning Entity DTO and all other items filled with new objects
175
     *
176
     * @param DataTransferObjectInterface $owningDto
177
     * @param string                      $relatedEntityFqn
178
     *
179
     * @return DataTransferObjectInterface
180
     * @throws DoctrineStaticMetaException
181
     * @throws ReflectionException
182
     */
183 2
    public function createDtoRelatedToDto(
184
        DataTransferObjectInterface $owningDto,
185
        string $relatedEntityFqn
186
    ): DataTransferObjectInterface {
187 2
        $this->resetCreationTransaction();
188
189 2
        return $this->createDtoRelatedToEntityDataObject($owningDto, $relatedEntityFqn);
190
    }
191
192
    /**
193
     * @param EntityData $owningDataObject
194
     * @param string     $relatedEntityFqn
195
     *
196
     * @return DataTransferObjectInterface
197
     * @throws DoctrineStaticMetaException
198
     * @throws ReflectionException
199
     *
200
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
201
     */
202 2
    private function createDtoRelatedToEntityDataObject(
203
        EntityData $owningDataObject,
204
        string $relatedEntityFqn
205
    ): DataTransferObjectInterface {
206 2
        $relatedDtoFqn = $this->namespaceHelper->getEntityDtoFqnFromEntityFqn($relatedEntityFqn);
207 2
        $newlyCreated  = false;
208 2
        $dto           = $this->getCreatedDto($relatedDtoFqn);
209 2
        if (null === $dto) {
210 2
            $newlyCreated = true;
211 2
            $dto          = $this->createDtoInstance($relatedDtoFqn);
212
        }
213
        /**
214
         * @var DoctrineStaticMeta $owningDsm
215
         */
216 2
        $owningEntityFqn = $owningDataObject::getEntityFqn();
217 2
        $owningDsm       = $owningEntityFqn::getDoctrineStaticMeta();
218 2
        $owningSingular  = $owningDsm->getSingular();
219 2
        $owningPlural    = $owningDsm->getPlural();
220
221
        /**
222
         * @var DoctrineStaticMeta $relatedDsm
223
         */
224 2
        $relatedDsm = $relatedEntityFqn::getDoctrineStaticMeta();
225
226 2
        $dtoSuffix = $owningDataObject instanceof DataTransferObjectInterface ? 'Dto' : '';
227
228 2
        $relatedRequiredRelations = $relatedDsm->getRequiredRelationProperties();
229 2
        foreach (array_keys($relatedRequiredRelations) as $propertyName) {
230
            switch ($propertyName) {
231 2
                case $owningSingular:
232 2
                    $getter = 'get' . $owningSingular . $dtoSuffix;
233
                    try {
234 2
                        if (null !== $dto->$getter()) {
235
                            break 2;
236
                        }
237 2
                    } catch (TypeError $e) {
238
                        //null will cause a type error on getter
239
                    }
240 2
                    $setter = 'set' . $owningSingular . $dtoSuffix;
241 2
                    $dto->$setter($owningDataObject);
242
243 2
                    break 2;
244
                case $owningPlural:
245
                    $collectionGetter = 'get' . $owningPlural;
246
                    $collection       = $dto->$collectionGetter();
247
                    foreach ($collection as $item) {
248
                        if ($item === $owningDataObject) {
249
                            break 3;
250
                        }
251
                    }
252
                    $collection->add($owningDataObject);
253
254
                    break 2;
255
            }
256
        }
257 2
        if (true === $newlyCreated) {
258 2
            $this->addRequiredItemsToDto($dto);
259
        }
260
261 2
        return $dto;
262
    }
263
264 2
    private function getCreatedDto(string $dtoFqn): ?DataTransferObjectInterface
265
    {
266 2
        return $this->createdDtos[$dtoFqn] ?? null;
267
    }
268
269 2
    private function createDtoInstance(string $dtoFqn): DataTransferObjectInterface
270
    {
271 2
        if (null !== $this->getCreatedDto($dtoFqn)) {
272
            throw new LogicException('Trying to set a created DTO ' . $dtoFqn . ' when one already exists');
273
        }
274 2
        $dto = new $dtoFqn();
275 2
        $this->setId($dto);
276 2
        $this->createdDtos[ltrim($dtoFqn, '\\')] = $dto;
277
278 2
        return $dto;
279
    }
280
281 2
    private function addNestedDto(
282
        DataTransferObjectInterface $dto,
283
        string $propertyName,
284
        string $entityInterfaceFqn
285
    ): void {
286 2
        $dtoSetter = 'set' . $propertyName . 'Dto';
287 2
        $dto->$dtoSetter(
288 2
            $this->createDtoRelatedToDto(
289 2
                $dto,
290 2
                $this->namespaceHelper->getEntityFqnFromEntityInterfaceFqn($entityInterfaceFqn)
291
            )
292
        );
293 2
    }
294
295 2
    private function addRequiredEmbeddableObjectsToDto(DataTransferObjectInterface $dto): void
296
    {
297 2
        $dsm                  = $this->getDsmFromEntityFqn($dto::getEntityFqn());
298 2
        $embeddableProperties = $dsm->getEmbeddableProperties();
299 2
        foreach ($embeddableProperties as $property => $embeddableObject) {
300
            $setter = 'set' . $property;
301
            $dto->$setter($embeddableObject::create($embeddableObject::DEFAULTS));
302
        }
303 2
    }
304
305
    /**
306
     * Create a DTO with a preset relation to the owning Entity and all other items filled with new objects
307
     *
308
     * @param EntityInterface $owningEntity
309
     * @param string          $relatedEntityFqn
310
     *
311
     * @return DataTransferObjectInterface
312
     * @throws DoctrineStaticMetaException
313
     * @throws ReflectionException
314
     */
315
    public function createDtoRelatedToEntityInstance(
316
        EntityInterface $owningEntity,
317
        string $relatedEntityFqn
318
    ): DataTransferObjectInterface {
319
        $this->resetCreationTransaction();
320
321
        return $this->createDtoRelatedToEntityDataObject($owningEntity, $relatedEntityFqn);
322
    }
323
324
    /**
325
     * Create a DTO with the values from teh Entity, optionally with some values directly overridden with your values
326
     * to set
327
     *
328
     * @param EntityInterface $entity
329
     *
330
     * @return mixed
331
     * @throws ReflectionException
332
     */
333 1
    public function createDtoFromEntity(EntityInterface $entity)
334
    {
335 1
        $this->resetCreationTransaction();
336 1
        $dsm     = $entity::getDoctrineStaticMeta();
337 1
        $dtoFqn  = $this->namespaceHelper->getEntityDtoFqnFromEntityFqn($dsm->getReflectionClass()->getName());
338 1
        $dto     = new $dtoFqn();
339 1
        $setters = $dsm->getSetters();
340 1
        foreach ($setters as $getterName => $setterName) {
341 1
            $dto->$setterName($entity->$getterName());
342
        }
343
344 1
        return $dto;
345
    }
346
}
347