/
DefaultDoctrineExecutor.php
executable file
·135 lines (112 loc) · 4.2 KB
/
DefaultDoctrineExecutor.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<?php
/**
* @package 3slab/VdmLibraryDoctrineTransportBundle
* @copyright 2020 Suez Smart Solutions 3S.lab
* @license https://github.com/3slab/VdmLibraryDoctrineTransportBundle/blob/master/LICENSE
*/
namespace Vdm\Bundle\LibraryDoctrineTransportBundle\Executor;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Vdm\Bundle\LibraryDoctrineTransportBundle\Exception\NoConnectionException;
use Vdm\Bundle\LibraryBundle\Model\Message;
class DefaultDoctrineExecutor extends AbstractDoctrineExecutor
{
/**
* {@inheritDoc}
*/
public function execute(Message $message): void
{
if (!$this->manager) {
throw new NoConnectionException('No connection was defined.');
}
$entityMetadatas = $message->getMetadatasByKey('entity');
if (count($entityMetadatas) > 0) {
$entityMetadata = array_shift($entityMetadatas);
$entityClass = $entityMetadata->getValue();
} else {
$entityClass = $this->getDefaultEntity();
}
$entity = $this->serializer->denormalize($message->getPayload(), $entityClass);
$entity = $this->matchEntity($entity);
$this->manager->persist($entity);
$this->manager->flush();
}
/**
* Defines logic to try and fetch previously existing entity and merges it with the new one.
*
* @param object $entity
*
* @return object|null
*/
protected function matchEntity(object $entity): ?object
{
$fqcn = get_class($entity);
$repository = $this->getRepository($fqcn);
// We'll be using different methods according to the options passed to the transport
if (static::SELECTION_MODE_IDENTIFER === $this->getFetchMode($fqcn)) {
$id = $this->getIdentifier($fqcn);
$matchedEntity = $repository->find($id);
} else {
$filters = $this->computeFilters($entity);
$id = json_encode($filters);
$matchedEntity = $repository->findOneBy($filters);
}
if ($matchedEntity) {
// If the entity already exist, merge it.
$entity = $this->merge($matchedEntity, $entity);
$logMessage = 'Updating entity of class {fqcn} with identity {id}.';
} else {
// If entity was not found, then we just have to create it.
$logMessage = 'Creating entity of class {fqcn} with identity {id}.';
}
// Log what happened, and return entity
$this->logger->info($logMessage, [
'fqcn' => $fqcn,
'id' => $id,
]);
return $entity;
}
/**
* Creates filter array with values from the entity.
*
* @param object $entity
*
* @return array
*/
protected function computeFilters(object $entity): array
{
$fqcn = get_class($entity);
$accessor = PropertyAccess::createPropertyAccessorBuilder()
->enableMagicCall()
->getPropertyAccessor()
;
$filterValues = [];
foreach ($this->filters[$fqcn] as $propety => $getter) {
$filterValues[] = $accessor->getValue($entity, $getter);
}
$filter = array_combine(array_keys($this->filters[$fqcn]), $filterValues);
return $filter;
}
/**
* Merges older entity with values from the new one.
*
* @param object $previousEntity
* @param object $newerEntity
*
* @return object
*/
public function merge(object $previousEntity, object $newerEntity): object
{
$fqcn = get_class($previousEntity);
$metadata = $this->manager->getClassMetadata($fqcn);
$mapping = $metadata->getFieldNames();
$identifierKey = array_search($metadata->getIdentifier(), $mapping, true);
// Remove identifer because it usually doesn't have a setter.
unset($mapping[$identifierKey]);
$accessor = PropertyAccess::createPropertyAccessor();
foreach ($mapping as $property) {
$newValue = $accessor->getValue($newerEntity, $property);
$accessor->setValue($previousEntity, $property, $newValue);
}
return $previousEntity;
}
}