ComparisonTrait::isFalse()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace Helix\DB\Fluent\Value;
4
5
use Closure;
6
use Helix\DB\EntityInterface;
7
use Helix\DB\Fluent\AbstractTrait;
8
use Helix\DB\Fluent\Branch;
9
use Helix\DB\Fluent\Predicate;
10
use Helix\DB\Fluent\ValueInterface;
11
use Helix\DB\Select;
12
13
/**
14
 * Comparative functions.
15
 *
16
 * Because SQLite doesn't have the `ANY`/`ALL` comparison operators,
17
 * subqueries are instead nested and correlated using `EXISTS` or `NOT EXISTS`,
18
 * which requires the first column of the subquery to have a name or alias so it's referable.
19
 */
20
trait ComparisonTrait
21
{
22
23
    use AbstractTrait;
24
25
    /**
26
     * Relational comparison helper.
27
     *
28
     * @param number|string|Select $arg
29
     * @param string $oper Relational operator
30
     * @param string $multi `ALL|ANY`
31
     * @return Predicate
32
     * @internal
33
     */
34
    protected function _isRelational($arg, string $oper, string $multi)
35
    {
36
        static $inverse = [
37
            '<' => '>=',
38
            '<=' => '>',
39
            '>=' => '<',
40
            '>' => '<='
41
        ];
42
        if ($arg instanceof Select) {
43
            if ($this->db->isSQLite()) {
44
                $sub = Select::factory($this->db, $arg, [$arg[0]]);
45
                if ($multi === 'ANY') {
46
                    return $sub->where("{$this} {$oper} {$arg[0]}")->isNotEmpty();
47
                }
48
                return $sub->where("{$this} {$inverse[$oper]} {$arg[0]}")->isEmpty();
49
            }
50
            return Predicate::factory($this->db, "{$this} {$oper} {$multi} ({$arg->toSql()})");
51
        }
52
        return Predicate::factory($this->db, "{$this} {$oper} {$this->db->quote($arg)}");
53
    }
54
55
    /**
56
     * Null-safe type-strict equality.
57
     *
58
     * - Mysql: `$this <=> $arg`, or `$this <=> ANY ($arg)`
59
     * - SQLite: `$this IS $arg`, or `EXISTS (... WHERE $this IS $arg[0])`
60
     *
61
     * @param null|scalar|EntityInterface|Select|ValueInterface $arg
62
     * @return Predicate
63
     */
64
    public function is($arg): Predicate
65
    {
66
        if ($arg instanceof Select) {
67
            if ($this->db->isSQLite()) {
68
                return Select::factory($this->db, $arg, [$arg[0]])
69
                    ->where("{$this} IS {$arg[0]}")
70
                    ->isNotEmpty();
71
            }
72
            return Predicate::factory($this->db, "{$this} <=> ANY ({$arg->toSql()})");
73
        }
74
        if ($arg === null or is_bool($arg)) {
75
            $arg = ['' => 'NULL', 1 => 'TRUE', 0 => 'FALSE'][$arg];
76
        } else {
77
            $arg = $this->db->quote($arg);
78
        }
79
        if ($this->db->isMySQL()) {
80
            return Predicate::factory($this->db, "{$this} <=> {$arg}");
81
        }
82
        return Predicate::factory($this->db, "{$this} IS {$arg}");
83
    }
84
85
    /**
86
     * `$this BETWEEN $min AND $max` (inclusive)
87
     *
88
     * @param number $min
89
     * @param number $max
90
     * @return Predicate
91
     */
92
    public function isBetween($min, $max)
93
    {
94
        $min = $this->db->quote($min);
95
        $max = $this->db->quote($max);
96
        return Predicate::factory($this->db, "{$this} BETWEEN {$min} AND {$max}");
97
    }
98
99
    /**
100
     * See {@link Predicate::match()}
101
     *
102
     * @param null|scalar|array|Closure|EntityInterface|Select|ValueInterface $arg
103
     * @return Predicate
104
     */
105
    public function isEqual($arg)
106
    {
107
        return Predicate::match($this->db, $this, $arg);
108
    }
109
110
    /**
111
     * `$this IS FALSE`
112
     *
113
     * @return Predicate
114
     */
115
    public function isFalse()
116
    {
117
        return Predicate::factory($this->db, "{$this} IS FALSE");
118
    }
119
120
    /**
121
     * `$this > $arg`, or driver-appropriate `$this > ALL (SELECT ...)`
122
     *
123
     * @param number|string|Select $arg
124
     * @return Predicate
125
     */
126
    public function isGt($arg)
127
    {
128
        return $this->_isRelational($arg, '>', 'ALL');
129
    }
130
131
    /**
132
     * Driver-appropriate `$this > ANY (SELECT ...)`
133
     *
134
     * @param Select $select
135
     * @return Predicate
136
     */
137
    public function isGtAny(Select $select)
138
    {
139
        return $this->_isRelational($select, '>', 'ANY');
140
    }
141
142
    /**
143
     * `$this >= $arg`, or driver-appropriate `$this >= ALL (SELECT ...)`
144
     *
145
     * @param number|string|Select $arg
146
     * @return Predicate
147
     */
148
    public function isGte($arg)
149
    {
150
        return $this->_isRelational($arg, '>=', 'ALL');
151
    }
152
153
    /**
154
     * Driver-appropriate `$this >= ANY (SELECT ...)`
155
     *
156
     * @param Select $select
157
     * @return Predicate
158
     */
159
    public function isGteAny(Select $select)
160
    {
161
        return $this->_isRelational($select, '>=', 'ANY');
162
    }
163
164
    /**
165
     * `$this LIKE $pattern`
166
     *
167
     * @param string $pattern
168
     * @return Predicate
169
     */
170
    public function isLike(string $pattern)
171
    {
172
        $pattern = $this->db->quote($pattern);
173
        return Predicate::factory($this->db, "{$this} LIKE {$pattern}");
174
    }
175
176
    /**
177
     * `$this < $arg`, or driver-appropriate `$this < ALL (SELECT ...)`
178
     *
179
     * @param number|string|Select $arg
180
     * @return Predicate
181
     */
182
    public function isLt($arg)
183
    {
184
        return $this->_isRelational($arg, '<', 'ALL');
185
    }
186
187
    /**
188
     * Driver-appropriate `$this < ANY (SELECT ...)`
189
     *
190
     * @param Select $select
191
     * @return Predicate
192
     */
193
    public function isLtAny(Select $select)
194
    {
195
        return $this->_isRelational($select, '<', 'ANY');
196
    }
197
198
    /**
199
     * `$this <= $arg`, or driver-appropriate `$this <= ALL (SELECT ...)`
200
     *
201
     * @param number|string|Select $arg
202
     * @return Predicate
203
     */
204
    public function isLte($arg)
205
    {
206
        return $this->_isRelational($arg, '<=', 'ALL');
207
    }
208
209
    /**
210
     * Driver-appropriate `$this <= ANY (SELECT ...)`
211
     *
212
     * @param Select $select
213
     * @return Predicate
214
     */
215
    public function isLteAny(Select $select)
216
    {
217
        return $this->_isRelational($select, '<=', 'ANY');
218
    }
219
220
    /**
221
     * Null-safe type-strict inequality.
222
     *
223
     * @param null|scalar|EntityInterface|Select|ValueInterface $arg
224
     * @return Predicate
225
     */
226
    public function isNot($arg)
227
    {
228
        return $this->is($arg)->lNot();
229
    }
230
231
    /**
232
     * `$this NOT BETWEEN $min AND $max` (inclusive)
233
     *
234
     * @param number $min
235
     * @param number $max
236
     * @return Predicate
237
     */
238
    public function isNotBetween($min, $max)
239
    {
240
        $min = $this->db->quote($min);
241
        $max = $this->db->quote($max);
242
        return Predicate::factory($this->db, "{$this} NOT BETWEEN {$min} AND {$max}");
243
    }
244
245
    /**
246
     * `$this <> $arg` or `$this NOT IN ($arg)`
247
     *
248
     * @param null|scalar|array|EntityInterface|Select|ValueInterface $arg
249
     * @return Predicate
250
     */
251
    public function isNotEqual($arg)
252
    {
253
        if ($arg instanceof Select) {
254
            return Predicate::factory($this->db, "{$this} NOT IN ({$arg->toSql()})");
255
        }
256
        if (is_array($arg)) {
257
            return Predicate::factory($this->db, "{$this} NOT IN ({$this->db->quoteList($arg)})");
258
        }
259
        return Predicate::factory($this->db, "{$this} <> {$this->db->quote($arg)}");
260
    }
261
262
    /**
263
     * `$this NOT LIKE $pattern`
264
     *
265
     * @param string $pattern
266
     * @return Predicate
267
     */
268
    public function isNotLike(string $pattern)
269
    {
270
        $pattern = $this->db->quote($pattern);
271
        return Predicate::factory($this->db, "{$this} NOT LIKE {$pattern}");
272
    }
273
274
    /**
275
     * `$this IS NOT NULL`
276
     *
277
     * @return Predicate
278
     */
279
    public function isNotNull()
280
    {
281
        return Predicate::factory($this->db, "{$this} IS NOT NULL");
282
    }
283
284
    /**
285
     * `$this NOT REGEXP $pattern`
286
     *
287
     * @param string $pattern
288
     * @return Predicate
289
     */
290
    public function isNotRegExp(string $pattern)
291
    {
292
        $pattern = $this->db->quote($pattern);
293
        return Predicate::factory($this->db, "{$this} NOT REGEXP {$pattern}");
294
    }
295
296
    /**
297
     * `$this IS NULL`
298
     *
299
     * @return Predicate
300
     */
301
    public function isNull()
302
    {
303
        return Predicate::factory($this->db, "{$this} IS NULL");
304
    }
305
306
    /**
307
     * `$this REGEXP $pattern`
308
     *
309
     * @param string $pattern
310
     * @return Predicate
311
     */
312
    public function isRegExp(string $pattern)
313
    {
314
        $pattern = $this->db->quote($pattern);
315
        return Predicate::factory($this->db, "{$this} REGEXP {$pattern}");
316
    }
317
318
    /**
319
     * `CASE $this ... END`
320
     *
321
     * > :warning: If `$values` are given, the keys are quoted as literal values.
322
     * > Omit `$values` and use {@link Branch::when()} if you need expressions for the `WHEN` clause.
323
     *
324
     * @param array $values `[when => then]`
325
     * @return Branch
326
     */
327
    public function switch(array $values = [])
328
    {
329
        return Branch::factory($this->db, "{$this}", $values);
330
    }
331
}
332