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.
Passed
Push — master ( 71fe6e...6a7137 )
by Christian
01:55
created

src/EventListener/ORM/SortableListener.php (2 issues)

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\ClassMetadata;
24
use Doctrine\ORM\Mapping\MappingException;
25
use Doctrine\ORM\NonUniqueResultException;
26
use Doctrine\ORM\QueryBuilder;
27
use Doctrine\ORM\UnitOfWork;
28
use LogicException;
29
use Symfony\Component\PropertyAccess\PropertyAccess;
30
use Symfony\Component\PropertyAccess\PropertyAccessor;
31
32
final class SortableListener implements EventSubscriber
33
{
34
    /**
35
     * @var PropertyAccessor
36
     */
37
    private $propertyAccessor;
38
39
    /**
40
     * @param PropertyAccessor $propertyAccessor
41
     */
42
    public function __construct(PropertyAccessor $propertyAccessor = null)
43
    {
44
        if (null === $propertyAccessor) {
45
            $propertyAccessor = PropertyAccess::createPropertyAccessor();
46
        }
47
48
        $this->propertyAccessor = $propertyAccessor;
49
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54
    public function getSubscribedEvents()
55
    {
56
        return [
57
            Events::prePersist,
58
            Events::preUpdate,
59
            Events::preRemove,
60
            Events::loadClassMetadata,
61
        ];
62
    }
63
64
    /**
65
     * @param LifecycleEventArgs $args
66
     */
67
    public function prePersist(LifecycleEventArgs $args): void
68
    {
69
        if (!$args->getEntity() instanceof PositionAwareInterface) {
70
            return;
71
        }
72
73
        $this->uniquePosition($args);
74
    }
75
76
    /**
77
     * @param PreUpdateEventArgs $args
78
     */
79
    public function preUpdate(PreUpdateEventArgs $args): void
80
    {
81
        if (!$args->getEntity() instanceof PositionAwareInterface) {
82
            return;
83
        }
84
85
        $position = $args->getEntity()->getPosition();
86
87
        if ($args->hasChangedField('position')) {
88
            $position = $args->getOldValue('position');
89
        }
90
91
        $this->uniquePosition($args, $position);
92
    }
93
94
    /**
95
     * @param LifecycleEventArgs $args
96
     */
97
    public function preRemove(LifecycleEventArgs $args): void
98
    {
99
        $entity = $args->getEntity();
100
101
        if ($entity instanceof PositionAwareInterface) {
102
            $this->movePosition($args->getEntityManager(), $entity, -1);
103
        }
104
    }
105
106
    /**
107
     * @param LoadClassMetadataEventArgs $eventArgs
108
     *
109
     * @throws MappingException
110
     */
111
    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void
112
    {
113
        $meta = $eventArgs->getClassMetadata();
114
115
        if (!$meta instanceof ClassMetadata) {
0 ignored issues
show
$meta is always a sub-type of Doctrine\ORM\Mapping\ClassMetadata.
Loading history...
116
            throw new LogicException('Class metadata was no ORM');
117
        }
118
119
        $reflClass = $meta->getReflectionClass();
120
121
        if (null === $reflClass || !ClassUtils::containsTrait($reflClass, SortableTrait::class)) {
122
            return;
123
        }
124
125
        if (!$meta->hasField('position')) {
126
            $meta->mapField([
127
                'type'      => 'integer',
128
                'fieldName' => 'position',
129
            ]);
130
        }
131
    }
132
133
    /**
134
     * @param LifecycleEventArgs $args
135
     * @param int|null           $oldPosition
136
     */
137
    private function uniquePosition(LifecycleEventArgs $args, ?int $oldPosition = null): void
138
    {
139
        $entity = $args->getEntity();
140
141
        if ($entity instanceof PositionAwareInterface) {
142
            if (null === $entity->getPosition()) {
143
                $position = $this->getNextPosition($args->getEntityManager(), $entity);
144
                $entity->setPosition($position);
145
            } elseif ($oldPosition && $oldPosition !== $entity->getPosition()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $oldPosition of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
146
                $this->movePosition($args->getEntityManager(), $entity);
147
            }
148
        }
149
    }
150
151
    /**
152
     * @param EntityManager          $em
153
     * @param PositionAwareInterface $entity
154
     * @param int                    $direction
155
     */
156
    private function movePosition(EntityManager $em, PositionAwareInterface $entity, int $direction = 1): void
157
    {
158
        $uow  = $em->getUnitOfWork();
159
        $meta = $em->getClassMetadata(\get_class($entity));
160
161
        $qb = $em->createQueryBuilder()
162
            ->update($meta->getName(), 'e')
163
            ->set('e.position', 'e.position + '.$direction)
164
        ;
165
166
        if ($direction > 0) {
167
            $qb->andWhere('e.position <= :position')->setParameter('position', $entity->getPosition());
168
        } elseif ($direction < 0) {
169
            $qb->andWhere('e.position >= :position')->setParameter('position', $entity->getPosition());
170
        } else {
171
            return;
172
        }
173
174
        $this->addGroupFilter($qb, $entity, $uow);
175
176
        $qb->getQuery()->execute();
177
    }
178
179
    /**
180
     * @param EntityManager          $em
181
     * @param PositionAwareInterface $entity
182
     *
183
     * @return int
184
     */
185
    private function getNextPosition(EntityManager $em, PositionAwareInterface $entity): int
186
    {
187
        $meta = $em->getClassMetadata(\get_class($entity));
188
189
        $qb = $em->createQueryBuilder()
190
            ->select('e')
191
            ->from($meta->getName(), 'e')
192
            ->addOrderBy('e.position', 'DESC')
193
            ->setMaxResults(1)
194
        ;
195
196
        $this->addGroupFilter($qb, $entity);
197
198
        try {
199
            $result = $qb->getQuery()->getOneOrNullResult();
200
201
            return ($result instanceof PositionAwareInterface ? $result->getPosition() : 0) + 1;
202
        } catch (NonUniqueResultException $ignored) {
203
            return 0;
204
        }
205
    }
206
207
    /**
208
     * @param QueryBuilder           $qb
209
     * @param PositionAwareInterface $entity
210
     * @param UnitOfWork             $uow
211
     */
212
    private function addGroupFilter(QueryBuilder $qb, PositionAwareInterface $entity, UnitOfWork $uow = null): void
213
    {
214
        foreach ($entity->getPositionGroup() as $field) {
215
            $value = $this->propertyAccessor->getValue($entity, $field);
216
217
            if (\is_object($value) && (null === $uow || null === $uow->getSingleIdentifierValue($value))) {
218
                continue;
219
            }
220
221
            $qb->andWhere('e.'.$field.' = :'.$field)->setParameter($field, $value);
222
        }
223
    }
224
}
225