Completed
Push — master ( 799620...8a5869 )
by Kamil
116:02 queued 102:30
created

Constraints/HasEnabledEntityValidator.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
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
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
declare(strict_types=1);
13
14
namespace Sylius\Bundle\CoreBundle\Validator\Constraints;
15
16
use Doctrine\Common\Persistence\ManagerRegistry;
17
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
18
use Doctrine\Common\Persistence\ObjectManager;
19
use Symfony\Component\PropertyAccess\PropertyAccess;
20
use Symfony\Component\PropertyAccess\PropertyAccessor;
21
use Symfony\Component\Validator\Constraint;
22
use Symfony\Component\Validator\ConstraintValidator;
23
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
24
use Webmozart\Assert\Assert;
25
26
final class HasEnabledEntityValidator extends ConstraintValidator
27
{
28
    /**
29
     * @var ManagerRegistry
30
     */
31
    private $registry;
32
33
    /**
34
     * @var PropertyAccessor
35
     */
36
    private $accessor;
37
38
    /**
39
     * @param ManagerRegistry $registry
40
     */
41
    public function __construct(ManagerRegistry $registry)
42
    {
43
        $this->registry = $registry;
44
        $this->accessor = PropertyAccess::createPropertyAccessor();
45
    }
46
47
    /**
48
     * {@inheritdoc}
49
     *
50
     * @throws \InvalidArgumentException
51
     * @throws ConstraintDefinitionException
52
     */
53
    public function validate($entity, Constraint $constraint): void
54
    {
55
        /** @var HasEnabledEntity $constraint */
56
        Assert::isInstanceOf($constraint, HasEnabledEntity::class);
57
58
        $enabled = $this->accessor->getValue($entity, $constraint->enabledPath);
59
60
        if ($enabled === true) {
61
            return;
62
        }
63
64
        $objectManager = $this->getProperObjectManager($constraint->objectManager, $entity);
65
66
        $this->ensureEntityHasProvidedEnabledField($objectManager, $entity, $constraint->enabledPath);
0 ignored issues
show
It seems like $objectManager defined by $this->getProperObjectMa...objectManager, $entity) on line 64 can be null; however, Sylius\Bundle\CoreBundle...sProvidedEnabledField() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
67
68
        $criteria = [$constraint->enabledPath => true];
69
70
        $repository = $objectManager->getRepository(get_class($entity));
71
        $results = $repository->{$constraint->repositoryMethod}($criteria);
72
73
        /* If the result is a MongoCursor, it must be advanced to the first
74
         * element. Rewinding should have no ill effect if $result is another
75
         * iterator implementation.
76
         */
77
        if ($results instanceof \Iterator) {
78
            $results->rewind();
79
        } elseif (is_array($results)) {
80
            reset($results);
81
        }
82
83
        if ($this->isLastEnabledEntity($results, $entity)) {
84
            $errorPath = null !== $constraint->errorPath ? $constraint->errorPath : $constraint->enabledPath;
85
86
            $this->context->buildViolation($constraint->message)->atPath($errorPath)->addViolation();
87
        }
88
    }
89
90
    /**
91
     * If no entity matched the query criteria or a single entity matched, which is the same as the entity being
92
     * validated, the entity is the last enabled entity available.
93
     *
94
     * @param array|\Iterator $result
95
     * @param object $entity
96
     *
97
     * @return bool
98
     */
99
    private function isLastEnabledEntity($result, $entity): bool
100
    {
101
        return !$result || 0 === count($result)
102
        || (1 === count($result) && $entity === ($result instanceof \Iterator ? $result->current() : current($result)));
103
    }
104
105
    /**
106
     * @param string|null $manager
107
     * @param object $entity
108
     *
109
     * @return ObjectManager|null
110
     */
111
    private function getProperObjectManager(?string $manager, $entity): ?ObjectManager
112
    {
113
        if ($manager) {
114
            $objectManager = $this->registry->getManager($manager);
115
116
            $this->validateObjectManager($objectManager, sprintf('Object manager "%s" does not exist.', $manager));
117
        } else {
118
            $objectManager = $this->registry->getManagerForClass(get_class($entity));
119
120
            $this->validateObjectManager(
121
                $objectManager,
122
                sprintf(
123
                    'Unable to find the object manager associated with an entity of class "%s".',
124
                    get_class($entity)
125
                )
126
            );
127
        }
128
129
        return $objectManager;
130
    }
131
132
    /**
133
     * @param ObjectManager|null $objectManager
134
     * @param string $exceptionMessage
135
     *
136
     * @throws ConstraintDefinitionException
137
     */
138
    private function validateObjectManager(?ObjectManager $objectManager, string $exceptionMessage): void
139
    {
140
        if (!$objectManager) {
141
            throw new ConstraintDefinitionException($exceptionMessage);
142
        }
143
    }
144
145
    /**
146
     * @param ObjectManager $objectManager
147
     * @param object $entity
148
     * @param string $enabledPropertyPath
149
     *
150
     * @throws ConstraintDefinitionException
151
     */
152
    private function ensureEntityHasProvidedEnabledField(ObjectManager $objectManager, $entity, string $enabledPropertyPath): void
153
    {
154
        /** @var ClassMetadata $class */
155
        $class = $objectManager->getClassMetadata(get_class($entity));
156
157
        if (!$class->hasField($enabledPropertyPath) && !$class->hasAssociation($enabledPropertyPath)) {
158
            throw new ConstraintDefinitionException(
159
                sprintf("The field '%s' is not mapped by Doctrine, so it cannot be validated.", $enabledPropertyPath)
160
            );
161
        }
162
    }
163
}
164