Passed
Push — master ( 27e93f...a6cf90 )
by BENOIT
02:04
created

DoctrineWatcher::watchIterable()   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 4
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\PropertyChangeset;
6
use BenTools\DoctrineWatcher\Changeset\ChangesetFactory;
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'   => true,
15
        'trigger_when_no_changes' => true,
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 = function (LifecycleEventArgs $eventArgs) use ($entityClass, $property, $callback, $options) {
57
            $em = $eventArgs->getEntityManager();
58
            $unitOfWork = $em->getUnitOfWork();
59
            $entity = $eventArgs->getEntity();
60
61
            // Do not trigger on the wrong entity
62
            if (!$entity instanceof $entityClass) {
63
                return;
64
            }
65
66
            // Do not trigger if entity was not managed
67
            if (false === $options['trigger_on_persist'] && $this->changesetFactory->isNotManagedYet($entity, $unitOfWork)) {
68
                return;
69
            }
70
71
            // Do not trigger if field has no changes
72
            $className = ClassUtils::getClass($entity);
73
            $classMetadata = $em->getClassMetadata($className);
74
            $changedProperties = $this->changesetFactory->getChangedProperties($entity, $unitOfWork, $classMetadata);
75
            if (!in_array($property, $changedProperties) && false === $options['trigger_when_no_changes']) {
76
                return;
77
            }
78
79
            $changeset = $this->changesetFactory->getChangeset($entity, $property, $unitOfWork, $classMetadata, $options['type']);
80
            $callback(
81
                $changeset,
82
                $this->changesetFactory->isNotManagedYet($entity, $unitOfWork) ? PropertyChangeset::INSERT : PropertyChangeset::UPDATE,
83
                $entity,
84
                $property
85
            );
86
        };
87
        $this->listeners[$entityClass][$property][] = $listener;
88
    }
89
90
    /**
91
     * @param string   $entityClass
92
     * @param string   $property
93
     * @param callable $callback
94
     * @param array    $options
95
     */
96
    public function watchIterable(string $entityClass, string $property, callable $callback, array $options = []): void
97
    {
98
        $this->watch($entityClass, $property, $callback, ['type' => PropertyChangeset::CHANGESET_ITERABLE] + $options);
99
    }
100
101
    /**
102
     * @param LifecycleEventArgs $eventArgs
103
     */
104
    private function trigger(LifecycleEventArgs $eventArgs): void
105
    {
106
        $entity = $eventArgs->getEntity();
107
        $class = ClassUtils::getClass($entity);
108
        foreach ($this->listeners[$class] ?? [] as $property => $listeners) {
109
            foreach ($listeners as $listener) {
110
                $listener($eventArgs);
111
            }
112
        }
113
    }
114
115
    /**
116
     * @param LifecycleEventArgs $eventArgs
117
     */
118
    public function prePersist(LifecycleEventArgs $eventArgs): void
119
    {
120
        $this->trigger($eventArgs);
121
    }
122
123
    /**
124
     * @param LifecycleEventArgs $eventArgs
125
     */
126
    public function preUpdate(LifecycleEventArgs $eventArgs): void
127
    {
128
        $this->trigger($eventArgs);
129
    }
130
131
    /**
132
     * @inheritDoc
133
     */
134
    public function getSubscribedEvents()
135
    {
136
        return [
137
            'prePersist',
138
            'preUpdate',
139
        ];
140
    }
141
}
142