Skip to content

Commit c75326b

Browse files
authored
Add casting for Finder. (#342)
Add casting for Finder filter
1 parent 27c0686 commit c75326b

20 files changed

+268
-40
lines changed

.phive/phars.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<phive xmlns="https://phar.io/phive">
3-
<phar name="phpstan" version="1.10.14" installed="1.10.14" location="./tools/phpstan" copy="false"/>
3+
<phar name="phpstan" version="1.10.59" installed="1.10.59" location="./tools/phpstan" copy="false"/>
44
<phar name="psalm" version="5.10.0" installed="5.10.0" location="./tools/psalm" copy="false"/>
55
</phive>

docs/filters-and-examples.md

+4
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,10 @@ The following options are supported by all filters except `Callback` and `Finder
238238

239239
- `options` (`array`, defaults to `[]`) Additional options to pass to the finder.
240240

241+
- `cast` (`array`, defaults to `[]`) Additional casts to be used on the (mapped
242+
field values. You can use `'int'`, `'bool'`, `'float'`, etc as strings. You can also
243+
use callable functions like `function ($value) { ... }` for more complex scenarios.
244+
241245
## Filtering by `belongsToMany` and `hasMany` associations
242246

243247
If you want to filter values related to a `belongsToMany` or `hasMany` association,

phpstan.neon

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
parameters:
2-
level: 7
2+
level: 8
33
checkMissingIterableValueType: false
44
checkGenericClassInNonGenericObjectType: false
55
paths:

src/Model/Filter/Base.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ abstract class Base
2020
/**
2121
* Default configuration.
2222
*
23-
* @var array
23+
* @var array<string, mixed>
2424
*/
2525
protected array $_defaultConfig = [];
2626

@@ -200,9 +200,9 @@ public function value(): mixed
200200
}
201201

202202
/**
203-
* @return array|string|null
203+
* @return mixed
204204
*/
205-
protected function passedValue(): string|array|null
205+
protected function passedValue(): mixed
206206
{
207207
if (!isset($this->_args[$this->name()])) {
208208
return null;

src/Model/Filter/Boolean.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Boolean extends Base
1010
/**
1111
* Default configuration.
1212
*
13-
* @var array
13+
* @var array<string, mixed>
1414
*/
1515
protected array $_defaultConfig = [
1616
'mode' => 'OR',

src/Model/Filter/Compare.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Compare extends Base
1010
/**
1111
* Default configuration.
1212
*
13-
* @var array
13+
* @var array<string, mixed>
1414
*/
1515
protected array $_defaultConfig = [
1616
'operator' => '>=',
@@ -20,7 +20,7 @@ class Compare extends Base
2020
/**
2121
* Allowed operators.
2222
*
23-
* @var array
23+
* @var array<string>
2424
*/
2525
protected array $_operators = [
2626
'>=', '<=', '<', '>',

src/Model/Filter/Escaper/DefaultEscaper.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class DefaultEscaper implements EscaperInterface
1212
/**
1313
* Default configuration.
1414
*
15-
* @var array
15+
* @var array<string, mixed>
1616
*/
1717
protected array $_defaultConfig = [
1818
'fromWildCardAny' => '%',

src/Model/Filter/Escaper/SqlserverEscaper.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class SqlserverEscaper extends DefaultEscaper
88
/**
99
* Default configuration.
1010
*
11-
* @var array
11+
* @var array<string, mixed>
1212
*/
1313
protected array $_defaultConfig = [
1414
'fromWildCardAny' => '%',

src/Model/Filter/Exists.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Exists extends Base
1010
/**
1111
* Default configuration.
1212
*
13-
* @var array
13+
* @var array<string, mixed>
1414
*/
1515
protected array $_defaultConfig = [
1616
'mode' => 'OR',

src/Model/Filter/Finder.php

+19-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33

44
namespace Search\Model\Filter;
55

6+
use Closure;
7+
68
class Finder extends Base
79
{
810
/**
9-
* @var array
11+
* @var array<string, mixed>
1012
*/
1113
protected array $_defaultConfig = [
1214
'map' => [],
1315
'options' => [],
16+
'cast' => [],
1417
];
1518

1619
/**
@@ -37,6 +40,21 @@ public function process(): bool
3740
foreach ($map as $to => $from) {
3841
$args[$to] = $args[$from] ?? null;
3942
}
43+
$casts = $this->getConfig('cast');
44+
foreach ($casts as $field => $toType) {
45+
$value = $args[$field] ?? null;
46+
if ($value === null) {
47+
continue;
48+
}
49+
50+
if ($toType instanceof Closure) {
51+
$value = $toType($value);
52+
} else {
53+
settype($value, $toType);
54+
}
55+
56+
$args[$field] = $value;
57+
}
4058

4159
$options = $this->getConfig('options');
4260
$args += $options;

src/Model/Filter/Like.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class Like extends Base
2222
/**
2323
* Default configuration.
2424
*
25-
* @var array
25+
* @var array<string, mixed>
2626
*/
2727
protected array $_defaultConfig = [
2828
'before' => false,

src/Model/Filter/Value.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Value extends Base
1212
/**
1313
* Default configuration.
1414
*
15-
* @var array
15+
* @var array<string, mixed>
1616
*/
1717
protected array $_defaultConfig = [
1818
'mode' => 'OR',

tests/TestApp/Model/Table/FinderArticlesTable.php

+36
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,40 @@ public function findSlugged(SelectQuery $query, string $slug): SelectQuery
4545
{
4646
return $query->where(['title' => $slug]);
4747
}
48+
49+
/**
50+
* Requires nullable slug key to be present in $options array.
51+
*
52+
* @param \Cake\ORM\Query\SelectQuery $query
53+
* @param string|null $slug
54+
* @return \Cake\ORM\Query\SelectQuery
55+
*/
56+
public function findSluggedNullable(SelectQuery $query, ?string $slug): SelectQuery
57+
{
58+
return $query->where(['title IS' => $slug]);
59+
}
60+
61+
/**
62+
* Requires uid key to be present in $options array.
63+
*
64+
* @param \Cake\ORM\Query\SelectQuery $query
65+
* @param int $uid
66+
* @return \Cake\ORM\Query\SelectQuery
67+
*/
68+
public function findUser(SelectQuery $query, int $uid): SelectQuery
69+
{
70+
return $query->where(['user_id' => $uid]);
71+
}
72+
73+
/**
74+
* Requires nullable uid key to be present in $options array.
75+
*
76+
* @param \Cake\ORM\Query\SelectQuery $query
77+
* @param int|null $uid
78+
* @return \Cake\ORM\Query\SelectQuery
79+
*/
80+
public function findUserNullable(SelectQuery $query, ?int $uid): SelectQuery
81+
{
82+
return $query->where(['user_id IS' => $uid]);
83+
}
4884
}

tests/TestCase/Model/Filter/BaseTest.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public function testConstructNonEmptyNameArgument($nonEmptyValue)
105105
$this->Manager,
106106
['fields' => 'fields']
107107
);
108-
$this->assertEquals($filter->name(), $nonEmptyValue);
108+
$this->assertSame($filter->name(), $nonEmptyValue);
109109
}
110110

111111
/**
@@ -144,13 +144,13 @@ public function testValue()
144144
);
145145

146146
$filter->setArgs(['fields' => 'value']);
147-
$this->assertEquals('value', $filter->value());
147+
$this->assertSame('value', $filter->value());
148148

149149
$filter->setArgs(['other_field' => 'value']);
150-
$this->assertEquals('default', $filter->value());
150+
$this->assertSame('default', $filter->value());
151151

152152
$filter->setArgs(['fields' => ['value1', 'value2']]);
153-
$this->assertEquals('default', $filter->value());
153+
$this->assertSame('default', $filter->value());
154154
}
155155

156156
/**

tests/TestCase/Model/Filter/BooleanTest.php

+13-13
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function testProcessWithFlagOn()
3131
'/WHERE Articles\.is_active = \:c0$/',
3232
$filter->getQuery()->sql()
3333
);
34-
$this->assertEquals(
34+
$this->assertSame(
3535
[true],
3636
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
3737
);
@@ -53,7 +53,7 @@ public function testProcessWithFlagOff()
5353
'/WHERE Articles\.is_active = \:c0$/',
5454
$filter->getQuery()->sql()
5555
);
56-
$this->assertEquals(
56+
$this->assertSame(
5757
[false],
5858
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
5959
);
@@ -75,7 +75,7 @@ public function testProcessWithStringFlagTrue()
7575
'/WHERE Articles\.is_active = \:c0$/',
7676
$filter->getQuery()->sql()
7777
);
78-
$this->assertEquals(
78+
$this->assertSame(
7979
[true],
8080
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
8181
);
@@ -97,7 +97,7 @@ public function testProcessWithStringFlagFalse()
9797
'/WHERE Articles\.is_active = \:c0$/',
9898
$filter->getQuery()->sql()
9999
);
100-
$this->assertEquals(
100+
$this->assertSame(
101101
[false],
102102
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
103103
);
@@ -119,7 +119,7 @@ public function testProcessWithBooleanFlagTrue()
119119
'/WHERE Articles\.is_active = \:c0$/',
120120
$filter->getQuery()->sql()
121121
);
122-
$this->assertEquals(
122+
$this->assertSame(
123123
[true],
124124
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
125125
);
@@ -141,7 +141,7 @@ public function testProcessWithBooleanFlagFalse()
141141
'/WHERE Articles\.is_active = \:c0$/',
142142
$filter->getQuery()->sql()
143143
);
144-
$this->assertEquals(
144+
$this->assertSame(
145145
[false],
146146
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
147147
);
@@ -163,7 +163,7 @@ public function testProcessWithStringFlag1()
163163
'/WHERE Articles\.is_active = \:c0$/',
164164
$filter->getQuery()->sql()
165165
);
166-
$this->assertEquals(
166+
$this->assertSame(
167167
[true],
168168
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
169169
);
@@ -185,7 +185,7 @@ public function testProcessWithStringFlag0()
185185
'/WHERE Articles\.is_active = \:c0$/',
186186
$filter->getQuery()->sql()
187187
);
188-
$this->assertEquals(
188+
$this->assertSame(
189189
[false],
190190
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
191191
);
@@ -207,7 +207,7 @@ public function testProcessWithIntegerFlag1()
207207
'/WHERE Articles\.is_active = \:c0$/',
208208
$filter->getQuery()->sql()
209209
);
210-
$this->assertEquals(
210+
$this->assertSame(
211211
[true],
212212
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
213213
);
@@ -226,7 +226,7 @@ public function testProcessWithIntegerFlag0()
226226
'/WHERE Articles\.is_active = \:c0$/',
227227
$filter->getQuery()->sql()
228228
);
229-
$this->assertEquals(
229+
$this->assertSame(
230230
[false],
231231
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
232232
);
@@ -285,7 +285,7 @@ public function testProcessMultiField()
285285
'/WHERE \(Articles\.is_active = :c0 OR Articles\.other = :c1\)$/',
286286
$filter->getQuery()->sql()
287287
);
288-
$this->assertEquals(
288+
$this->assertSame(
289289
[true, true],
290290
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
291291
);
@@ -310,7 +310,7 @@ public function testProcessMultiFieldWithAndMode()
310310
'/WHERE \(Articles\.is_active = :c0 AND Articles\.other = :c1\)$/',
311311
$filter->getQuery()->sql()
312312
);
313-
$this->assertEquals(
313+
$this->assertSame(
314314
[true, true],
315315
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
316316
);
@@ -332,7 +332,7 @@ public function testProcessDefaultFallbackForDisallowedMultiValue()
332332
'/WHERE Articles\.is_active = :c0$/',
333333
$filter->getQuery()->sql()
334334
);
335-
$this->assertEquals(
335+
$this->assertSame(
336336
[true],
337337
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
338338
);

tests/TestCase/Model/Filter/CallbackTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public function testProcess()
3636
'/WHERE title = \:c0$/',
3737
$filter->getQuery()->sql()
3838
);
39-
$this->assertEquals(
39+
$this->assertSame(
4040
['test'],
4141
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
4242
);

tests/TestCase/Model/Filter/ExistsTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public function testProcessWithFlagOnNotNullable()
7474
'/WHERE Articles\.number != \:c0$/',
7575
$filter->getQuery()->sql()
7676
);
77-
$this->assertEquals(
77+
$this->assertSame(
7878
[''],
7979
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
8080
);
@@ -100,7 +100,7 @@ public function testProcessWithFlagOffNotNullable()
100100
'/WHERE Articles\.number = \:c0$/',
101101
$filter->getQuery()->sql()
102102
);
103-
$this->assertEquals(
103+
$this->assertSame(
104104
[''],
105105
Hash::extract($filter->getQuery()->getValueBinder()->bindings(), '{s}.value')
106106
);

0 commit comments

Comments
 (0)