Completed
Push — master ( 250fa5...0b3aa1 )
by Massimiliano
06:03
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
     * Constructor.
38
     *
39
     * @param string $tagClassName
40
     * @param bool   $purge        whether to delete tags when entity is deleted
41
     *
42
     * @throws MappingException
43
     * @throws \InvalidArgumentException
44
     */
45
    public function __construct(string $tagClassName, $purge = false)
46
    {
47
        if (!class_exists($tagClassName)) {
48
            throw MappingException::nonExistingClass($tagClassName);
49
        }
50
        $this->tag = new $tagClassName();
51
        if (!$this->tag instanceof TagInterface) {
52
            throw new \InvalidArgumentException(sprintf('Class "%s" must implement TagInterface.', $tagClassName));
53
        }
54
        $this->purge = $purge;
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function getSubscribedEvents()
61
    {
62
        return ['onFlush'];
63
    }
64
65
    /**
66
     * Main method: call setTags() on entities scheduled to be inserted or updated, and
67
     * possibly call purgeTags() on entities scheduled to be deleted.
68
     *
69
     * @param OnFlushEventArgs $args
70
     */
71
    public function onFlush(OnFlushEventArgs $args)
72
    {
73
        $this->manager = $args->getEntityManager();
74
        $this->uow = $this->manager->getUnitOfWork();
75
        foreach ($this->uow->getScheduledEntityInsertions() as $key => $entity) {
76
            if ($entity instanceof TaggableInterface) {
77
                $this->setTags($entity, false);
78
            }
79
        }
80
        foreach ($this->uow->getScheduledEntityUpdates() as $key => $entity) {
81
            if ($entity instanceof TaggableInterface) {
82
                $this->setTags($entity, true);
83
            }
84
        }
85
        if ($this->purge) {
86
            foreach ($this->uow->getScheduledEntityDeletions() as $key => $entity) {
87
                if ($entity instanceof TaggableInterface) {
88
                    $this->purgeTags($entity);
89
                }
90
            }
91
        }
92
    }
93
94
    /**
95
     * Do the stuff.
96
     *
97
     * @param TaggableInterface $entity
98
     * @param bool              $update true if entity is being updated, false otherwise
99
     */
100
    protected function setTags(TaggableInterface $entity, bool $update = false)
101
    {
102
        $tagNames = $entity->getTagNames();
103
        if (empty($tagNames) && !$update) {
104
            return;
105
        }
106
        // need to clone here, to avoid getting new tags
107
        $oldTags = is_object($entityTags = $entity->getTags()) ? clone $entityTags : $entityTags;
108
        $tagClassMetadata = $this->manager->getClassMetadata(get_class($this->tag));
109
        $repository = $this->manager->getRepository(get_class($this->tag));
110
        foreach ($tagNames as $tagName) {
111
            $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...
112
            if (empty($tag)) {
113
                // if tag doesn't exist, create it
114
                $tag = clone $this->tag;
115
                $tag->setName($tagName);
116
                $this->manager->persist($tag);
117
                // see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#onflush
118
                $this->uow->computeChangeSet($tagClassMetadata, $tag);
119
            }
120
            if (!$entity->hasTag($tag)) {
121
                // add tag only if not already added
122
                $entity->addTag($tag);
123
            }
124
        }
125
        // if updating, need to check if some tags were removed
126
        if ($update) {
127
            foreach ($oldTags as $oldTag) {
128
                if (!in_array($oldTag->getName(), $tagNames)) {
129
                    $entity->removeTag($oldTag);
130
                }
131
            }
132
        }
133
        // see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#onflush
134
        $entityClassMetadata = $this->manager->getClassMetadata(get_class($entity));
135
        $this->uow->computeChangeSets($entityClassMetadata, $entity);
136
    }
137
138
    /**
139
     * Purge oprhan tags
140
     * Warning: DO NOT purge tags if you have more than one entity
141
     * with tags, since this could lead to costraint violations.
142
     *
143
     * @param TaggableInterface $entity
144
     */
145
    protected function purgeTags(TaggableInterface $entity)
146
    {
147
        foreach ($entity->getTags() as $oldTag) {
148
            $this->manager->remove($oldTag);
149
        }
150
    }
151
}
152