Completed
Push — 2.0 ( d6b5ff...33bb83 )
by Peter
08:35 queued 11s
created

Comparison::resolveContext()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 4
nc 3
nop 1
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * This file is part of the Happyr Doctrine Specification package.
6
 *
7
 * (c) Tobias Nyholm <[email protected]>
8
 *     Kacper Gunia <[email protected]>
9
 *     Peter Gribanov <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Happyr\DoctrineSpecification\Filter;
16
17
use Doctrine\ORM\Query\Expr\Comparison as DoctrineComparison;
18
use Doctrine\ORM\QueryBuilder;
19
use Happyr\DoctrineSpecification\Exception\InvalidArgumentException;
20
use Happyr\DoctrineSpecification\Operand\ArgumentToOperandConverter;
21
use Happyr\DoctrineSpecification\Operand\Operand;
22
23
/**
24
 * Comparison class.
25
 *
26
 * This is used when you need to compare two values
27
 */
28
abstract class Comparison implements Filter, Satisfiable
29
{
30
    protected const EQ = '=';
31
32
    protected const NEQ = '<>';
33
34
    protected const LT = '<';
35
36
    protected const LTE = '<=';
37
38
    protected const GT = '>';
39
40
    protected const GTE = '>=';
41
42
    /**
43
     * @var Operand|string
44
     */
45
    private $field;
46
47
    /**
48
     * @var Operand|string
49
     */
50
    private $value;
51
52
    /**
53
     * @var string|null
54
     */
55
    private $context;
56
57
    /**
58
     * @var string[]
59
     */
60
    private static $operators = [
61
        self::EQ, self::NEQ,
62
        self::LT, self::LTE,
63
        self::GT, self::GTE,
64
    ];
65
66
    /**
67
     * @var string
68
     */
69
    private $operator;
70
71
    /**
72
     * Make sure the $field has a value equals to $value.
73
     *
74
     * @param string         $operator
75
     * @param Operand|string $field
76
     * @param Operand|mixed  $value
77
     * @param string|null    $context
78
     *
79
     * @throws InvalidArgumentException
80
     */
81
    public function __construct(string $operator, $field, $value, ?string $context = null)
82
    {
83
        if (!in_array($operator, self::$operators, true)) {
84
            throw new InvalidArgumentException(sprintf(
85
                '"%s" is not a valid comparison operator. Valid operators are: "%s"',
86
                $operator,
87
                implode(', ', self::$operators)
88
            ));
89
        }
90
91
        $this->operator = $operator;
92
        $this->field = $field;
93
        $this->value = $value;
94
        $this->context = $context;
95
    }
96
97
    /**
98
     * @param QueryBuilder $qb
99
     * @param string       $context
100
     *
101
     * @return string
102
     */
103
    public function getFilter(QueryBuilder $qb, string $context): string
104
    {
105
        if (null !== $this->context) {
106
            $context = sprintf('%s.%s', $context, $this->context);
107
        }
108
109
        $field = ArgumentToOperandConverter::toField($this->field);
110
        $value = ArgumentToOperandConverter::toValue($this->value);
111
112
        return (string) new DoctrineComparison(
113
            $field->transform($qb, $context),
114
            $this->operator,
115
            $value->transform($qb, $context)
116
        );
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    public function filterCollection(iterable $collection, ?string $context = null): iterable
123
    {
124
        $context = $this->resolveContext($context);
125
        $field = ArgumentToOperandConverter::toField($this->field);
126
        $value = ArgumentToOperandConverter::toValue($this->value);
127
128
        foreach ($collection as $candidate) {
129
            if ($this->compare($field->execute($candidate, $context), $value->execute($candidate, $context))) {
130
                yield $candidate;
131
            }
132
        }
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function isSatisfiedBy($candidate, ?string $context = null): bool
139
    {
140
        $context = $this->resolveContext($context);
141
        $field = ArgumentToOperandConverter::toField($this->field);
142
        $value = ArgumentToOperandConverter::toValue($this->value);
143
144
        return $this->compare($field->execute($candidate, $context), $value->execute($candidate, $context));
0 ignored issues
show
Bug introduced by
It seems like $candidate defined by parameter $candidate on line 138 can also be of type array; however, Happyr\DoctrineSpecifica...rand\Operand::execute() does only seem to accept array<integer,*>|object, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
145
    }
146
147
    /**
148
     * @param string|null $context
149
     *
150
     * @return string|null
151
     */
152
    private function resolveContext(?string $context): ?string
153
    {
154
        if (null !== $this->context && null !== $context) {
155
            return sprintf('%s.%s', $context, $this->context);
156
        }
157
158
        if (null !== $this->context) {
159
            return $this->context;
160
        }
161
162
        return $context;
163
    }
164
165
    /**
166
     * @param mixed $field
167
     * @param mixed $value
168
     *
169
     * @return bool
170
     */
171
    abstract protected function compare($field, $value): bool;
172
}
173