Completed
Push — master ( 76a901...5ab4c0 )
by BENOIT
04:54
created

DoctrineWatcher::preUpdate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BenTools\DoctrineWatcher\Watcher;
4
5
use BenTools\DoctrineWatcher\Changeset\ChangesetFactory;
6
use BenTools\DoctrineWatcher\Changeset\PropertyChangeset;
7
use Doctrine\Common\EventSubscriber;
8
use Doctrine\Common\Util\ClassUtils;
9
use Doctrine\ORM\Event\LifecycleEventArgs;
10
11
final class DoctrineWatcher implements EventSubscriber
12
{
13
    public const DEFAULT_OPTIONS = [
14
        'trigger_on_persist'   => false,
15
        'trigger_when_no_changes' => false,
16
        'type'    => PropertyChangeset::CHANGESET_DEFAULT
17
    ];
18
19
    /**
20
     * @var array
21
     */
22
    private $defaulOptions;
23
24
    /**
25
     * @var ChangesetFactory
26
     */
27
    private $changesetFactory;
28
29
    /**
30
     * @var callable[]
31
     */
32
    private $listeners = [];
33
34
    /**
35
     * DoctrineWatcher constructor.
36
     * @param array                 $options
37
     * @param ChangesetFactory|null $changesetFactory
38
     */
39
    public function __construct(
40
        array $options = self::DEFAULT_OPTIONS,
41
        ChangesetFactory $changesetFactory = null
42
    ) {
43
        $this->defaulOptions = $options + self::DEFAULT_OPTIONS;
44
        $this->changesetFactory = $changesetFactory ?? new ChangesetFactory();
45
    }
46
47
    /**
48
     * @param string   $entityClass
49
     * @param string   $property
50
     * @param callable $callback
51
     * @param array    $options
52
     */
53
    public function watch(string $entityClass, string $property, callable $callback, array $options = []): void
54
    {
55
        $options = $options + $this->defaulOptions;
56
        $listener = $this->createPropertyListener($entityClass, $property, $callback, $options);
57
        $this->listeners[$entityClass][$property][] = $listener;
58
    }
59
60
    /**
61
     * @param string   $entityClass
62
     * @param string   $property
63
     * @param callable $callback
64
     * @param array    $options
65
     */
66
    public function watchIterable(string $entityClass, string $property, callable $callback, array $options = []): void
67
    {
68
        $this->watch($entityClass, $property, $callback, ['type' => PropertyChangeset::CHANGESET_ITERABLE] + $options);
69
    }
70
71
    /**
72
     * @param string   $entityClass
73
     * @param string   $property
74
     * @param callable $callback
75
     * @param array    $options
76
     * @return callable
77
     */
78
    private function createPropertyListener(string $entityClass, string $property, callable $callback, array $options = []): callable
79
    {
80
        return function (LifecycleEventArgs $eventArgs, string $operationType) use ($entityClass, $property, $callback, $options) {
81
            $em = $eventArgs->getEntityManager();
82
            $unitOfWork = $em->getUnitOfWork();
83
            $entity = $eventArgs->getEntity();
84
85
            // Do not trigger on the wrong entity
86
            if (!$entity instanceof $entityClass) {
87
                return;
88
            }
89
90
            // Do not trigger if entity was not managed
91
            if (false === $options['trigger_on_persist'] && PropertyChangeset::INSERT === $operationType) {
92
                return;
93
            }
94
95
            // Do not trigger if field has no changes
96
            $className = ClassUtils::getClass($entity);
97
            $classMetadata = $em->getClassMetadata($className);
98
            $changedProperties = $this->changesetFactory->getChangedProperties($entity, $unitOfWork, $classMetadata);
99
            if (!in_array($property, $changedProperties) && false === $options['trigger_when_no_changes']) {
100
                return;
101
            }
102
103
            $changeset = $this->changesetFactory->getChangeset($entity, $property, $unitOfWork, $classMetadata, $options['type']);
104
            $callback(
105
                $changeset,
106
                $operationType,
107
                $entity,
108
                $property
109
            );
110
        };
111
    }
112
113
    /**
114
     * @param LifecycleEventArgs $eventArgs
115
     * @param string             $operationType
116
     */
117
    private function trigger(LifecycleEventArgs $eventArgs, string $operationType): void
118
    {
119
        $entity = $eventArgs->getEntity();
120
        $class = ClassUtils::getClass($entity);
121
        foreach ($this->listeners[$class] ?? [] as $property => $listeners) {
122
            foreach ($listeners as $listener) {
123
                $listener($eventArgs, $operationType);
124
            }
125
        }
126
    }
127
128
    /**
129
     * @param LifecycleEventArgs $eventArgs
130
     */
131
    public function postPersist(LifecycleEventArgs $eventArgs): void
132
    {
133
        $this->trigger($eventArgs, PropertyChangeset::INSERT);
134
    }
135
136
    /**
137
     * @param LifecycleEventArgs $eventArgs
138
     */
139
    public function postUpdate(LifecycleEventArgs $eventArgs): void
140
    {
141
        $this->trigger($eventArgs, PropertyChangeset::UPDATE);
142
    }
143
144
    /**
145
     * @inheritDoc
146
     */
147
    public function getSubscribedEvents()
148
    {
149
        return [
150
            'postPersist',
151
            'postUpdate',
152
        ];
153
    }
154
}
155