Completed
Push — master ( e8e652...72b591 )
by Massimiliano
02:28
created

Listener/TagSubscriber.php (1 issue)

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
    public function __construct(string $tagClassName, $purge = false)
43
    {
44
        if (!class_exists($tagClassName)) {
45
            throw MappingException::nonExistingClass($tagClassName);
46
        }
47
        $this->tag = new $tagClassName();
48
        if (!$this->tag instanceof TagInterface) {
49
            throw new \InvalidArgumentException(sprintf('Class "%s" must implement TagInterface.', $tagClassName));
50
        }
51
        $this->purge = $purge;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function getSubscribedEvents()
58
    {
59
        return ['onFlush'];
60
    }
61
62
    /**
63
     * Main method: call setTags() on entities scheduled to be inserted or updated, and
64
     * possibly call purgeTags() on entities scheduled to be deleted.
65
     *
66
     * @param OnFlushEventArgs $args
67
     */
68
    public function onFlush(OnFlushEventArgs $args)
69
    {
70
        $this->manager = $args->getEntityManager();
71
        $this->uow = $this->manager->getUnitOfWork();
72
        foreach ($this->uow->getScheduledEntityInsertions() as $key => $entity) {
73
            if ($entity instanceof TaggableInterface) {
74
                $this->setTags($entity, false);
75
            }
76
        }
77
        foreach ($this->uow->getScheduledEntityUpdates() as $key => $entity) {
78
            if ($entity instanceof TaggableInterface) {
79
                $this->setTags($entity, true);
80
            }
81
        }
82
        if ($this->purge) {
83
            foreach ($this->uow->getScheduledEntityDeletions() as $key => $entity) {
84
                if ($entity instanceof TaggableInterface) {
85
                    $this->purgeTags($entity);
86
                }
87
            }
88
        }
89
    }
90
91
    /**
92
     * Do the stuff.
93
     *
94
     * @param TaggableInterface $entity
95
     * @param bool              $update true if entity is being updated, false otherwise
96
     */
97
    protected function setTags(TaggableInterface $entity, bool $update = false)
0 ignored issues
show
This operation has 300 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

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