1 | <?php |
||||||
2 | |||||||
3 | namespace Beelab\TagBundle\Listener; |
||||||
4 | |||||||
5 | use Beelab\TagBundle\Tag\TaggableInterface; |
||||||
6 | use Beelab\TagBundle\Tag\TagInterface; |
||||||
7 | use Doctrine\Common\EventSubscriber; |
||||||
8 | use Doctrine\Common\Persistence\Mapping\MappingException as LegacyMappingException; |
||||||
0 ignored issues
–
show
|
|||||||
9 | use Doctrine\ORM\Event\OnFlushEventArgs; |
||||||
10 | use Doctrine\Persistence\Mapping\MappingException; |
||||||
11 | |||||||
12 | /** |
||||||
13 | * Add tags to entities that implements TaggableInterface. |
||||||
14 | */ |
||||||
15 | final class TagSubscriber implements EventSubscriber |
||||||
16 | { |
||||||
17 | /** |
||||||
18 | * @var \Doctrine\ORM\EntityManager |
||||||
19 | */ |
||||||
20 | private $manager; |
||||||
21 | |||||||
22 | /** |
||||||
23 | * @var \Doctrine\ORM\UnitOfWork |
||||||
24 | */ |
||||||
25 | private $uow; |
||||||
26 | |||||||
27 | /** |
||||||
28 | * @var TagInterface |
||||||
29 | */ |
||||||
30 | private $tag; |
||||||
31 | |||||||
32 | /** |
||||||
33 | * @var bool |
||||||
34 | */ |
||||||
35 | private $purge; |
||||||
36 | |||||||
37 | /** |
||||||
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 | if (\class_exists('Doctrine\Common\Persistence\Mapping\MappingException')) { |
||||||
47 | throw LegacyMappingException::nonExistingClass($tagClassName); |
||||||
48 | } |
||||||
49 | throw MappingException::nonExistingClass($tagClassName); |
||||||
50 | } |
||||||
51 | $this->tag = new $tagClassName(); |
||||||
52 | if (!$this->tag instanceof TagInterface) { |
||||||
53 | throw new \InvalidArgumentException(\sprintf('Class "%s" must implement TagInterface.', $tagClassName)); |
||||||
54 | } |
||||||
55 | $this->purge = $purge; |
||||||
56 | } |
||||||
57 | |||||||
58 | public function getSubscribedEvents(): array |
||||||
59 | { |
||||||
60 | return ['onFlush']; |
||||||
61 | } |
||||||
62 | |||||||
63 | /** |
||||||
64 | * Main method: call setTags() on entities scheduled to be inserted or updated, and |
||||||
65 | * possibly call purgeTags() on entities scheduled to be deleted. |
||||||
66 | */ |
||||||
67 | public function onFlush(OnFlushEventArgs $args): void |
||||||
68 | { |
||||||
69 | $this->manager = $args->getEntityManager(); |
||||||
70 | $this->uow = $this->manager->getUnitOfWork(); |
||||||
71 | foreach ($this->uow->getScheduledEntityInsertions() as $key => $entity) { |
||||||
72 | if ($entity instanceof TaggableInterface) { |
||||||
73 | $this->setTags($entity, false); |
||||||
74 | } |
||||||
75 | } |
||||||
76 | foreach ($this->uow->getScheduledEntityUpdates() as $key => $entity) { |
||||||
77 | if ($entity instanceof TaggableInterface) { |
||||||
78 | $this->setTags($entity, true); |
||||||
79 | } |
||||||
80 | } |
||||||
81 | if ($this->purge) { |
||||||
82 | foreach ($this->uow->getScheduledEntityDeletions() as $key => $entity) { |
||||||
83 | if ($entity instanceof TaggableInterface) { |
||||||
84 | $this->purgeTags($entity); |
||||||
85 | } |
||||||
86 | } |
||||||
87 | } |
||||||
88 | } |
||||||
89 | |||||||
90 | /** |
||||||
91 | * Do the stuff. |
||||||
92 | * |
||||||
93 | * @param bool $update true if entity is being updated, false otherwise |
||||||
94 | */ |
||||||
95 | private 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\Persistence\ObjectRepository . Did you maybe mean findOneBy() ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed.
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); |
||||||
0 ignored issues
–
show
The call to
Doctrine\ORM\UnitOfWork::computeChangeSets() has too many arguments starting with $entityClassMetadata .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.
Loading history...
|
|||||||
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 | private function purgeTags(TaggableInterface $entity): void |
||||||
139 | { |
||||||
140 | foreach ($entity->getTags() as $oldTag) { |
||||||
141 | $this->manager->remove($oldTag); |
||||||
142 | } |
||||||
143 | } |
||||||
144 | } |
||||||
145 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths