From b28aeb1fdd80835583b52f446493507f0d981f95 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Thu, 2 Nov 2023 16:27:57 +0100 Subject: [PATCH 1/7] FEATURE: Add ``PropertyValueCriteriaMatcher`` The property value criteria matcher accepts a ``PropertyValueCriteriaInterface`` and provides static methods to check wether a ``Node`` or a ``PropertyCollection`` matches those criteria. --- .../PropertyValueCriteriaMatcher.php | 103 +++++ .../PropertyValueCriteriaMatcherTest.php | 355 ++++++++++++++++++ 2 files changed, 458 insertions(+) create mode 100644 Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php create mode 100644 Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php new file mode 100644 index 00000000000..c5034381b80 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php @@ -0,0 +1,103 @@ +criteria1) && self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2); + case $propertyValueCriteria instanceof OrCriteria: + return self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) || self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2); + case $propertyValueCriteria instanceof NegateCriteria: + return !self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria); + case $propertyValueCriteria instanceof PropertyValueContains: + $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + if ($propertyValueCriteria->caseSensitive) { + return is_string($propertyValue) + ? str_contains($propertyValue, $propertyValueCriteria->value) + : false; + } else { + return is_string($propertyValue) + ? str_contains(mb_strtolower($propertyValue), mb_strtolower($propertyValueCriteria->value)) + : false; + } + case $propertyValueCriteria instanceof PropertyValueEndsWith: + $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + if ($propertyValueCriteria->caseSensitive) { + return is_string($propertyValue) + ? str_ends_with($propertyValue, $propertyValueCriteria->value) + : false; + } else { + return is_string($propertyValue) + ? str_ends_with(mb_strtolower($propertyValue), mb_strtolower($propertyValueCriteria->value)) + : false; + } + case $propertyValueCriteria instanceof PropertyValueStartsWith: + $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + if ($propertyValueCriteria->caseSensitive) { + return is_string($propertyValue) + ? str_starts_with($propertyValue, $propertyValueCriteria->value) + : false; + } else { + return is_string($propertyValue) + ? str_starts_with(mb_strtolower($propertyValue), mb_strtolower($propertyValueCriteria->value)) + : false; + } + case $propertyValueCriteria instanceof PropertyValueEquals: + if (!$propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value)) { + return false; + } + $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + if ($propertyValueCriteria->caseSensitive) { + return $propertyValue == $propertyValueCriteria->value; + } else { + return (is_string($propertyValue) ? mb_strtolower($propertyValue) : $propertyValue) + == (is_string($propertyValueCriteria->value) ? mb_strtolower($propertyValueCriteria->value) : $propertyValueCriteria->value); + } + case $propertyValueCriteria instanceof PropertyValueGreaterThan: + return $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) + && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value > $propertyValueCriteria->value; + case $propertyValueCriteria instanceof PropertyValueGreaterThanOrEqual: + return $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) + && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value >= $propertyValueCriteria->value; + case $propertyValueCriteria instanceof PropertyValueLessThan: + return $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) + && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value < $propertyValueCriteria->value; + case $propertyValueCriteria instanceof PropertyValueLessThanOrEqual: + return $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) + && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value <= $propertyValueCriteria->value; + default: + throw new \InvalidArgumentException(sprintf('Invalid/unsupported property value criteria "%s"', get_debug_type($propertyValueCriteria)), 1679561073); + } + } + + public static function matchesNode(Node $node, PropertyValueCriteriaInterface $propertyValueCriteria): bool + { + return static::matchesPropertyCollection($node->properties, $propertyValueCriteria); + } +} diff --git a/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php new file mode 100644 index 00000000000..5d4ae31efc9 --- /dev/null +++ b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php @@ -0,0 +1,355 @@ +propertyCollection = new PropertyCollection( + SerializedPropertyValues::fromArray([ + 'nullProperty' => new SerializedPropertyValue(null, 'null'), + 'stringProperty' => new SerializedPropertyValue('foo', 'string'), + 'integerProperty' => new SerializedPropertyValue(123, 'int') + ]), + $subjectProvider->propertyConverter + ); + } + + public function andCriteriaDataProvider(): \Generator + { + $trueCriterium = PropertyValueEquals::create(PropertyName::fromString('stringProperty'), 'foo', true); + $falseCriterium = PropertyValueEquals::create(PropertyName::fromString('stringProperty'), 'other', true); + + yield 'both criteria are true' => [$trueCriterium, $trueCriterium, true]; + yield 'first criterium is true' => [$trueCriterium, $falseCriterium, false]; + yield 'last criterium is true' => [$falseCriterium, $trueCriterium, false]; + yield 'both criteria are false' => [$falseCriterium, $falseCriterium, false]; + } + + /** + * @test + * @dataProvider andCriteriaDataProvider + */ + public function andCriteria(PropertyValueCriteriaInterface $criteriaA, PropertyValueCriteriaInterface $criteriaB, $expectation) + { + + $this->assertSame( + $expectation, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + AndCriteria::create($criteriaA, $criteriaB) + ) + ); + } + + public function negateCriteriaDataProvider(): \Generator + { + $trueCriterium = PropertyValueEquals::create(PropertyName::fromString('stringProperty'), 'foo',true); + $falseCriterium = PropertyValueEquals::create(PropertyName::fromString('stringProperty'), 'other', true); + + yield 'criterium is true' => [$trueCriterium, false]; + yield 'criterium is false' => [$falseCriterium, true]; + } + + /** + * @test + * @dataProvider negateCriteriaDataProvider + */ + public function negateCriteria(PropertyValueCriteriaInterface $criteriaA, $expectation) + { + $this->assertSame( + $expectation, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + NegateCriteria::create($criteriaA) + ) + ); + } + + public function orCriteriaDataProvider(): \Generator + { + $trueCriterium = PropertyValueEquals::create(PropertyName::fromString('stringProperty'), 'foo', true); + $falseCriterium = PropertyValueEquals::create(PropertyName::fromString('stringProperty'), 'other', true); + + yield 'both criteria are true' => [$trueCriterium, $trueCriterium, true]; + yield 'first criterium is true' => [$trueCriterium, $falseCriterium, true]; + yield 'last criterium is true' => [$falseCriterium, $trueCriterium, true]; + yield 'both criteria are false' => [$falseCriterium, $falseCriterium, false]; + } + + /** + * @test + * @dataProvider orCriteriaDataProvider + */ + public function orCriteria(PropertyValueCriteriaInterface $criteriaA, PropertyValueCriteriaInterface $criteriaB, $expectation) + { + + $this->assertSame( + $expectation, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + OrCriteria::create($criteriaA, $criteriaB) + ) + ); + } + + public function containsCriteriaDataProvider(): \Generator + { + yield 'existing "stringProperty" contains "foo"' => ['stringProperty', 'foo', true, true]; + yield 'existing "stringProperty" contains "Foo" (non case sensitive)' => ['stringProperty', 'Foo', false, true]; + yield 'existing "stringProperty" contains "Foo" (case sensitive)' => ['stringProperty', 'Foo', true, false]; + yield 'existing "stringProperty" contains "fo"' => ['stringProperty', 'fo', true, true]; + yield 'existing "stringProperty" contains "oo"' => ['stringProperty', 'oo', true, true]; + yield 'existing "stringProperty" does not contain "bar"' => ['foo', 'bar', true, false]; + yield 'existing "integerProperty" does not contain "foo"' => ['integerProperty', 'foo', true, false]; + yield 'not existing "otherProperty" does not contain "foo"' => ['otherProperty', 'foo', true, false]; + } + + /** + * @test + * @dataProvider containsCriteriaDataProvider + */ + public function containsCriteria(string $propertyName, mixed $propertyValueToExpect, bool $caseSensitive, bool $expectedResult): void + { + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + PropertyValueContains::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect, + $caseSensitive + ) + ) + ); + } + + public function valueStartsWithCriteriaDataProvider(): \Generator + { + yield 'existing "stringProperty" starts with "f"' => ['stringProperty', 'f', true, true]; + yield 'existing "stringProperty" starts with "foo"' => ['stringProperty', 'foo', true, true]; + yield 'existing "stringProperty" starts with "Foo" (case insensitive)' => ['stringProperty', 'Foo', false, true]; + yield 'existing "stringProperty" does not start with "Foo" (case sensitive)' => ['stringProperty', 'Foo', true, false]; + yield 'existing "stringProperty" does not start with "fooo"' => ['stringProperty', 'ffoo', true, false]; + yield 'existing "stringProperty" does not start with "bar"' => ['stringProperty', 'bar', true, false]; + yield 'existing "integerProperty" does not start with "foo"' => ['integerProperty', 'foo', true, false]; + yield 'not existing "otherProperty" does not start with "foo"' => ['otherProperty', 'foo', true, false]; + } + + /** + * @test + * @dataProvider valueStartsWithCriteriaDataProvider + */ + public function valueStartsWithCriteria(string $propertyName, mixed $propertyValueToExpect, bool $caseSensitive, bool $expectedResult): void + { + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + PropertyValueStartsWith::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect, + $caseSensitive + ) + ) + ); + } + + public function valueEndsWithCriteriaDataProvider(): \Generator + { + yield 'existing "stringProperty" ends with "o"' => ['stringProperty', 'o', true, true]; + yield 'existing "stringProperty" ends with "foo"' => ['stringProperty', 'foo', true, true]; + yield 'existing "stringProperty" ends with "Foo" (case insensitive)' => ['stringProperty', 'Foo', false, true]; + yield 'existing "stringProperty" does not end with "Foo" (case sensitive)' => ['stringProperty', 'Foo', true, false]; + yield 'existing "stringProperty" does not end with "ffoo"' => ['stringProperty', 'ffoo', true, false]; + yield 'existing "stringProperty" does not end with "bar"' => ['stringProperty', 'bar', true, false]; + yield 'existing "integerProperty" does not end with "foo"' => ['integerProperty', 'foo', true, false]; + yield 'not existing "otherProperty" does not end with "foo"' => ['otherProperty', 'foo', true, false]; + } + + /** + * @test + * @dataProvider valueEndsWithCriteriaDataProvider + */ + public function valueEndsWithCriteria(string $propertyName, mixed $propertyValueToExpect, bool $caseSensitive, bool $expectedResult): void + { + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + PropertyValueEndsWith::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect, + $caseSensitive + ) + ) + ); + } + + public function equalsCriteriaDataProvider(): \Generator + { + yield 'existing "stringProperty" equals "foo"' => ['stringProperty', 'foo', true, true]; + yield 'existing "stringProperty" equals "Foo" (case insensitive)' => ['stringProperty', 'Foo', false, true]; + yield 'existing "stringProperty" does not equal "Foo" (case sensitive)' => ['stringProperty', 'Foo', true, false]; + yield 'existing "integerProperty" does equal 123' => ['integerProperty', 123, true, true]; + yield 'existing "stringProperty" does not equal "bar"' => ['stringProperty', 'bar', true, false]; + yield 'existing "stringProperty" does not equal 123' => ['stringProperty', 123, true, false]; + yield 'existing "nullProperty" does not equal 123' => ['nullProperty', 123, true, false]; + yield 'existing "stringProperty" does not equal empty string' => ['stringProperty', '', true, false]; + yield 'existing "integerProperty" does not equal 0' => ['integerProperty', 0, true, false]; + yield 'non existing "otherProperty" bar does not equal "foo"' => ['otherProperty', 'foo', true, false]; + } + + /** + * @test + * @dataProvider equalsCriteriaDataProvider + */ + public function equalsCriteria(string $propertyName, mixed $propertyValueToExpect, bool $caseSensitive, bool $expectedResult): void + { + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + PropertyValueEquals::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect, + $caseSensitive + ) + ) + ); + } + + public function greaterThanCriteriaDataProvider(): \Generator + { + yield 'existing "integerProperty" is greater than 0' => ['integerProperty', 0, true]; + yield 'existing "integerProperty" is greater than 122' => ['integerProperty', 122, true]; + yield 'existing "integerProperty" is not greater than 123' => ['integerProperty', 123, false]; + yield 'existing "integerProperty" is not greater than 124' => ['integerProperty', 124, false]; + yield 'existing "integerProperty" is not greater than 999' => ['integerProperty', 999, false]; + } + + /** + * @test + * @dataProvider greaterThanCriteriaDataProvider + */ + public function greaterThanCriteria(string $propertyName, mixed $propertyValueToExpect, bool $expectedResult): void + { + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + PropertyValueGreaterThan::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect + ) + ) + ); + } + + public function greaterThanOrEqualCriteriaDataProvider(): \Generator + { + yield 'existing "integerProperty" is greater than 0' => ['integerProperty', 0, true]; + yield 'existing "integerProperty" is greater than 122' => ['integerProperty', 122, true]; + yield 'existing "integerProperty" is not greater than 123' => ['integerProperty', 123, true]; + yield 'existing "integerProperty" is not greater than 124' => ['integerProperty', 124, false]; + yield 'existing "integerProperty" is not greater than 999' => ['integerProperty', 999, false]; + } + + /** + * @test + * @dataProvider greaterThanOrEqualCriteriaDataProvider + */ + public function greaterThanOrEquelCriteria(string $propertyName, mixed $propertyValueToExpect, bool $expectedResult): void + { + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + PropertyValueGreaterThanOrEqual::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect + ) + ) + ); + } + + public function lessThanCriteriaDataProvider(): \Generator + { + yield 'existing "integerProperty" is greater than 0' => ['integerProperty', 0, false]; + yield 'existing "integerProperty" is greater than 122' => ['integerProperty', 122, false]; + yield 'existing "integerProperty" is not greater than 123' => ['integerProperty', 123, false]; + yield 'existing "integerProperty" is not greater than 124' => ['integerProperty', 124, true]; + yield 'existing "integerProperty" is not greater than 999' => ['integerProperty', 999, true]; + } + + /** + * @test + * @dataProvider lessThanCriteriaDataProvider + */ + public function lessThanCriteria(string $propertyName, mixed $propertyValueToExpect, bool $expectedResult): void + { + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + PropertyValueLessThan::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect + ) + ) + ); + } + + public function lessThanOrEqualCriteriaDataProvider(): \Generator + { + yield 'existing "integerProperty" is greater than 0' => ['integerProperty', 0, false]; + yield 'existing "integerProperty" is greater than 122' => ['integerProperty', 122, false]; + yield 'existing "integerProperty" is not greater than 123' => ['integerProperty', 123, true]; + yield 'existing "integerProperty" is not greater than 124' => ['integerProperty', 124, true]; + yield 'existing "integerProperty" is not greater than 999' => ['integerProperty', 999, true]; + } + + /** + * @test + * @dataProvider lessThanOrEqualCriteriaDataProvider + */ + public function lessThanOrEqualCriteria(string $propertyName, mixed $propertyValueToExpect, bool $expectedResult): void + { + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesPropertyCollection( + $this->propertyCollection, + PropertyValueLessThanOrEqual::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect + ) + ) + ); + } +} From d887854db3deea87adb47b121b5a35d34d75aefc Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Fri, 16 Feb 2024 15:39:39 +0100 Subject: [PATCH 2/7] Update Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php Co-authored-by: Bastian Waidelich --- .../PropertyValueCriteriaMatcher.php | 111 ++++++++---------- 1 file changed, 46 insertions(+), 65 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php index c5034381b80..155b927893a 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php @@ -28,72 +28,53 @@ final class PropertyValueCriteriaMatcher { public static function matchesPropertyCollection(PropertyCollection $propertyCollection, PropertyValueCriteriaInterface $propertyValueCriteria): bool { - switch (true) { - case $propertyValueCriteria instanceof AndCriteria: - return self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) && self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2); - case $propertyValueCriteria instanceof OrCriteria: - return self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) || self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2); - case $propertyValueCriteria instanceof NegateCriteria: - return !self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria); - case $propertyValueCriteria instanceof PropertyValueContains: - $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; - if ($propertyValueCriteria->caseSensitive) { - return is_string($propertyValue) - ? str_contains($propertyValue, $propertyValueCriteria->value) - : false; - } else { - return is_string($propertyValue) - ? str_contains(mb_strtolower($propertyValue), mb_strtolower($propertyValueCriteria->value)) - : false; - } - case $propertyValueCriteria instanceof PropertyValueEndsWith: - $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; - if ($propertyValueCriteria->caseSensitive) { - return is_string($propertyValue) - ? str_ends_with($propertyValue, $propertyValueCriteria->value) - : false; - } else { - return is_string($propertyValue) - ? str_ends_with(mb_strtolower($propertyValue), mb_strtolower($propertyValueCriteria->value)) - : false; - } - case $propertyValueCriteria instanceof PropertyValueStartsWith: - $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; - if ($propertyValueCriteria->caseSensitive) { - return is_string($propertyValue) - ? str_starts_with($propertyValue, $propertyValueCriteria->value) - : false; - } else { - return is_string($propertyValue) - ? str_starts_with(mb_strtolower($propertyValue), mb_strtolower($propertyValueCriteria->value)) - : false; - } - case $propertyValueCriteria instanceof PropertyValueEquals: - if (!$propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value)) { - return false; - } - $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; - if ($propertyValueCriteria->caseSensitive) { - return $propertyValue == $propertyValueCriteria->value; - } else { - return (is_string($propertyValue) ? mb_strtolower($propertyValue) : $propertyValue) - == (is_string($propertyValueCriteria->value) ? mb_strtolower($propertyValueCriteria->value) : $propertyValueCriteria->value); - } - case $propertyValueCriteria instanceof PropertyValueGreaterThan: - return $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) - && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value > $propertyValueCriteria->value; - case $propertyValueCriteria instanceof PropertyValueGreaterThanOrEqual: - return $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) - && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value >= $propertyValueCriteria->value; - case $propertyValueCriteria instanceof PropertyValueLessThan: - return $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) - && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value < $propertyValueCriteria->value; - case $propertyValueCriteria instanceof PropertyValueLessThanOrEqual: - return $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) - && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value <= $propertyValueCriteria->value; - default: - throw new \InvalidArgumentException(sprintf('Invalid/unsupported property value criteria "%s"', get_debug_type($propertyValueCriteria)), 1679561073); + return match ($propertyValueCriteria::class) { + AndCriteria::class => self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) && self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2), + OrCriteria::class => self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) || self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2), + NegateCriteria::class => !self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria), + PropertyValueContains::class => self::compareStringPropertyValues($propertyCollection, $propertyValueCriteria, static fn (string $propertyValue, string $criteriaPropertyValue) => str_contains($propertyValue, $criteriaPropertyValue)), + PropertyValueEndsWith::class => self::compareStringPropertyValues($propertyCollection, $propertyValueCriteria, static fn (string $propertyValue, string $criteriaPropertyValue) => str_ends_with($propertyValue, $criteriaPropertyValue)), + PropertyValueStartsWith::class => self::compareStringPropertyValues($propertyCollection, $propertyValueCriteria, static fn (string $propertyValue, string $criteriaPropertyValue) => str_starts_with($propertyValue, $criteriaPropertyValue)), + PropertyValueEquals::class => self::propertyValueEquals($propertyCollection, $propertyValueCriteria), + PropertyValueGreaterThan::class => $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) + && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value > $propertyValueCriteria->value, + PropertyValueGreaterThanOrEqual::class => $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) + && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value >= $propertyValueCriteria->value, + PropertyValueLessThan::class => $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) + && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value < $propertyValueCriteria->value, + PropertyValueLessThanOrEqual::class => $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) + && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value <= $propertyValueCriteria->value, + default => throw new \InvalidArgumentException(sprintf('Invalid/unsupported property value criteria "%s"', get_debug_type($propertyValueCriteria)), 1679561073), + }; + } + + /** + * @param \Closure (string, string): bool $comparator + */ + private static function compareStringPropertyValues(PropertyCollection $propertyCollection, PropertyValueContains|PropertyValueEndsWith|PropertyValueStartsWith $propertyValueCriteria, \Closure $comparator): bool + { + $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + if (!is_string($propertyValue)) { + return false; + } + $criteriaPropertyValue = $propertyValueCriteria->value; + if (!$propertyValueCriteria->caseSensitive) { + $criteriaPropertyValue = mb_strtolower($propertyValueCriteria->value); + $propertyValue = mb_strtolower($propertyValue); + } + return $comparator($propertyValue, $criteriaPropertyValue); + } + + private static function propertyValueEquals(PropertyCollection $propertyCollection, PropertyValueEquals $propertyValueCriteria): bool + { + if (!$propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value)) { + return false; + } + $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + if ($propertyValueCriteria->caseSensitive) { + return $propertyValue == $propertyValueCriteria->value; } + return (is_string($propertyValue) ? mb_strtolower($propertyValue) : $propertyValue) == (is_string($propertyValueCriteria->value) ? mb_strtolower($propertyValueCriteria->value) : $propertyValueCriteria->value); } public static function matchesNode(Node $node, PropertyValueCriteriaInterface $propertyValueCriteria): bool From 41f89df4cfc5a49302e9fd798b98c176e9f7861f Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Fri, 16 Feb 2024 16:06:27 +0100 Subject: [PATCH 3/7] TASK: Simplify implementation and avoid passing around function which is hard to get at times --- .../PropertyValueCriteriaMatcher.php | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php index 155b927893a..0a2c0bdea83 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php @@ -29,12 +29,14 @@ final class PropertyValueCriteriaMatcher public static function matchesPropertyCollection(PropertyCollection $propertyCollection, PropertyValueCriteriaInterface $propertyValueCriteria): bool { return match ($propertyValueCriteria::class) { - AndCriteria::class => self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) && self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2), - OrCriteria::class => self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) || self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2), + AndCriteria::class => self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) + && self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2), + OrCriteria::class => self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) + || self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2), NegateCriteria::class => !self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria), - PropertyValueContains::class => self::compareStringPropertyValues($propertyCollection, $propertyValueCriteria, static fn (string $propertyValue, string $criteriaPropertyValue) => str_contains($propertyValue, $criteriaPropertyValue)), - PropertyValueEndsWith::class => self::compareStringPropertyValues($propertyCollection, $propertyValueCriteria, static fn (string $propertyValue, string $criteriaPropertyValue) => str_ends_with($propertyValue, $criteriaPropertyValue)), - PropertyValueStartsWith::class => self::compareStringPropertyValues($propertyCollection, $propertyValueCriteria, static fn (string $propertyValue, string $criteriaPropertyValue) => str_starts_with($propertyValue, $criteriaPropertyValue)), + PropertyValueContains::class => self::propertyValueContains($propertyCollection, $propertyValueCriteria), + PropertyValueEndsWith::class => self::propertyValueEndsWith($propertyCollection, $propertyValueCriteria), + PropertyValueStartsWith::class => self::propertyValueStartsWith($propertyCollection, $propertyValueCriteria), PropertyValueEquals::class => self::propertyValueEquals($propertyCollection, $propertyValueCriteria), PropertyValueGreaterThan::class => $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value > $propertyValueCriteria->value, @@ -48,21 +50,34 @@ public static function matchesPropertyCollection(PropertyCollection $propertyCol }; } - /** - * @param \Closure (string, string): bool $comparator - */ - private static function compareStringPropertyValues(PropertyCollection $propertyCollection, PropertyValueContains|PropertyValueEndsWith|PropertyValueStartsWith $propertyValueCriteria, \Closure $comparator): bool + private static function propertyValueContains(PropertyCollection $propertyCollection, PropertyValueContains $propertyValueCriteria): bool { $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; - if (!is_string($propertyValue)) { - return false; + if ($propertyValueCriteria->caseSensitive) { + return is_string($propertyValue) ? str_contains($propertyValue, $propertyValueCriteria->value) : false; + } else { + return is_string($propertyValue) ? str_contains(mb_strtolower($propertyValue), mb_strtolower($propertyValueCriteria->value)) : false; } - $criteriaPropertyValue = $propertyValueCriteria->value; - if (!$propertyValueCriteria->caseSensitive) { - $criteriaPropertyValue = mb_strtolower($propertyValueCriteria->value); - $propertyValue = mb_strtolower($propertyValue); + } + + private static function propertyValueStartsWith(PropertyCollection $propertyCollection, PropertyValueStartsWith $propertyValueCriteria): bool + { + $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + if ($propertyValueCriteria->caseSensitive) { + return is_string($propertyValue) ? str_starts_with($propertyValue, $propertyValueCriteria->value) : false; + } else { + return is_string($propertyValue) ? str_starts_with(mb_strtolower($propertyValue), mb_strtolower($propertyValueCriteria->value)) : false; + } + } + + private static function propertyValueEndsWith(PropertyCollection $propertyCollection, PropertyValueEndsWith $propertyValueCriteria): bool + { + $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + if ($propertyValueCriteria->caseSensitive) { + return is_string($propertyValue) ? str_ends_with($propertyValue, $propertyValueCriteria->value) : false; + } else { + return is_string($propertyValue) ? str_ends_with(mb_strtolower($propertyValue), mb_strtolower($propertyValueCriteria->value)) : false; } - return $comparator($propertyValue, $criteriaPropertyValue); } private static function propertyValueEquals(PropertyCollection $propertyCollection, PropertyValueEquals $propertyValueCriteria): bool From d667d7d78cb81efa15b394f9bd5e4bf2f8beffa5 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Fri, 23 Feb 2024 16:39:53 +0100 Subject: [PATCH 4/7] BUGFIX: Use strict comparison for quals operation --- .../Filter/PropertyValue/PropertyValueCriteriaMatcher.php | 4 ++-- .../PropertyValue/PropertyValueCriteriaMatcherTest.php | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php index 0a2c0bdea83..2b435e2fced 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php @@ -87,9 +87,9 @@ private static function propertyValueEquals(PropertyCollection $propertyCollecti } $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; if ($propertyValueCriteria->caseSensitive) { - return $propertyValue == $propertyValueCriteria->value; + return $propertyValue === $propertyValueCriteria->value; } - return (is_string($propertyValue) ? mb_strtolower($propertyValue) : $propertyValue) == (is_string($propertyValueCriteria->value) ? mb_strtolower($propertyValueCriteria->value) : $propertyValueCriteria->value); + return (is_string($propertyValue) ? mb_strtolower($propertyValue) : $propertyValue) === (is_string($propertyValueCriteria->value) ? mb_strtolower($propertyValueCriteria->value) : $propertyValueCriteria->value); } public static function matchesNode(Node $node, PropertyValueCriteriaInterface $propertyValueCriteria): bool diff --git a/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php index 5d4ae31efc9..4bba2dca142 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php @@ -224,6 +224,8 @@ public function equalsCriteriaDataProvider(): \Generator yield 'existing "stringProperty" does not equal empty string' => ['stringProperty', '', true, false]; yield 'existing "integerProperty" does not equal 0' => ['integerProperty', 0, true, false]; yield 'non existing "otherProperty" bar does not equal "foo"' => ['otherProperty', 'foo', true, false]; + yield 'existing "stringProperty" does not equal true' => ['stringProperty', true, true, false]; + yield 'existing "stringProperty" does not equal true (case insensitive)' => ['stringProperty', true, false, false]; } /** From 94b42fcbb6a90dc8ec6de01379e8d744e2511bff Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Fri, 23 Feb 2024 16:44:02 +0100 Subject: [PATCH 5/7] TASK: Move public matches node method to the top --- .../PropertyValue/PropertyValueCriteriaMatcher.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php index 2b435e2fced..a5290cdf955 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php @@ -26,6 +26,11 @@ */ final class PropertyValueCriteriaMatcher { + public static function matchesNode(Node $node, PropertyValueCriteriaInterface $propertyValueCriteria): bool + { + return static::matchesPropertyCollection($node->properties, $propertyValueCriteria); + } + public static function matchesPropertyCollection(PropertyCollection $propertyCollection, PropertyValueCriteriaInterface $propertyValueCriteria): bool { return match ($propertyValueCriteria::class) { @@ -91,9 +96,4 @@ private static function propertyValueEquals(PropertyCollection $propertyCollecti } return (is_string($propertyValue) ? mb_strtolower($propertyValue) : $propertyValue) === (is_string($propertyValueCriteria->value) ? mb_strtolower($propertyValueCriteria->value) : $propertyValueCriteria->value); } - - public static function matchesNode(Node $node, PropertyValueCriteriaInterface $propertyValueCriteria): bool - { - return static::matchesPropertyCollection($node->properties, $propertyValueCriteria); - } } From 5e6c640afea3698cbea2482b0fab90817bcfa6b0 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Sat, 24 Feb 2024 19:52:13 +0100 Subject: [PATCH 6/7] TASK: Add methods `matchesSerializedPropertyValues` `matchesNode` and `matchesPropertyCollection` internally use this method --- .../PropertyValueCriteriaMatcher.php | 60 +++++---- .../PropertyValueCriteriaMatcherTest.php | 121 +++++++++++++++++- 2 files changed, 148 insertions(+), 33 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php index a5290cdf955..9830139cd9a 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php +++ b/Neos.ContentRepository.Core/Classes/Projection/ContentGraph/Filter/PropertyValue/PropertyValueCriteriaMatcher.php @@ -4,6 +4,7 @@ namespace Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue; +use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\Criteria\AndCriteria; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\Criteria\NegateCriteria; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\Criteria\OrCriteria; @@ -28,36 +29,41 @@ final class PropertyValueCriteriaMatcher { public static function matchesNode(Node $node, PropertyValueCriteriaInterface $propertyValueCriteria): bool { - return static::matchesPropertyCollection($node->properties, $propertyValueCriteria); + return static::matchesSerializedPropertyValues($node->properties->serialized(), $propertyValueCriteria); } public static function matchesPropertyCollection(PropertyCollection $propertyCollection, PropertyValueCriteriaInterface $propertyValueCriteria): bool + { + return static::matchesSerializedPropertyValues($propertyCollection->serialized(), $propertyValueCriteria); + } + + public static function matchesSerializedPropertyValues(SerializedPropertyValues $serializedPropertyValues, PropertyValueCriteriaInterface $propertyValueCriteria): bool { return match ($propertyValueCriteria::class) { - AndCriteria::class => self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) - && self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2), - OrCriteria::class => self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria1) - || self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria2), - NegateCriteria::class => !self::matchesPropertyCollection($propertyCollection, $propertyValueCriteria->criteria), - PropertyValueContains::class => self::propertyValueContains($propertyCollection, $propertyValueCriteria), - PropertyValueEndsWith::class => self::propertyValueEndsWith($propertyCollection, $propertyValueCriteria), - PropertyValueStartsWith::class => self::propertyValueStartsWith($propertyCollection, $propertyValueCriteria), - PropertyValueEquals::class => self::propertyValueEquals($propertyCollection, $propertyValueCriteria), - PropertyValueGreaterThan::class => $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) - && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value > $propertyValueCriteria->value, - PropertyValueGreaterThanOrEqual::class => $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) - && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value >= $propertyValueCriteria->value, - PropertyValueLessThan::class => $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) - && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value < $propertyValueCriteria->value, - PropertyValueLessThanOrEqual::class => $propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value) - && $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value <= $propertyValueCriteria->value, + AndCriteria::class => self::matchesSerializedPropertyValues($serializedPropertyValues, $propertyValueCriteria->criteria1) + && self::matchesSerializedPropertyValues($serializedPropertyValues, $propertyValueCriteria->criteria2), + OrCriteria::class => self::matchesSerializedPropertyValues($serializedPropertyValues, $propertyValueCriteria->criteria1) + || self::matchesSerializedPropertyValues($serializedPropertyValues, $propertyValueCriteria->criteria2), + NegateCriteria::class => !self::matchesSerializedPropertyValues($serializedPropertyValues, $propertyValueCriteria->criteria), + PropertyValueContains::class => self::propertyValueContains($serializedPropertyValues, $propertyValueCriteria), + PropertyValueEndsWith::class => self::propertyValueEndsWith($serializedPropertyValues, $propertyValueCriteria), + PropertyValueStartsWith::class => self::propertyValueStartsWith($serializedPropertyValues, $propertyValueCriteria), + PropertyValueEquals::class => self::propertyValueEquals($serializedPropertyValues, $propertyValueCriteria), + PropertyValueGreaterThan::class => $serializedPropertyValues->propertyExists($propertyValueCriteria->propertyName->value) + && $serializedPropertyValues->getProperty($propertyValueCriteria->propertyName->value)?->value > $propertyValueCriteria->value, + PropertyValueGreaterThanOrEqual::class => $serializedPropertyValues->propertyExists($propertyValueCriteria->propertyName->value) + && $serializedPropertyValues->getProperty($propertyValueCriteria->propertyName->value)?->value >= $propertyValueCriteria->value, + PropertyValueLessThan::class => $serializedPropertyValues->propertyExists($propertyValueCriteria->propertyName->value) + && $serializedPropertyValues->getProperty($propertyValueCriteria->propertyName->value)?->value < $propertyValueCriteria->value, + PropertyValueLessThanOrEqual::class => $serializedPropertyValues->propertyExists($propertyValueCriteria->propertyName->value) + && $serializedPropertyValues->getProperty($propertyValueCriteria->propertyName->value)?->value <= $propertyValueCriteria->value, default => throw new \InvalidArgumentException(sprintf('Invalid/unsupported property value criteria "%s"', get_debug_type($propertyValueCriteria)), 1679561073), }; } - private static function propertyValueContains(PropertyCollection $propertyCollection, PropertyValueContains $propertyValueCriteria): bool + private static function propertyValueContains(SerializedPropertyValues $serializedPropertyValues, PropertyValueContains $propertyValueCriteria): bool { - $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + $propertyValue = $serializedPropertyValues->getProperty($propertyValueCriteria->propertyName->value)?->value; if ($propertyValueCriteria->caseSensitive) { return is_string($propertyValue) ? str_contains($propertyValue, $propertyValueCriteria->value) : false; } else { @@ -65,9 +71,9 @@ private static function propertyValueContains(PropertyCollection $propertyCollec } } - private static function propertyValueStartsWith(PropertyCollection $propertyCollection, PropertyValueStartsWith $propertyValueCriteria): bool + private static function propertyValueStartsWith(SerializedPropertyValues $serializedPropertyValues, PropertyValueStartsWith $propertyValueCriteria): bool { - $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + $propertyValue = $serializedPropertyValues->getProperty($propertyValueCriteria->propertyName->value)?->value; if ($propertyValueCriteria->caseSensitive) { return is_string($propertyValue) ? str_starts_with($propertyValue, $propertyValueCriteria->value) : false; } else { @@ -75,9 +81,9 @@ private static function propertyValueStartsWith(PropertyCollection $propertyColl } } - private static function propertyValueEndsWith(PropertyCollection $propertyCollection, PropertyValueEndsWith $propertyValueCriteria): bool + private static function propertyValueEndsWith(SerializedPropertyValues $serializedPropertyValues, PropertyValueEndsWith $propertyValueCriteria): bool { - $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + $propertyValue = $serializedPropertyValues->getProperty($propertyValueCriteria->propertyName->value)?->value; if ($propertyValueCriteria->caseSensitive) { return is_string($propertyValue) ? str_ends_with($propertyValue, $propertyValueCriteria->value) : false; } else { @@ -85,12 +91,12 @@ private static function propertyValueEndsWith(PropertyCollection $propertyCollec } } - private static function propertyValueEquals(PropertyCollection $propertyCollection, PropertyValueEquals $propertyValueCriteria): bool + private static function propertyValueEquals(SerializedPropertyValues $serializedPropertyValues, PropertyValueEquals $propertyValueCriteria): bool { - if (!$propertyCollection->serialized()->propertyExists($propertyValueCriteria->propertyName->value)) { + if (!$serializedPropertyValues->propertyExists($propertyValueCriteria->propertyName->value)) { return false; } - $propertyValue = $propertyCollection->serialized()->getProperty($propertyValueCriteria->propertyName->value)?->value; + $propertyValue = $serializedPropertyValues->getProperty($propertyValueCriteria->propertyName->value)?->value; if ($propertyValueCriteria->caseSensitive) { return $propertyValue === $propertyValueCriteria->value; } diff --git a/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php index 4bba2dca142..a1f8cdc0907 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php @@ -27,16 +27,19 @@ class PropertyValueCriteriaMatcherTest extends TestCase { protected PropertyCollection $propertyCollection; + protected SerializedPropertyValues $serializedPropertyValues; public function setUp(): void { + $this->serializedPropertyValues = SerializedPropertyValues::fromArray([ + 'nullProperty' => new SerializedPropertyValue(null, 'null'), + 'stringProperty' => new SerializedPropertyValue('foo', 'string'), + 'integerProperty' => new SerializedPropertyValue(123, 'int') + ]); + $subjectProvider = new NodeSubjectProvider(); $this->propertyCollection = new PropertyCollection( - SerializedPropertyValues::fromArray([ - 'nullProperty' => new SerializedPropertyValue(null, 'null'), - 'stringProperty' => new SerializedPropertyValue('foo', 'string'), - 'integerProperty' => new SerializedPropertyValue(123, 'int') - ]), + $this->serializedPropertyValues, $subjectProvider->propertyConverter ); } @@ -66,6 +69,14 @@ public function andCriteria(PropertyValueCriteriaInterface $criteriaA, PropertyV AndCriteria::create($criteriaA, $criteriaB) ) ); + + $this->assertSame( + $expectation, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + AndCriteria::create($criteriaA, $criteriaB) + ) + ); } public function negateCriteriaDataProvider(): \Generator @@ -90,6 +101,14 @@ public function negateCriteria(PropertyValueCriteriaInterface $criteriaA, $expec NegateCriteria::create($criteriaA) ) ); + + $this->assertSame( + $expectation, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + NegateCriteria::create($criteriaA) + ) + ); } public function orCriteriaDataProvider(): \Generator @@ -109,7 +128,6 @@ public function orCriteriaDataProvider(): \Generator */ public function orCriteria(PropertyValueCriteriaInterface $criteriaA, PropertyValueCriteriaInterface $criteriaB, $expectation) { - $this->assertSame( $expectation, PropertyValueCriteriaMatcher::matchesPropertyCollection( @@ -117,6 +135,13 @@ public function orCriteria(PropertyValueCriteriaInterface $criteriaA, PropertyVa OrCriteria::create($criteriaA, $criteriaB) ) ); + $this->assertSame( + $expectation, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + OrCriteria::create($criteriaA, $criteriaB) + ) + ); } public function containsCriteriaDataProvider(): \Generator @@ -148,6 +173,17 @@ public function containsCriteria(string $propertyName, mixed $propertyValueToExp ) ) ); + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + PropertyValueContains::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect, + $caseSensitive + ) + ) + ); } public function valueStartsWithCriteriaDataProvider(): \Generator @@ -179,6 +215,17 @@ public function valueStartsWithCriteria(string $propertyName, mixed $propertyVal ) ) ); + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + PropertyValueStartsWith::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect, + $caseSensitive + ) + ) + ); } public function valueEndsWithCriteriaDataProvider(): \Generator @@ -210,6 +257,17 @@ public function valueEndsWithCriteria(string $propertyName, mixed $propertyValue ) ) ); + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + PropertyValueEndsWith::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect, + $caseSensitive + ) + ) + ); } public function equalsCriteriaDataProvider(): \Generator @@ -245,6 +303,17 @@ public function equalsCriteria(string $propertyName, mixed $propertyValueToExpec ) ) ); + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + PropertyValueEquals::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect, + $caseSensitive + ) + ) + ); } public function greaterThanCriteriaDataProvider(): \Generator @@ -272,6 +341,16 @@ public function greaterThanCriteria(string $propertyName, mixed $propertyValueTo ) ) ); + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + PropertyValueGreaterThan::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect + ) + ) + ); } public function greaterThanOrEqualCriteriaDataProvider(): \Generator @@ -299,6 +378,16 @@ public function greaterThanOrEquelCriteria(string $propertyName, mixed $property ) ) ); + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + PropertyValueGreaterThanOrEqual::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect + ) + ) + ); } public function lessThanCriteriaDataProvider(): \Generator @@ -326,6 +415,16 @@ public function lessThanCriteria(string $propertyName, mixed $propertyValueToExp ) ) ); + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + PropertyValueLessThan::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect + ) + ) + ); } public function lessThanOrEqualCriteriaDataProvider(): \Generator @@ -353,5 +452,15 @@ public function lessThanOrEqualCriteria(string $propertyName, mixed $propertyVal ) ) ); + $this->assertEquals( + $expectedResult, + PropertyValueCriteriaMatcher::matchesSerializedPropertyValues( + $this->serializedPropertyValues, + PropertyValueLessThanOrEqual::create( + PropertyName::fromString($propertyName), + $propertyValueToExpect + ) + ) + ); } } From 893d6e66f9ea835b9af636b56810158258b93cbb Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sun, 25 Feb 2024 10:12:52 +0100 Subject: [PATCH 7/7] TASK: Use empty `PropertyConverter` instead of `$subjectProvider->propertyConverter` The `NodeSubjectProvider` will change in the future. --- .../PropertyValue/PropertyValueCriteriaMatcherTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php index a1f8cdc0907..d2b8d74f323 100644 --- a/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php +++ b/Neos.ContentRepository.Core/Tests/Unit/Projection/ContentGraph/PropertyValue/PropertyValueCriteriaMatcherTest.php @@ -6,6 +6,7 @@ use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValue; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\Criteria\AndCriteria; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\Criteria\NegateCriteria; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\Criteria\OrCriteria; @@ -21,8 +22,8 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\PropertyValueCriteriaMatcher; use Neos\ContentRepository\Core\Projection\ContentGraph\PropertyCollection; use Neos\ContentRepository\Core\SharedModel\Node\PropertyName; -use Neos\ContentRepository\TestSuite\Unit\NodeSubjectProvider; use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Serializer; class PropertyValueCriteriaMatcherTest extends TestCase { @@ -37,10 +38,9 @@ public function setUp(): void 'integerProperty' => new SerializedPropertyValue(123, 'int') ]); - $subjectProvider = new NodeSubjectProvider(); $this->propertyCollection = new PropertyCollection( $this->serializedPropertyValues, - $subjectProvider->propertyConverter + new PropertyConverter(new Serializer()) ); }