diff --git a/CHANGELOG.md b/CHANGELOG.md index 4912bf20..41a77911 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ `PaginatorInterface::getCurrentPageSize()` and `OffsetPaginator::getTotalItems()` (@vjik) - Chg #165: Simplify `FilterInterface` and `FilterHandlerInterface` (@vjik) - Chg #166: Remove `EqualsEmpty` filter (@vjik) +- Bug #173: Make `Like` filter case-sensitive (@arogachev) +- New #173: Add `ILike` filter (case-insensitive variation of `Like` filter) (@arogachev) ## 1.0.1 January 25, 2023 diff --git a/README.md b/README.md index 0f459ac1..13022ccf 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,7 @@ Filter could be composed with: - `EqualsNull` - `GreaterThan` - `GreaterThanOrEqual` +- `ILike` - `In` - `LessThan` - `LessThanOrEqual` diff --git a/composer.json b/composer.json index 8cef1c22..cb55fae2 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ ], "require": { "php": "^8.1", + "ext-mbstring": "*", "yiisoft/arrays": "^3.0" }, "require-dev": { diff --git a/src/Reader/Filter/ILike.php b/src/Reader/Filter/ILike.php new file mode 100644 index 00000000..3cabf898 --- /dev/null +++ b/src/Reader/Filter/ILike.php @@ -0,0 +1,33 @@ +field; + } + + public function getValue(): string + { + return $this->value; + } +} diff --git a/src/Reader/Filter/Like.php b/src/Reader/Filter/Like.php index fc6781f1..ffd79801 100644 --- a/src/Reader/Filter/Like.php +++ b/src/Reader/Filter/Like.php @@ -7,7 +7,7 @@ use Yiisoft\Data\Reader\FilterInterface; /** - * `Like` filter defines a criteria for ensuring field value is like-match to a given value. + * `Like` filter defines a criteria for ensuring field value is like-match to a given value (case-sensitive). */ final class Like implements FilterInterface { diff --git a/src/Reader/Iterable/FilterHandler/ILikeHandler.php b/src/Reader/Iterable/FilterHandler/ILikeHandler.php new file mode 100644 index 00000000..65ff00b1 --- /dev/null +++ b/src/Reader/Iterable/FilterHandler/ILikeHandler.php @@ -0,0 +1,33 @@ +getField()); + + /** @infection-ignore-all MBString No suitable test case was found yet. */ + return is_string($itemValue) && mb_stripos($itemValue, $filter->getValue()) !== false; + } +} diff --git a/src/Reader/Iterable/FilterHandler/LikeHandler.php b/src/Reader/Iterable/FilterHandler/LikeHandler.php index 5dac7d36..05eb5621 100644 --- a/src/Reader/Iterable/FilterHandler/LikeHandler.php +++ b/src/Reader/Iterable/FilterHandler/LikeHandler.php @@ -10,10 +10,9 @@ use Yiisoft\Data\Reader\Iterable\IterableFilterHandlerInterface; use function is_string; -use function stripos; /** - * `Like` iterable filter handler ensures that the field value is like-match to a given value. + * `Like` iterable filter handler ensures that the field value is like-match to a given value (case-sensitive). */ final class LikeHandler implements IterableFilterHandlerInterface { @@ -28,6 +27,7 @@ public function match(object|array $item, FilterInterface $filter, array $iterab $itemValue = ArrayHelper::getValue($item, $filter->getField()); - return is_string($itemValue) && stripos($itemValue, $filter->getValue()) !== false; + /** @infection-ignore-all MBString No suitable test case was found yet. */ + return is_string($itemValue) && mb_strpos($itemValue, $filter->getValue()) !== false; } } diff --git a/src/Reader/Iterable/IterableDataReader.php b/src/Reader/Iterable/IterableDataReader.php index 81cb90e1..211d5c6e 100644 --- a/src/Reader/Iterable/IterableDataReader.php +++ b/src/Reader/Iterable/IterableDataReader.php @@ -20,6 +20,7 @@ use Yiisoft\Data\Reader\Iterable\FilterHandler\EqualsNullHandler; use Yiisoft\Data\Reader\Iterable\FilterHandler\GreaterThanHandler; use Yiisoft\Data\Reader\Iterable\FilterHandler\GreaterThanOrEqualHandler; +use Yiisoft\Data\Reader\Iterable\FilterHandler\ILikeHandler; use Yiisoft\Data\Reader\Iterable\FilterHandler\InHandler; use Yiisoft\Data\Reader\Iterable\FilterHandler\LessThanHandler; use Yiisoft\Data\Reader\Iterable\FilterHandler\LessThanOrEqualHandler; @@ -73,6 +74,7 @@ public function __construct(private iterable $data) new EqualsNullHandler(), new GreaterThanHandler(), new GreaterThanOrEqualHandler(), + new ILikeHandler(), new InHandler(), new LessThanHandler(), new LessThanOrEqualHandler(), diff --git a/tests/Reader/IterableDataReaderTest.php b/tests/Reader/IterableDataReaderTest.php index 74069b37..112527de 100644 --- a/tests/Reader/IterableDataReaderTest.php +++ b/tests/Reader/IterableDataReaderTest.php @@ -14,6 +14,7 @@ use Yiisoft\Data\Reader\Filter\Equals; use Yiisoft\Data\Reader\Filter\GreaterThan; use Yiisoft\Data\Reader\Filter\GreaterThanOrEqual; +use Yiisoft\Data\Reader\Filter\ILike; use Yiisoft\Data\Reader\Filter\In; use Yiisoft\Data\Reader\Filter\LessThan; use Yiisoft\Data\Reader\Filter\LessThanOrEqual; @@ -264,7 +265,19 @@ public function testInFiltering(): void public function testLikeFiltering(): void { - $filter = new Like('name', 'agent'); + $filter = new Like('name', 'Agent'); + $reader = (new IterableDataReader(self::DEFAULT_DATASET)) + ->withFilter($filter); + + $this->assertSame([ + 2 => self::ITEM_3, + 3 => self::ITEM_4, + ], $reader->read()); + } + + public function testILikeFiltering(): void + { + $filter = new ILike('name', 'agent'); $reader = (new IterableDataReader(self::DEFAULT_DATASET)) ->withFilter($filter); @@ -307,7 +320,7 @@ public function testAllFiltering(): void { $filter = new All( new GreaterThan('id', 3), - new Like('name', 'agent') + new Like('name', 'Agent') ); $reader = (new IterableDataReader(self::DEFAULT_DATASET)) ->withFilter($filter); @@ -343,7 +356,7 @@ public function testFilteredCount(): void $this->assertSame(5, $total, 'Wrong count of elements'); - $reader = $reader->withFilter(new Like('name', 'agent')); + $reader = $reader->withFilter(new Like('name', 'Agent')); $totalAgents = count($reader); $this->assertSame(2, $totalAgents, 'Wrong count of filtered elements'); } diff --git a/tests/Reader/IterableHandler/ILikeTest.php b/tests/Reader/IterableHandler/ILikeTest.php new file mode 100644 index 00000000..24eef62e --- /dev/null +++ b/tests/Reader/IterableHandler/ILikeTest.php @@ -0,0 +1,32 @@ + 1, 'value' => 'Great Cat Fighter'], 'value', 'Great Cat Fighter'], + [true, ['id' => 1, 'value' => 'Great Cat Fighter'], 'value', 'Cat'], + [true, ['id' => 1, 'value' => 'Great Cat Fighter'], 'value', 'cat'], + [false, ['id' => 1, 'value' => 'Great Cat Fighter'], 'id', '1'], + [true, ['id' => 1, 'value' => '๐Ÿ™๐Ÿ™‚๐Ÿ™'], 'value', '๐Ÿ™‚'], + [true, ['id' => 1, 'value' => 'ะŸั€ะธะฒะตั‚ ะผะธั€'], 'value', ' '], + ]; + } + + #[DataProvider('matchDataProvider')] + public function testMatch(bool $expected, array $item, string $field, string $value): void + { + $processor = new ILikeHandler(); + $this->assertSame($expected, $processor->match($item, new ILike($field, $value), [])); + } +} diff --git a/tests/Reader/IterableHandler/LikeTest.php b/tests/Reader/IterableHandler/LikeTest.php index f5a943f6..4bed7caf 100644 --- a/tests/Reader/IterableHandler/LikeTest.php +++ b/tests/Reader/IterableHandler/LikeTest.php @@ -14,22 +14,19 @@ final class LikeTest extends TestCase public static function matchDataProvider(): array { return [ - [true, 'value', 'Great Cat Fighter'], - [true, 'value', 'Cat'], - [false, 'id', '1'], + [true, ['id' => 1, 'value' => 'Great Cat Fighter'], 'value', 'Great Cat Fighter'], + [true, ['id' => 1, 'value' => 'Great Cat Fighter'], 'value', 'Cat'], + [false, ['id' => 1, 'value' => 'Great Cat Fighter'], 'value', 'cat'], + [false, ['id' => 1, 'value' => 'Great Cat Fighter'], 'id', '1'], + [true, ['id' => 1, 'value' => '๐Ÿ™๐Ÿ™‚๐Ÿ™'], 'value', '๐Ÿ™‚'], + [true, ['id' => 1, 'value' => 'ะŸั€ะธะฒะตั‚ ะผะธั€'], 'value', ' '], ]; } #[DataProvider('matchDataProvider')] - public function testMatch(bool $expected, string $field, string $value): void + public function testMatch(bool $expected, array $item, string $field, string $value): void { $processor = new LikeHandler(); - - $item = [ - 'id' => 1, - 'value' => 'Great Cat Fighter', - ]; - $this->assertSame($expected, $processor->match($item, new Like($field, $value), [])); } }