GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

SortableListener   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 31
eloc 68
dl 0
loc 155
rs 9.92
c 1
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
A preUpdate() 0 13 3
A getSubscribedEvents() 0 7 1
A preRemove() 0 6 2
A movePosition() 0 21 3
A prePersist() 0 7 2
A loadClassMetadata() 0 14 4
A uniquePosition() 0 10 5
A getNextPosition() 0 23 4
A addGroupFilter() 0 10 5
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * (c) Christian Gripp <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Core23\Doctrine\EventListener\ORM;
13
14
use Core23\Doctrine\Model\PositionAwareInterface;
15
use Core23\Doctrine\Model\Traits\SortableTrait;
16
use Core23\Doctrine\Util\ClassUtils;
17
use Doctrine\Common\EventSubscriber;
18
use Doctrine\ORM\EntityManager;
19
use Doctrine\ORM\Event\LifecycleEventArgs;
20
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
21
use Doctrine\ORM\Event\PreUpdateEventArgs;
22
use Doctrine\ORM\Events;
23
use Doctrine\ORM\Mapping\MappingException;
24
use Doctrine\ORM\NonUniqueResultException;
25
use Doctrine\ORM\QueryBuilder;
26
use Doctrine\ORM\UnitOfWork;
27
use Symfony\Component\PropertyAccess\PropertyAccess;
28
use Symfony\Component\PropertyAccess\PropertyAccessor;
29
30
final class SortableListener implements EventSubscriber
31
{
32
    /**
33
     * @var PropertyAccessor
34
     */
35
    private $propertyAccessor;
36
37
    /**
38
     * @param PropertyAccessor $propertyAccessor
39
     */
40
    public function __construct(PropertyAccessor $propertyAccessor = null)
41
    {
42
        if (null === $propertyAccessor) {
43
            $propertyAccessor = PropertyAccess::createPropertyAccessor();
44
        }
45
46
        $this->propertyAccessor = $propertyAccessor;
47
    }
48
49
    public function getSubscribedEvents()
50
    {
51
        return [
52
            Events::prePersist,
53
            Events::preUpdate,
54
            Events::preRemove,
55
            Events::loadClassMetadata,
56
        ];
57
    }
58
59
    public function prePersist(LifecycleEventArgs $args): void
60
    {
61
        if (!$args->getEntity() instanceof PositionAwareInterface) {
62
            return;
63
        }
64
65
        $this->uniquePosition($args);
66
    }
67
68
    public function preUpdate(PreUpdateEventArgs $args): void
69
    {
70
        if (!$args->getEntity() instanceof PositionAwareInterface) {
71
            return;
72
        }
73
74
        $position = $args->getEntity()->getPosition();
75
76
        if ($args->hasChangedField('position')) {
77
            $position = $args->getOldValue('position');
78
        }
79
80
        $this->uniquePosition($args, $position);
81
    }
82
83
    public function preRemove(LifecycleEventArgs $args): void
84
    {
85
        $entity = $args->getEntity();
86
87
        if ($entity instanceof PositionAwareInterface) {
88
            $this->movePosition($args->getEntityManager(), $entity, -1);
89
        }
90
    }
91
92
    /**
93
     * @throws MappingException
94
     */
95
    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void
96
    {
97
        $meta = $eventArgs->getClassMetadata();
98
99
        $reflClass = $meta->getReflectionClass();
100
101
        if (null === $reflClass || !ClassUtils::containsTrait($reflClass, SortableTrait::class)) {
102
            return;
103
        }
104
105
        if (!$meta->hasField('position')) {
106
            $meta->mapField([
107
                'type'      => 'integer',
108
                'fieldName' => 'position',
109
            ]);
110
        }
111
    }
112
113
    private function uniquePosition(LifecycleEventArgs $args, ?int $oldPosition = null): void
114
    {
115
        $entity = $args->getEntity();
116
117
        if ($entity instanceof PositionAwareInterface) {
118
            if (null === $entity->getPosition()) {
119
                $position = $this->getNextPosition($args->getEntityManager(), $entity);
120
                $entity->setPosition($position);
121
            } elseif (null !== $oldPosition && $oldPosition !== $entity->getPosition()) {
122
                $this->movePosition($args->getEntityManager(), $entity);
123
            }
124
        }
125
    }
126
127
    private function movePosition(EntityManager $em, PositionAwareInterface $entity, int $direction = 1): void
128
    {
129
        $uow  = $em->getUnitOfWork();
130
        $meta = $em->getClassMetadata(\get_class($entity));
131
132
        $qb = $em->createQueryBuilder()
133
            ->update($meta->getName(), 'e')
134
            ->set('e.position', 'e.position + '.$direction)
135
        ;
136
137
        if ($direction > 0) {
138
            $qb->andWhere('e.position <= :position')->setParameter('position', $entity->getPosition());
139
        } elseif ($direction < 0) {
140
            $qb->andWhere('e.position >= :position')->setParameter('position', $entity->getPosition());
141
        } else {
142
            return;
143
        }
144
145
        $this->addGroupFilter($qb, $entity, $uow);
146
147
        $qb->getQuery()->execute();
148
    }
149
150
    private function getNextPosition(EntityManager $em, PositionAwareInterface $entity): int
151
    {
152
        $meta = $em->getClassMetadata(\get_class($entity));
153
154
        $qb = $em->createQueryBuilder()
155
            ->select('e')
156
            ->from($meta->getName(), 'e')
157
            ->addOrderBy('e.position', 'DESC')
158
            ->setMaxResults(1)
159
        ;
160
161
        $this->addGroupFilter($qb, $entity);
162
163
        try {
164
            $result = $qb->getQuery()->getOneOrNullResult();
165
166
            if ($result instanceof PositionAwareInterface && null !== $result->getPosition()) {
167
                return $result->getPosition() + 1;
168
            }
169
        } catch (NonUniqueResultException $ignored) {
0 ignored issues
show
Coding Style Comprehensibility introduced by core23
Consider adding a comment why this CATCH block is empty.
Loading history...
170
        }
171
172
        return 0;
173
    }
174
175
    private function addGroupFilter(QueryBuilder $qb, PositionAwareInterface $entity, UnitOfWork $uow = null): void
176
    {
177
        foreach ($entity->getPositionGroup() as $field) {
178
            $value = $this->propertyAccessor->getValue($entity, $field);
179
180
            if (\is_object($value) && (null === $uow || null === $uow->getSingleIdentifierValue($value))) {
181
                continue;
182
            }
183
184
            $qb->andWhere('e.'.$field.' = :'.$field)->setParameter($field, $value);
185
        }
186
    }
187
}
188