Completed
Push — master ( 064cd2...74a0dd )
by Massimiliano
07:53 queued 06:35
created

Listener/TagSubscriber.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Beelab\TagBundle\Listener;
4
5
use Beelab\TagBundle\Tag\TagInterface;
6
use Beelab\TagBundle\Tag\TaggableInterface;
7
use Doctrine\Common\EventSubscriber;
8
use Doctrine\Common\Persistence\Mapping\MappingException;
9
use Doctrine\ORM\Event\OnFlushEventArgs;
10
11
/**
12
 * Add tags to entities that implements TaggableInterface.
13
 */
14
class TagSubscriber implements EventSubscriber
15
{
16
    /**
17
     * @var \Doctrine\ORM\EntityManager
18
     */
19
    protected $manager;
20
21
    /**
22
     * @var \Doctrine\ORM\UnitOfWork
23
     */
24
    protected $uow;
25
26
    /**
27
     * @var TagInterface
28
     */
29
    protected $tag;
30
31
    /**
32
     * @var bool
33
     */
34
    protected $purge;
35
36
    /**
37
     * @param string $tagClassName
38
     * @param bool   $purge        whether to delete tags when entity is deleted
39
     *
40
     * @throws MappingException
41
     * @throws \InvalidArgumentException
42
     */
43
    public function __construct(string $tagClassName, bool $purge = false)
44
    {
45
        if (!class_exists($tagClassName)) {
46
            throw MappingException::nonExistingClass($tagClassName);
47
        }
48
        $this->tag = new $tagClassName();
49
        if (!$this->tag instanceof TagInterface) {
50
            throw new \InvalidArgumentException(sprintf('Class "%s" must implement TagInterface.', $tagClassName));
51
        }
52
        $this->purge = $purge;
53
    }
54
55
    public function getSubscribedEvents(): array
56
    {
57
        return ['onFlush'];
58
    }
59
60
    /**
61
     * Main method: call setTags() on entities scheduled to be inserted or updated, and
62
     * possibly call purgeTags() on entities scheduled to be deleted.
63
     *
64
     * @param OnFlushEventArgs $args
65
     */
66
    public function onFlush(OnFlushEventArgs $args): void
67
    {
68
        $this->manager = $args->getEntityManager();
69
        $this->uow = $this->manager->getUnitOfWork();
70
        foreach ($this->uow->getScheduledEntityInsertions() as $key => $entity) {
71
            if ($entity instanceof TaggableInterface) {
72
                $this->setTags($entity, false);
73
            }
74
        }
75
        foreach ($this->uow->getScheduledEntityUpdates() as $key => $entity) {
76
            if ($entity instanceof TaggableInterface) {
77
                $this->setTags($entity, true);
78
            }
79
        }
80
        if ($this->purge) {
81
            foreach ($this->uow->getScheduledEntityDeletions() as $key => $entity) {
82
                if ($entity instanceof TaggableInterface) {
83
                    $this->purgeTags($entity);
84
                }
85
            }
86
        }
87
    }
88
89
    /**
90
     * Do the stuff.
91
     *
92
     * @param TaggableInterface $entity
93
     * @param bool              $update true if entity is being updated, false otherwise
94
     */
95
    protected function setTags(TaggableInterface $entity, bool $update = false): void
96
    {
97
        $tagNames = $entity->getTagNames();
98
        if (empty($tagNames) && !$update) {
99
            return;
100
        }
101
        // need to clone here, to avoid getting new tags
102
        $oldTags = is_object($entityTags = $entity->getTags()) ? clone $entityTags : $entityTags;
103
        $tagClassMetadata = $this->manager->getClassMetadata(get_class($this->tag));
104
        $repository = $this->manager->getRepository(get_class($this->tag));
105
        foreach ($tagNames as $tagName) {
106
            $tag = $repository->findOneByName($tagName);
0 ignored issues
show
The method findOneByName() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findOneBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
107
            if (empty($tag)) {
108
                // if tag doesn't exist, create it
109
                $tag = clone $this->tag;
110
                $tag->setName($tagName);
111
                $this->manager->persist($tag);
112
                // see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#onflush
113
                $this->uow->computeChangeSet($tagClassMetadata, $tag);
114
            }
115
            if (!$entity->hasTag($tag)) {
116
                // add tag only if not already added
117
                $entity->addTag($tag);
118
            }
119
        }
120
        // if updating, need to check if some tags were removed
121
        if ($update) {
122
            foreach ($oldTags as $oldTag) {
123
                if (!in_array($oldTag->getName(), $tagNames)) {
124
                    $entity->removeTag($oldTag);
125
                }
126
            }
127
        }
128
        // see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#onflush
129
        $entityClassMetadata = $this->manager->getClassMetadata(get_class($entity));
130
        $this->uow->computeChangeSets($entityClassMetadata, $entity);
131
    }
132
133
    /**
134
     * Purge oprhan tags
135
     * Warning: DO NOT purge tags if you have more than one entity
136
     * with tags, since this could lead to costraint violations.
137
     *
138
     * @param TaggableInterface $entity
139
     */
140
    protected function purgeTags(TaggableInterface $entity): void
141
    {
142
        foreach ($entity->getTags() as $oldTag) {
143
            $this->manager->remove($oldTag);
144
        }
145
    }
146
}
147