Passed
Push — master ( 74d060...d17a79 )
by Kirill
05:31 queued 10s
created

EntityChecker::getCaseInsensitiveSelect()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
c 0
b 0
f 0
nc 1
nop 3
dl 0
loc 9
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Validation\Checker;
6
7
use Cycle\ORM\ORMInterface;
8
use Cycle\ORM\Select;
9
use Cycle\ORM\Select\Repository;
10
use Spiral\Core\Container\SingletonInterface;
11
use Spiral\Database\Injection\Expression;
12
use Spiral\Validation\AbstractChecker;
13
14
class EntityChecker extends AbstractChecker implements SingletonInterface
15
{
16
    /**
17
     * {@inheritdoc}
18
     */
19
    public const MESSAGES = [
20
        'exists' => '[[Entity not exists.]]',
21
        'unique' => '[[Value should be unique.]]',
22
    ];
23
24
    /** @var ORMInterface */
25
    private $orm;
26
27
    /**
28
     * @param ORMInterface $orm
29
     */
30
    public function __construct(ORMInterface $orm)
31
    {
32
        $this->orm = $orm;
33
    }
34
35
    /**
36
     * @param string|int  $value
37
     * @param string      $role
38
     * @param string|null $field
39
     * @param bool        $ignoreCase
40
     * @return bool
41
     */
42
    public function exists($value, string $role, ?string $field = null, bool $ignoreCase = false): bool
43
    {
44
        $repository = $this->orm->getRepository($role);
45
        if ($field === null) {
46
            return $repository->findByPK($value) !== null;
47
        }
48
49
        if ($ignoreCase && is_string($value) && $repository instanceof Repository) {
50
            return $this
51
                ->getCaseInsensitiveSelect($repository, $field, $value)
52
                ->fetchOne() !== null;
53
        }
54
55
        return $repository->findOne([$field => $value]) !== null;
56
    }
57
58
    /**
59
     * @param mixed    $value
60
     * @param string   $role
61
     * @param string   $field
62
     * @param string[] $withFields
63
     * @param bool     $ignoreCase
64
     * @return bool
65
     */
66
    public function unique($value, string $role, string $field, array $withFields = [], bool $ignoreCase = false): bool
67
    {
68
        $values = $this->withValues($withFields);
69
        $values[$field] = $value;
70
71
        if ($this->isProvidedByContext($role, $values)) {
72
            return true;
73
        }
74
75
        $repository = $this->orm->getRepository($role);
76
77
        if ($ignoreCase && is_string($value) && $repository instanceof Repository) {
78
            return $this
79
                ->getCaseInsensitiveSelect($repository, $field, $value)
80
                ->fetchOne() === null;
81
        }
82
83
        return $repository->findOne($values) === null;
84
    }
85
86
    /**
87
     * @param string[] $fields
88
     * @return array
89
     */
90
    private function withValues(array $fields): array
91
    {
92
        $values = [];
93
        foreach ($fields as $field) {
94
            if ($this->getValidator()->hasValue($field)) {
95
                $values[$field] = $this->getValidator()->getValue($field);
96
            }
97
        }
98
99
        return $values;
100
    }
101
102
    /**
103
     * @param string $role
104
     * @param array  $values
105
     * @return bool
106
     */
107
    private function isProvidedByContext(string $role, array $values): bool
108
    {
109
        $entity = $this->getValidator()->getContext();
110
        if (!is_object($entity) || !$this->orm->getHeap()->has($entity)) {
111
            return false;
112
        }
113
114
        $extract = $this->orm->getMapper($role)->extract($entity);
115
        foreach ($values as $field => $value) {
116
            if (!isset($extract[$field]) || $extract[$field] !== $value) {
117
                return false;
118
            }
119
        }
120
121
        return true;
122
    }
123
124
    /**
125
     * @param Repository $repository
126
     * @param string $field
127
     * @param string $value
128
     * @return Select
129
     */
130
    private function getCaseInsensitiveSelect(Repository $repository, string $field, string $value): Select
131
    {
132
        $select = $repository->select();
133
        $queryBuilder = $select->getBuilder();
134
135
        return $select
136
            ->where(
137
                new Expression("LOWER({$queryBuilder->resolve($field)})"),
138
                mb_strtolower($value)
139
            )
140
        ;
141
    }
142
}
143