From 3bbabb3576aec44da65856e675fa0ebd1ddb66dd Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sun, 4 Aug 2024 15:18:02 +0200 Subject: [PATCH] TASK: Introduce array support for SearchTermMatcher --- .../Dto/SerializedPropertyValue.php | 2 - .../Filter/SearchTerm/SearchTermMatcher.php | 24 +++++++---- .../SearchTerm/SearchTermMatcherTest.php | 40 ++++++++++++++++--- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Dto/SerializedPropertyValue.php b/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Dto/SerializedPropertyValue.php index 650652adb12..6df6fcd1d6d 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Dto/SerializedPropertyValue.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Dto/SerializedPropertyValue.php @@ -38,8 +38,6 @@ private function __construct( } /** - * If the value is NULL an unset-property instruction will be returned instead. - * * @param Value $value */ public static function create( diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/SearchTerm/SearchTermMatcher.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/SearchTerm/SearchTermMatcher.php index b24cce52777..4781cff6bf9 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/SearchTerm/SearchTermMatcher.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/SearchTerm/SearchTermMatcher.php @@ -23,22 +23,32 @@ public static function matchesNode(Node $node, SearchTerm $searchTerm): bool public static function matchesSerializedPropertyValues(SerializedPropertyValues $serializedPropertyValues, SearchTerm $searchTerm): bool { foreach ($serializedPropertyValues as $serializedPropertyValue) { - if (self::matchesSerializedPropertyValue($serializedPropertyValue, $searchTerm)) { + if (self::matchesValue($serializedPropertyValue->value, $searchTerm)) { return true; } } return false; } - private static function matchesSerializedPropertyValue(SerializedPropertyValue $serializedPropertyValue, SearchTerm $searchTerm): bool + private static function matchesValue(mixed $value, SearchTerm $searchTerm): bool { + if (is_array($value) || $value instanceof \ArrayObject) { + foreach ($value as $subValue) { + if (self::matchesValue($subValue, $searchTerm)) { + return true; + } + } + return false; + } + return match (true) { - is_string($serializedPropertyValue->value) => mb_stripos($serializedPropertyValue->value, $searchTerm->term) !== false, + is_string($value) => mb_stripos($value, $searchTerm->term) !== false, // the following behaviour might seem odd, but is implemented after how the database filtering should behave - is_int($serializedPropertyValue->value), - is_float($serializedPropertyValue->value) => str_contains((string)$serializedPropertyValue->value, $searchTerm->term), - $serializedPropertyValue->value === true => $searchTerm->term === 'true', - $serializedPropertyValue->value === false => $searchTerm->term === 'false' + is_int($value), + is_float($value) => str_contains((string)$value, $searchTerm->term), + $value === true => $searchTerm->term === 'true', + $value === false => $searchTerm->term === 'false', + default => throw new \InvalidArgumentException(sprintf('Handling for type %s is not implemented.', get_debug_type($value))), }; } } diff --git a/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/Filter/SearchTerm/SearchTermMatcherTest.php b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/Filter/SearchTerm/SearchTermMatcherTest.php index e3c64afb46f..212319a15bf 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/Filter/SearchTerm/SearchTermMatcherTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/Filter/SearchTerm/SearchTermMatcherTest.php @@ -12,7 +12,7 @@ class SearchTermMatcherTest extends TestCase { - public function matchingStringComparisonExamples(): iterable + public static function matchingStringComparisonExamples(): iterable { yield 'string found inside string' => ['brown', self::value('the brown fox')]; yield 'string found inside string (ci)' => ['BrOWn', self::value('the brown fox')]; @@ -25,7 +25,7 @@ public function matchingStringComparisonExamples(): iterable yield 'string found inside string with special chars' => ['ä b-c+#@', self::value('the example: "ä b-c+#@"')]; } - public function matchingNumberLikeComparisonExamples(): iterable + public static function matchingNumberLikeComparisonExamples(): iterable { yield 'string-number found inside string' => [ '22', @@ -58,7 +58,7 @@ public function matchingNumberLikeComparisonExamples(): iterable ]; } - public function matchingBooleanLikeComparisonExamples(): iterable + public static function matchingBooleanLikeComparisonExamples(): iterable { yield 'string-boolean inside string' => [ 'true', @@ -76,6 +76,32 @@ public function matchingBooleanLikeComparisonExamples(): iterable ]; } + public static function matchingArrayComparisonExamples(): iterable + { + // automates the following: + yield 'inside array: string found inside string' => ['foo', self::value(['foo'])]; + yield 'inside array-object: string found inside string' => ['foo', self::value(new \ArrayObject(['foo']))]; + + foreach([ + ...iterator_to_array(self::matchingStringComparisonExamples()), + ...iterator_to_array(self::matchingNumberLikeComparisonExamples()), + ...iterator_to_array(self::matchingBooleanLikeComparisonExamples()), + ] as $name => [$searchTerm, $properties]) { + /** @var SerializedPropertyValues $properties */ + yield 'inside nested array: ' . $name => [$searchTerm, SerializedPropertyValues::fromArray( + array_map( + fn (SerializedPropertyValue $value) => SerializedPropertyValue::create( + // arbitrary deep nested + [[$value->value]], + 'array' + ), + iterator_to_array($properties) + ) + )]; + } + } + + public function notMatchingExamples(): iterable { yield 'different chars' => ['aepfel', self::value('äpfel')]; @@ -83,6 +109,9 @@ public function notMatchingExamples(): iterable yield 'string not found inside string' => ['reptv', self::value('eras tour')]; yield 'integer' => ['0999', self::value(999)]; yield 'float with comma' => ['12,45', self::value(12.34)]; + yield 'array with unmatched string' => ['hello', self::value(['hi'])]; + yield 'array key is not considered matching' => ['key', self::value(['key' => 'foo'])]; + yield 'nested array key is not considered matching' => ['key', self::value([['key' => 'foo']])]; } /** @@ -90,6 +119,7 @@ public function notMatchingExamples(): iterable * @dataProvider matchingStringComparisonExamples * @dataProvider matchingNumberLikeComparisonExamples * @dataProvider matchingBooleanLikeComparisonExamples + * @dataProvider matchingArrayComparisonExamples */ public function searchTermMatchesProperties( string $searchTerm, @@ -119,12 +149,12 @@ public function searchTermDoesntMatchesProperties( ); } - private static function value(string|bool|float|int $value): SerializedPropertyValues + private static function value(string|bool|float|int|array|\ArrayObject $value): SerializedPropertyValues { return SerializedPropertyValues::fromArray([ 'test-property' => SerializedPropertyValue::create( $value, - gettype($value) + '' ), ]); }