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
Pull Request — master (#152)
by joseph
17:58
created

DtoFactory::resetCreationTransaction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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