diff --git a/core/modules/user/src/Exception/ConditionAlreadySetException.php b/core/modules/user/src/Exception/ConditionAlreadySetException.php new file mode 100644 index 0000000..2a14ece --- /dev/null +++ b/core/modules/user/src/Exception/ConditionAlreadySetException.php @@ -0,0 +1,11 @@ +module = $module; + $this->setter('module', $module); return $this; } @@ -66,7 +46,7 @@ public function setModule($module) { * {@inheritdoc} */ public function setUid($uid) { - $this->uid = $uid; + $this->setter('uid', $uid); return $this; } @@ -74,7 +54,7 @@ public function setUid($uid) { * {@inheritdoc} */ public function setName($name) { - $this->name = $name; + $this->setter('name', $name); return $this; } @@ -82,35 +62,42 @@ public function setName($name) { * {@inheritdoc} */ public function setValue($value) { - $this->value = $value; + $is_scalar_value = is_scalar($value); + if (!$is_scalar_value) { + $value = serialize($value); + } + $this->setter('value', $value); + if (!$is_scalar_value) { + $this->select->condition('serialized', 1); + } return $this; } /** + * Sets a condition on the query for the provided name using the value. + * + * @param string $name + * The name of the field to check. + * @param string|int $value + * The value to test the field against. + * + * @throws \Drupal\user\Exception\ConditionAlreadySetException + * Exception thrown when the select object already has a condition on the + * field. + */ + protected function setter($name, $value) { + if (isset($this->sets[$name]) && $this->sets[$name]) { + throw new ConditionAlreadySetException("Can not add another condition on '$name'"); + } + $this->select->condition($name, $value); + $this->sets[$name] = TRUE; + } + + /** * {@inheritdoc} */ public function execute() { - // Create a query with the supplied conditions. - $select = clone $this->select; - if ($this->module) { - $select->condition('module', $this->module); - } - if ($this->uid) { - $select->condition('uid', $this->uid); - } - if ($this->name) { - $select->condition('name', $this->name); - } - if ($this->value) { - if (!is_scalar($this->value)) { - $select->condition('value', serialize($this->value)); - $select->condition('serialized', 1); - } - else { - $select->condition('value', $this->value); - } - } - foreach ($select->execute() as $row) { + foreach ($this->select->execute() as $row) { if ($row->serialized) { $row->value = unserialize($row->value); } diff --git a/core/modules/user/src/UserDataQueryInterface.php b/core/modules/user/src/UserDataQueryInterface.php index ac314ff..7271302 100644 --- a/core/modules/user/src/UserDataQueryInterface.php +++ b/core/modules/user/src/UserDataQueryInterface.php @@ -12,43 +12,53 @@ /** * Sets the module value. * - * @param string|null $module - * The name of the module the data is associated with. Use a NULL value to - * unset an already set module. + * @param string $module + * The name of the module the data is associated with. * * @return $this + * + * @throws \Drupal\user\Exception\ConditionAlreadySetException + * Exception thrown when the select object already has a module condition. */ public function setModule($module); /** * Sets the user account ID the data is associated with. * - * @param int|null $uid - * The user account ID the data is associated with. Use a NULL value to - * unset an already set user account ID. + * @param int $uid + * The user account ID the data is associated with. * * @return $this + * + * @throws \Drupal\user\Exception\ConditionAlreadySetException + * Exception thrown when the select object already has a user account ID + * condition. */ public function setUid($uid); /** * Sets the name of the data key. * - * @param string|null $name - * The name of the data key. Use a NULL value to unset an already set data - * key. + * @param string $name + * The name of the data key. * * @return $this + * + * @throws \Drupal\user\Exception\ConditionAlreadySetException + * Exception thrown when the select object already has a name condition. */ public function setName($name); /** * Sets the value stored. * - * @param mixed|null $value - * The value stored. Use a NULL value to unset an already set value. + * @param mixed $value + * The value stored. * * @return $this + * + * @throws \Drupal\user\Exception\ConditionAlreadySetException + * Exception thrown when the select object already has a value condition. */ public function setValue($value); diff --git a/core/modules/user/tests/src/Kernel/UserDataQueryTest.php b/core/modules/user/tests/src/Kernel/UserDataQueryTest.php index 1cefc04..2cb4602 100644 --- a/core/modules/user/tests/src/Kernel/UserDataQueryTest.php +++ b/core/modules/user/tests/src/Kernel/UserDataQueryTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\user\Kernel; use Drupal\KernelTests\KernelTestBase; +use Drupal\user\Exception\ConditionAlreadySetException; /** * @coversDefaultClass \Drupal\user\UserDataQuery @@ -104,78 +105,75 @@ public function userDataQueryProvider() { * @covers ::setUid * @covers ::setName * @covers ::setValue + * + * @dataProvider userDataQueryMultipleMethodCallsProvider + */ + public function testUserDataQueryMultipleMethodCalls($method, $name, $value) { + $this->setExpectedException(ConditionAlreadySetException::class, "Can not add another condition on '$name'"); + /** @var \Drupal\user\UserData $user_data */ + $user_data = \Drupal::service('user.data'); + $user_data_query = $user_data->getUserDataQuery(); + $user_data_query->$method($value); + $user_data_query->$method($value); + } + + /** + * Data provider for testUserDataQueryMultipleMethodCalls. + * + * @return array + */ + public function userDataQueryMultipleMethodCallsProvider() { + return [ + ['setUid', 'uid', 1], + ['setModule', 'module', 'test'], + ['setName', 'name', 'test'], + ['setValue', 'value', 'test'], + ]; + } + + /** + * @covers ::execute + */ + public function testEmpty() { + /** @var \Drupal\user\UserData $user_data */ + $user_data = \Drupal::service('user.data'); + $user_data_query = $user_data->getUserDataQuery(); + $user_data_query->setUid(101); + $this->assertSame([], iterator_to_array($user_data_query->execute())); + } + + /** + * @covers ::setValue * @covers ::execute */ - public function testUserDataQueryMultipleMethodCalls() { + public function testSetValue() { $contents = [ [ 'module' => 'test', 'uid' => '100', 'name' => 'foo', - 'value' => 'bar', - ], - [ - 'module' => 'another_test', - 'uid' => '101', - 'name' => 'foo', - 'value' => 'bar', - ], - [ - 'module' => 'test', - 'uid' => '101', - 'name' => 'foo2', - 'value' => 'bar2', + 'value' => NULL, ], ]; - /** @var \Drupal\user\UserData $user_data */ $user_data = \Drupal::service('user.data'); foreach ($contents as $row) { $user_data->set($row['module'], $row['uid'], $row['name'], $row['value']); } $user_data_query = $user_data->getUserDataQuery(); - $user_data_query->setModule('test'); - $this->assertEquals([(object) $contents[0], (object) $contents[2]], iterator_to_array($user_data_query->execute())); - - $user_data_query->setModule('another_test'); - $this->assertEquals([(object) $contents[1]], iterator_to_array($user_data_query->execute())); - $user_data_query->setValue('bar'); - $this->assertEquals([(object) $contents[1]], iterator_to_array($user_data_query->execute())); - - // Test unsetting something. - $user_data_query->setModule(NULL); - $this->assertEquals([(object) $contents[0], (object) $contents[1]], iterator_to_array($user_data_query->execute())); - - $user_data_query->setName('foo2'); - $this->assertEquals([], iterator_to_array($user_data_query->execute())); - - $user_data_query->setValue('bar2')->setModule('test'); - $this->assertEquals([(object) $contents[2]], iterator_to_array($user_data_query->execute())); - - $user_data_query->setName('does_not_exist'); - $this->assertEquals([], iterator_to_array($user_data_query->execute())); - - // Test unsetting everything. - $user_data_query->setName(NULL)->setValue(NULL)->setModule(NULL); - $this->assertEquals([(object) $contents[0], (object) $contents[1], (object) $contents[2]], iterator_to_array($user_data_query->execute())); - - $user_data_query->setUid(100); + // Test getting NULL values. + $user_data_query->setValue(NULL); $this->assertEquals([(object) $contents[0]], iterator_to_array($user_data_query->execute())); - $user_data_query->setUid(101); - $this->assertEquals([(object) $contents[1], (object) $contents[2]], iterator_to_array($user_data_query->execute())); - } - - /** - * @covers ::execute - */ - public function testEmpty() { - /** @var \Drupal\user\UserData $user_data */ - $user_data = \Drupal::service('user.data'); - $user_data_query = $user_data->getUserDataQuery(); - $user_data_query->setUid(101); - $this->assertSame([], iterator_to_array($user_data_query->execute())); + // Test that catching \Drupal\user\Exception\ConditionAlreadySetException + // and re-running the query does not break is the value is not scalar. + try { + $user_data_query->setValue(['value']); + } + catch (ConditionAlreadySetException $e) { + } + $this->assertEquals([(object) $contents[0]], iterator_to_array($user_data_query->execute())); } }