From f8800d2bd6c8466b95a1c87c5bab4696c5cc759e Mon Sep 17 00:00:00 2001 From: Bennett Date: Thu, 21 May 2026 02:36:02 +0100 Subject: [PATCH 1/3] Fix equalTo array membership queries --- src/Parse/ParseQuery.php | 30 +++++++++++++++++++- tests/Parse/ParseQueryTest.php | 51 ++++++++++++++++++++++++++++++---- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/Parse/ParseQuery.php b/src/Parse/ParseQuery.php index be7826b8..c7a59451 100644 --- a/src/Parse/ParseQuery.php +++ b/src/Parse/ParseQuery.php @@ -140,7 +140,11 @@ public function get($objectId, $useMasterKey = false) */ public function equalTo($key, $value) { - $this->addCondition($key, '$eq', $value); + if (isset($this->where[$key]) && $this->isConditionMap($this->where[$key])) { + $this->addCondition($key, '$eq', $value); + } else { + $this->where[$key] = ParseClient::_encode($value, true); + } return $this; } @@ -158,10 +162,34 @@ private function addCondition($key, $condition, $value) { if (!isset($this->where[$key])) { $this->where[$key] = []; + } elseif (!$this->isConditionMap($this->where[$key])) { + $this->where[$key] = ['$eq' => $this->where[$key]]; } $this->where[$key][$condition] = ParseClient::_encode($value, true); } + /** + * Checks whether a where entry is an operator map rather than a direct equality value. + * + * @param mixed $value Where entry value. + * + * @return bool + */ + private function isConditionMap($value) + { + if (!is_array($value)) { + return false; + } + + foreach (array_keys($value) as $key) { + if (!is_string($key) || strpos($key, '$') !== 0) { + return false; + } + } + + return !empty($value); + } + /** * Sets the conditions of this parse query from an array * diff --git a/tests/Parse/ParseQueryTest.php b/tests/Parse/ParseQueryTest.php index ae251ab1..51c901c9 100644 --- a/tests/Parse/ParseQueryTest.php +++ b/tests/Parse/ParseQueryTest.php @@ -2663,9 +2663,7 @@ public function testGetAndSetConditions() $this->assertEquals([ 'where' => [ - 'key' => [ - '$eq' => 'value', - ], + 'key' => 'value', 'key2' => [ '$ne' => 'value2', ], @@ -2726,9 +2724,7 @@ public function testCountDoesNotOverrideConditions() $this->assertSame([ 'where' => [ - 'country' => [ - '$eq' => 'US' - ] + 'country' => 'US' ], 'limit' => 1, ], $query->_getOptions()); @@ -2811,4 +2807,47 @@ public function testEqualToWithSameKeyDoesNotOverrideOtherConditions() ], ], $query->_getOptions()); } + + /** + * @group query-equalTo-conditions + */ + public function testEqualToAfterDirectEqualityDoesNotOverrideOtherConditions() + { + $query = new ParseQuery('TestObject'); + $query->equalTo('status', 'active'); + $query->notEqualTo('status', 'archived'); + + $this->assertSame([ + 'where' => [ + 'status' => [ + '$eq' => 'active', + '$ne' => 'archived', + ] + ], + ], $query->_getOptions()); + } + + /** + * @group query-equalTo-conditions + */ + public function testEqualToPointerUsesDirectEqualityForArrayFieldMembership() + { + $user = new ParseObject('_User'); + $user->_mergeAfterFetch([ + 'objectId' => 'someUserId', + ]); + + $query = new ParseQuery('_Role'); + $query->equalTo('users', $user); + + $this->assertSame([ + 'where' => [ + 'users' => [ + '__type' => 'Pointer', + 'className' => '_User', + 'objectId' => 'someUserId', + ], + ], + ], $query->_getOptions()); + } } From 95a6b9ea29203bc93246f7ceb66d0b331b91e371 Mon Sep 17 00:00:00 2001 From: Bennett Date: Thu, 21 May 2026 12:20:23 +0100 Subject: [PATCH 2/3] Address query encoding review feedback --- src/Parse/ParseQuery.php | 4 ++-- tests/Parse/ParseQueryTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Parse/ParseQuery.php b/src/Parse/ParseQuery.php index c7a59451..12fa494e 100644 --- a/src/Parse/ParseQuery.php +++ b/src/Parse/ParseQuery.php @@ -181,8 +181,8 @@ private function isConditionMap($value) return false; } - foreach (array_keys($value) as $key) { - if (!is_string($key) || strpos($key, '$') !== 0) { + foreach ($value as $operatorKey => $_) { + if (!is_string($operatorKey) || strpos($operatorKey, '$') !== 0) { return false; } } diff --git a/tests/Parse/ParseQueryTest.php b/tests/Parse/ParseQueryTest.php index 51c901c9..3bc03b75 100644 --- a/tests/Parse/ParseQueryTest.php +++ b/tests/Parse/ParseQueryTest.php @@ -2830,7 +2830,7 @@ public function testEqualToAfterDirectEqualityDoesNotOverrideOtherConditions() /** * @group query-equalTo-conditions */ - public function testEqualToPointerUsesDirectEqualityForArrayFieldMembership() + public function testEqualToPointerSerializesAsDirectWhereValue() { $user = new ParseObject('_User'); $user->_mergeAfterFetch([ From d0d1cd7f669034e0f5ec07e41a0f81e89b9ae8c9 Mon Sep 17 00:00:00 2001 From: Bennett Date: Thu, 21 May 2026 12:37:11 +0100 Subject: [PATCH 3/3] fix: preserve null equality conditions --- src/Parse/ParseQuery.php | 2 +- tests/Parse/ParseQueryTest.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Parse/ParseQuery.php b/src/Parse/ParseQuery.php index 12fa494e..a212390d 100644 --- a/src/Parse/ParseQuery.php +++ b/src/Parse/ParseQuery.php @@ -160,7 +160,7 @@ public function equalTo($key, $value) */ private function addCondition($key, $condition, $value) { - if (!isset($this->where[$key])) { + if (!array_key_exists($key, $this->where)) { $this->where[$key] = []; } elseif (!$this->isConditionMap($this->where[$key])) { $this->where[$key] = ['$eq' => $this->where[$key]]; diff --git a/tests/Parse/ParseQueryTest.php b/tests/Parse/ParseQueryTest.php index 3bc03b75..fb891232 100644 --- a/tests/Parse/ParseQueryTest.php +++ b/tests/Parse/ParseQueryTest.php @@ -2827,6 +2827,25 @@ public function testEqualToAfterDirectEqualityDoesNotOverrideOtherConditions() ], $query->_getOptions()); } + /** + * @group query-equalTo-conditions + */ + public function testEqualToNullBeforeConditionPreservesNullEquality() + { + $query = new ParseQuery('TestObject'); + $query->equalTo('status', null); + $query->notEqualTo('status', 'archived'); + + $this->assertSame([ + 'where' => [ + 'status' => [ + '$eq' => null, + '$ne' => 'archived', + ] + ], + ], $query->_getOptions()); + } + /** * @group query-equalTo-conditions */