From 8907d036ce15bf1175c2d8479611ad47bf6b31e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amel=20Junuzovi=C4=87?= Date: Sun, 6 Oct 2024 21:03:45 +0200 Subject: [PATCH 1/9] feat: Add optional dbGroup support in validation rules for multiple database connections cs fixer docs format fix rules add test testIsNotUniqueWithDBConnectionAsParameter --- system/Validation/Rules.php | 48 +++++++++++------- system/Validation/StrictRules/Rules.php | 50 ++++++++++++------- .../StrictRules/DatabaseRelatedRulesTest.php | 26 ++++++++++ .../source/libraries/validation.rst | 16 ++++-- 4 files changed, 103 insertions(+), 37 deletions(-) diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index 06586a64190d..817308d53dd7 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -110,6 +110,7 @@ public function greater_than_equal_to($str, string $min): bool * accept only one filter). * * Example: + * is_not_unique[dbGroup.table.field,where_field,where_value] * is_not_unique[table.field,where_field,where_value] * is_not_unique[menu.id,active,1] * @@ -122,16 +123,21 @@ public function is_not_unique($str, string $field, array $data): bool } // Grab any data for exclusion of a single row. - [$field, $whereField, $whereValue] = array_pad( - explode(',', $field), - 3, - null - ); - - // Break the table and field apart - sscanf($field, '%[^.].%[^.]', $table, $field); + [$field, $whereField, $whereValue] = array_pad(explode(',', $field), 3, null); + + // Break the dbGroup, table and field apart from the field string + $parts = explode('.', $field, 3); + $numParts = count($parts); + + if ($numParts === 3) { + [$dbGroup, $table, $field] = $parts; + } elseif ($numParts === 2) { + [$table, $field] = $parts; + } else { + throw new InvalidArgumentException('The field must be in the format "table.field" or "dbGroup.table.field".'); + } - $row = Database::connect($data['DBGroup'] ?? null) + $row = Database::connect($dbGroup ?? $data['DBGroup'] ?? null) ->table($table) ->select('1') ->where($field, $str) @@ -170,6 +176,7 @@ public function in_list($value, string $list): bool * record updates. * * Example: + * is_unique[dbGroup.table.field,ignore_field,ignore_value] * is_unique[table.field,ignore_field,ignore_value] * is_unique[users.email,id,5] * @@ -181,15 +188,22 @@ public function is_unique($str, string $field, array $data): bool $str = (string) $str; } - [$field, $ignoreField, $ignoreValue] = array_pad( - explode(',', $field), - 3, - null - ); - - sscanf($field, '%[^.].%[^.]', $table, $field); + // Grab any data for exclusion of a single row. + [$field, $ignoreField, $ignoreValue] = array_pad(explode(',', $field), 3, null); + + // Break the dbGroup, table and field apart from the field string + $parts = explode('.', $field, 3); + $numParts = count($parts); + + if ($numParts === 3) { + [$dbGroup, $table, $field] = $parts; + } elseif ($numParts === 2) { + [$table, $field] = $parts; + } else { + throw new InvalidArgumentException('The field must be in the format "table.field" or "dbGroup.table.field".'); + } - $row = Database::connect($data['DBGroup'] ?? null) + $row = Database::connect($dbGroup ?? $data['DBGroup'] ?? null) ->table($table) ->select('1') ->where($field, $str) diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php index ec02a4e0a0ac..2bc1dc8e1b35 100644 --- a/system/Validation/StrictRules/Rules.php +++ b/system/Validation/StrictRules/Rules.php @@ -16,6 +16,7 @@ use CodeIgniter\Helpers\Array\ArrayHelper; use CodeIgniter\Validation\Rules as NonStrictRules; use Config\Database; +use InvalidArgumentException; /** * Validation Rules. @@ -134,6 +135,7 @@ public function greater_than_equal_to($str, string $min): bool * accept only one filter). * * Example: + * is_not_unique[dbGroup.table.field,where_field,where_value] * is_not_unique[table.field,where_field,where_value] * is_not_unique[menu.id,active,1] * @@ -146,16 +148,21 @@ public function is_not_unique($str, string $field, array $data): bool } // Grab any data for exclusion of a single row. - [$field, $whereField, $whereValue] = array_pad( - explode(',', $field), - 3, - null - ); - - // Break the table and field apart - sscanf($field, '%[^.].%[^.]', $table, $field); + [$field, $whereField, $whereValue] = array_pad(explode(',', $field), 3, null); + + // Break the dbGroup, table and field apart from the field string + $parts = explode('.', $field, 3); + $numParts = count($parts); + + if ($numParts === 3) { + [$dbGroup, $table, $field] = $parts; + } elseif ($numParts === 2) { + [$table, $field] = $parts; + } else { + throw new InvalidArgumentException('The field must be in the format "table.field" or "dbGroup.table.field".'); + } - $row = Database::connect($data['DBGroup'] ?? null) + $row = Database::connect($dbGroup ?? $data['DBGroup'] ?? null) ->table($table) ->select('1') ->where($field, $str) @@ -196,6 +203,7 @@ public function in_list($value, string $list): bool * record updates. * * Example: + * is_unique[dbGroup.table.field,ignore_field,ignore_value] * is_unique[table.field,ignore_field,ignore_value] * is_unique[users.email,id,5] * @@ -207,15 +215,23 @@ public function is_unique($str, string $field, array $data): bool return false; } - [$field, $ignoreField, $ignoreValue] = array_pad( - explode(',', $field), - 3, - null - ); - - sscanf($field, '%[^.].%[^.]', $table, $field); + // Grab any data for exclusion of a single row. + [$field, $ignoreField, $ignoreValue] = array_pad(explode(',', $field), 3, null); + + // Break the dbGroup, table and field apart from the field string + // Break the dbGroup, table and field apart from the field string + $parts = explode('.', $field, 3); + $numParts = count($parts); + + if ($numParts === 3) { + [$dbGroup, $table, $field] = $parts; + } elseif ($numParts === 2) { + [$table, $field] = $parts; + } else { + throw new InvalidArgumentException('The field must be in the format "table.field" or "dbGroup.table.field".'); + } - $row = Database::connect($data['DBGroup'] ?? null) + $row = Database::connect($dbGroup ?? $data['DBGroup'] ?? null) ->table($table) ->select('1') ->where($field, $str) diff --git a/tests/system/Validation/StrictRules/DatabaseRelatedRulesTest.php b/tests/system/Validation/StrictRules/DatabaseRelatedRulesTest.php index 5f8472ec29f0..f05a55ba2f64 100644 --- a/tests/system/Validation/StrictRules/DatabaseRelatedRulesTest.php +++ b/tests/system/Validation/StrictRules/DatabaseRelatedRulesTest.php @@ -97,6 +97,17 @@ public function testIsUniqueWithDBConnection(): void $this->assertTrue($result); } + public function testIsUniqueWithDBConnectionAsParameter(): void + { + $this->validation->setRules(['email' => 'is_unique[tests.user.email]']); + + $data = ['email' => 'derek@world.co.uk']; + + $result = $this->validation->run($data); + + $this->assertTrue($result); + } + public function testIsUniqueWithInvalidDBGroup(): void { $this->expectException(InvalidArgumentException::class); @@ -296,4 +307,19 @@ public function testIsNotUniqueByManualRun(): void $this->assertTrue($this->createRules()->is_not_unique('deva@example.com', 'user.email,id,{id}', [])); } + + public function testIsNotUniqueWithDBConnectionAsParameter(): void + { + Database::connect() + ->table('user') + ->insert([ + 'name' => 'Derek Travis', + 'email' => 'derek@world.com', + 'country' => 'Elbonia', + ]); + + $data = ['email' => 'derek@world.com']; + $this->validation->setRules(['email' => 'is_not_unique[tests.user.email]']); + $this->assertTrue($this->validation->run($data)); + } } diff --git a/user_guide_src/source/libraries/validation.rst b/user_guide_src/source/libraries/validation.rst index 25111c2cdcef..515a37a3277b 100644 --- a/user_guide_src/source/libraries/validation.rst +++ b/user_guide_src/source/libraries/validation.rst @@ -886,6 +886,13 @@ Available Rules .. note:: Rule is a string; there must be **no spaces** between the parameters, especially the ``is_unique`` rule. There can be no spaces before and after ``ignore_value``. +.. note:: Since version v4.5.6, you can optionally include ``dbGroup`` in validation rules like ``is_unique`` and ``is_not_unique``. + This allows specifying which database connection to use during validation, giving you more flexibility when working with multiple databases. + To use ``dbGroup``, you place it before the table name in the validation rule, like this: + ``is_unique[dbGroup.table.field,ignore_field,ignore_value]`` or ``is_not_unique[dbGroup.table.field,where_field,where_value]``. + In these examples, ``dbGroup`` is used to select the appropriate database connection for the specified table and field. + If ``dbGroup`` is not provided, the default database group will be used. + .. literalinclude:: validation/038.php :lines: 2- @@ -949,13 +956,16 @@ is_natural No Fails if field contains anything other than is_natural_no_zero No Fails if field contains anything other than a natural number, except zero: ``1``, ``2``, ``3``, etc. -is_not_unique Yes Checks the database to see if the given value ``is_not_unique[table.field,where_field,where_value]`` +is_not_unique Yes Checks the database to see if the given value ``is_not_unique[table.field,where_field,where_value]`` or ``is_not_unique[dbGroup.table.field,where_field,where_value]`` exists. Can ignore records by field/value to filter (currently accept only one filter). -is_unique Yes Checks if this field value exists in the ``is_unique[table.field,ignore_field,ignore_value]`` + (Since v4.5.6, you can optionally pass + the dbGroup as a parameter) +is_unique Yes Checks if this field value exists in the ``is_unique[table.field,ignore_field,ignore_value]`` or ``is_unique[dbGroup.table.field,ignore_field,ignore_value]`` database. Optionally set a column and value to ignore, useful when updating records to - ignore itself. + ignore itself. (Since v4.5.6, you can + optionally pass the dbGroup as a parameter) less_than Yes Fails if field is greater than or equal to ``less_than[8]`` the parameter value or not numeric. less_than_equal_to Yes Fails if field is greater than the parameter ``less_than_equal_to[8]`` From f4f86b33951412de3ee54952dfaa882ad3baf31d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amel=20Junuzovi=C4=87?= Date: Sun, 6 Oct 2024 21:58:30 +0200 Subject: [PATCH 2/9] docs: update v4.5.6.rst --- user_guide_src/source/changelogs/v4.5.6.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/user_guide_src/source/changelogs/v4.5.6.rst b/user_guide_src/source/changelogs/v4.5.6.rst index 33e3e5a1a0ab..e9e7d224d014 100644 --- a/user_guide_src/source/changelogs/v4.5.6.rst +++ b/user_guide_src/source/changelogs/v4.5.6.rst @@ -22,6 +22,9 @@ Message Changes Changes ******* +- **Validation:** Rule ``is_unique`` and ``is_not_unique`` now accept an optional + ``dbGroup`` as parameter. + ************ Deprecations ************ From 325ffdc0d990476ee89672d2b45a77772cbd4801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amel=20Junuzovi=C4=87?= Date: Sun, 6 Oct 2024 22:12:09 +0200 Subject: [PATCH 3/9] docs: fix tabs --- user_guide_src/source/changelogs/v4.5.6.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.5.6.rst b/user_guide_src/source/changelogs/v4.5.6.rst index e9e7d224d014..ba415de7e00c 100644 --- a/user_guide_src/source/changelogs/v4.5.6.rst +++ b/user_guide_src/source/changelogs/v4.5.6.rst @@ -23,7 +23,7 @@ Changes ******* - **Validation:** Rule ``is_unique`` and ``is_not_unique`` now accept an optional - ``dbGroup`` as parameter. + ``dbGroup`` as parameter. ************ Deprecations From 2fdc559387a6d8f2445d6e2f1d55c7b38a495b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amel=20Junuzovi=C4=87?= Date: Mon, 7 Oct 2024 22:29:25 +0200 Subject: [PATCH 4/9] docs update for v4.6.0 --- user_guide_src/source/changelogs/v4.5.6.rst | 3 --- user_guide_src/source/changelogs/v4.6.0.rst | 2 ++ user_guide_src/source/libraries/validation.rst | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.5.6.rst b/user_guide_src/source/changelogs/v4.5.6.rst index ba415de7e00c..33e3e5a1a0ab 100644 --- a/user_guide_src/source/changelogs/v4.5.6.rst +++ b/user_guide_src/source/changelogs/v4.5.6.rst @@ -22,9 +22,6 @@ Message Changes Changes ******* -- **Validation:** Rule ``is_unique`` and ``is_not_unique`` now accept an optional - ``dbGroup`` as parameter. - ************ Deprecations ************ diff --git a/user_guide_src/source/changelogs/v4.6.0.rst b/user_guide_src/source/changelogs/v4.6.0.rst index d3dc4529d512..be51ebaae245 100644 --- a/user_guide_src/source/changelogs/v4.6.0.rst +++ b/user_guide_src/source/changelogs/v4.6.0.rst @@ -219,6 +219,8 @@ Libraries See :ref:`FileCollection::retainMultiplePatterns() `. - **Validation:** Added ``min_dims`` validation rule to ``FileRules`` class. See :ref:`Validation `. +- **Validation:** Rule ``is_unique`` and ``is_not_unique`` now accept an optional + ``dbGroup`` as parameter. See :ref:`Validation `. Helpers and Functions ===================== diff --git a/user_guide_src/source/libraries/validation.rst b/user_guide_src/source/libraries/validation.rst index 515a37a3277b..2111e5c526b1 100644 --- a/user_guide_src/source/libraries/validation.rst +++ b/user_guide_src/source/libraries/validation.rst @@ -886,7 +886,7 @@ Available Rules .. note:: Rule is a string; there must be **no spaces** between the parameters, especially the ``is_unique`` rule. There can be no spaces before and after ``ignore_value``. -.. note:: Since version v4.5.6, you can optionally include ``dbGroup`` in validation rules like ``is_unique`` and ``is_not_unique``. +.. note:: Since version v4.6.0, you can optionally include ``dbGroup`` in validation rules like ``is_unique`` and ``is_not_unique``. This allows specifying which database connection to use during validation, giving you more flexibility when working with multiple databases. To use ``dbGroup``, you place it before the table name in the validation rule, like this: ``is_unique[dbGroup.table.field,ignore_field,ignore_value]`` or ``is_not_unique[dbGroup.table.field,where_field,where_value]``. @@ -959,12 +959,12 @@ is_natural_no_zero No Fails if field contains anything other than is_not_unique Yes Checks the database to see if the given value ``is_not_unique[table.field,where_field,where_value]`` or ``is_not_unique[dbGroup.table.field,where_field,where_value]`` exists. Can ignore records by field/value to filter (currently accept only one filter). - (Since v4.5.6, you can optionally pass + (Since v4.6.0, you can optionally pass the dbGroup as a parameter) is_unique Yes Checks if this field value exists in the ``is_unique[table.field,ignore_field,ignore_value]`` or ``is_unique[dbGroup.table.field,ignore_field,ignore_value]`` database. Optionally set a column and value to ignore, useful when updating records to - ignore itself. (Since v4.5.6, you can + ignore itself. (Since v4.6.0, you can optionally pass the dbGroup as a parameter) less_than Yes Fails if field is greater than or equal to ``less_than[8]`` the parameter value or not numeric. @@ -1104,7 +1104,7 @@ min_dims Yes Fails if the minimum width and height of an parameter is the field name. The second is the width, and the third is the height. Will also fail if the file cannot be determined - to be an image. (This rule was added in + to be an image. (This rule was added in v4.6.0.) mime_in Yes Fails if the file's mime type is not one ``mime_in[field_name,image/png,image/jpeg]`` listed in the parameters. From 35f7b1e5b444d9af45d2b9eecb208e869791affc Mon Sep 17 00:00:00 2001 From: maniaba <61078470+maniaba@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:44:14 +0200 Subject: [PATCH 5/9] Update validation.rst --- user_guide_src/source/libraries/validation.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/user_guide_src/source/libraries/validation.rst b/user_guide_src/source/libraries/validation.rst index 2111e5c526b1..96416355bb22 100644 --- a/user_guide_src/source/libraries/validation.rst +++ b/user_guide_src/source/libraries/validation.rst @@ -886,13 +886,6 @@ Available Rules .. note:: Rule is a string; there must be **no spaces** between the parameters, especially the ``is_unique`` rule. There can be no spaces before and after ``ignore_value``. -.. note:: Since version v4.6.0, you can optionally include ``dbGroup`` in validation rules like ``is_unique`` and ``is_not_unique``. - This allows specifying which database connection to use during validation, giving you more flexibility when working with multiple databases. - To use ``dbGroup``, you place it before the table name in the validation rule, like this: - ``is_unique[dbGroup.table.field,ignore_field,ignore_value]`` or ``is_not_unique[dbGroup.table.field,where_field,where_value]``. - In these examples, ``dbGroup`` is used to select the appropriate database connection for the specified table and field. - If ``dbGroup`` is not provided, the default database group will be used. - .. literalinclude:: validation/038.php :lines: 2- From 3cd43979daa8d6da44ea586e19d054c5cabda2d4 Mon Sep 17 00:00:00 2001 From: maniaba <61078470+maniaba@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:20:35 +0100 Subject: [PATCH 6/9] Update user_guide_src/source/changelogs/v4.6.0.rst Co-authored-by: Michal Sniatala --- user_guide_src/source/changelogs/v4.6.0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.6.0.rst b/user_guide_src/source/changelogs/v4.6.0.rst index be51ebaae245..0a3ed22fa37d 100644 --- a/user_guide_src/source/changelogs/v4.6.0.rst +++ b/user_guide_src/source/changelogs/v4.6.0.rst @@ -219,8 +219,8 @@ Libraries See :ref:`FileCollection::retainMultiplePatterns() `. - **Validation:** Added ``min_dims`` validation rule to ``FileRules`` class. See :ref:`Validation `. -- **Validation:** Rule ``is_unique`` and ``is_not_unique`` now accept an optional - ``dbGroup`` as parameter. See :ref:`Validation `. +- **Validation:** Rules: ``is_unique`` and ``is_not_unique`` now accept the optional + ``dbGroup`` as part of the first parameter. See :ref:`Validation `. Helpers and Functions ===================== From 180eebf1a478c973a5ff2bdd9405337f791a71e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amel=20Junuzovi=C4=87?= Date: Wed, 13 Nov 2024 09:34:02 +0100 Subject: [PATCH 7/9] Add a test case for `InvalidArgumentException` and remove the duplicate comment. --- system/Validation/StrictRules/Rules.php | 1 - .../StrictRules/DatabaseRelatedRulesTest.php | 24 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php index 2bc1dc8e1b35..5c2825f1db04 100644 --- a/system/Validation/StrictRules/Rules.php +++ b/system/Validation/StrictRules/Rules.php @@ -218,7 +218,6 @@ public function is_unique($str, string $field, array $data): bool // Grab any data for exclusion of a single row. [$field, $ignoreField, $ignoreValue] = array_pad(explode(',', $field), 3, null); - // Break the dbGroup, table and field apart from the field string // Break the dbGroup, table and field apart from the field string $parts = explode('.', $field, 3); $numParts = count($parts); diff --git a/tests/system/Validation/StrictRules/DatabaseRelatedRulesTest.php b/tests/system/Validation/StrictRules/DatabaseRelatedRulesTest.php index f05a55ba2f64..bd0be4ccc223 100644 --- a/tests/system/Validation/StrictRules/DatabaseRelatedRulesTest.php +++ b/tests/system/Validation/StrictRules/DatabaseRelatedRulesTest.php @@ -108,6 +108,18 @@ public function testIsUniqueWithDBConnectionAsParameter(): void $this->assertTrue($result); } + public function testIsUniqueWrongParametersThrowInvalidArgumentException(): void + { + $this->validation->setRules(['email' => 'is_unique[invalid_parameters]']); + + $data = ['email' => 'derek@world.co.uk']; + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The field must be in the format "table.field" or "dbGroup.table.field".'); + + $this->validation->run($data); + } + public function testIsUniqueWithInvalidDBGroup(): void { $this->expectException(InvalidArgumentException::class); @@ -322,4 +334,16 @@ public function testIsNotUniqueWithDBConnectionAsParameter(): void $this->validation->setRules(['email' => 'is_not_unique[tests.user.email]']); $this->assertTrue($this->validation->run($data)); } + + public function testIsNotUniqueWrongParametersThrowInvalidArgumentException(): void + { + $this->validation->setRules(['email' => 'is_not_unique[invalid_parameters]']); + + $data = ['email' => 'derek@world.co.uk']; + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The field must be in the format "table.field" or "dbGroup.table.field".'); + + $this->validation->run($data); + } } From 440545e34bd705d54b8f393e535de42a29308b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amel=20Junuzovi=C4=87?= Date: Thu, 14 Nov 2024 07:53:08 +0100 Subject: [PATCH 8/9] refactor: code for is_unique and is_not_unique methods; extracted common code into prepareUniqueQuery method --- system/Validation/Rules.php | 80 ++++++++++++------------- system/Validation/StrictRules/Rules.php | 63 +------------------ 2 files changed, 40 insertions(+), 103 deletions(-) diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index 817308d53dd7..7cf8570a9317 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -13,6 +13,7 @@ namespace CodeIgniter\Validation; +use CodeIgniter\Database\BaseBuilder; use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\Helpers\Array\ArrayHelper; use Config\Database; @@ -118,40 +119,17 @@ public function greater_than_equal_to($str, string $min): bool */ public function is_not_unique($str, string $field, array $data): bool { - if (! is_string($str) && $str !== null) { - $str = (string) $str; - } - - // Grab any data for exclusion of a single row. - [$field, $whereField, $whereValue] = array_pad(explode(',', $field), 3, null); - - // Break the dbGroup, table and field apart from the field string - $parts = explode('.', $field, 3); - $numParts = count($parts); - - if ($numParts === 3) { - [$dbGroup, $table, $field] = $parts; - } elseif ($numParts === 2) { - [$table, $field] = $parts; - } else { - throw new InvalidArgumentException('The field must be in the format "table.field" or "dbGroup.table.field".'); - } - - $row = Database::connect($dbGroup ?? $data['DBGroup'] ?? null) - ->table($table) - ->select('1') - ->where($field, $str) - ->limit(1); + [$builder, $whereField, $whereValue] = $this->prepareUniqueQuery($str, $field, $data); if ( $whereField !== null && $whereField !== '' && $whereValue !== null && $whereValue !== '' && ! preg_match('/^\{(\w+)\}$/', $whereValue) ) { - $row = $row->where($whereField, $whereValue); + $builder = $builder->where($whereField, $whereValue); } - return $row->get()->getRow() !== null; + return $builder->get()->getRow() !== null; } /** @@ -184,14 +162,38 @@ public function in_list($value, string $list): bool */ public function is_unique($str, string $field, array $data): bool { - if (! is_string($str) && $str !== null) { - $str = (string) $str; + [$builder, $ignoreField, $ignoreValue] = $this->prepareUniqueQuery($str, $field, $data); + + if ( + $ignoreField !== null && $ignoreField !== '' + && $ignoreValue !== null && $ignoreValue !== '' + && ! preg_match('/^\{(\w+)\}$/', $ignoreValue) + ) { + $builder = $builder->where("{$ignoreField} !=", $ignoreValue); + } + + return $builder->get()->getRow() === null; + } + + /** + * Prepares the database query for uniqueness checks. + * + * @param mixed $value The value to check. + * @param string $field The field parameters. + * @param array $data Additional data. + * + * @return array{0: BaseBuilder, 1: string|null, 2: string|null} + */ + private function prepareUniqueQuery($value, string $field, array $data): array + { + if (! is_string($value) && $value !== null) { + $value = (string) $value; } - // Grab any data for exclusion of a single row. - [$field, $ignoreField, $ignoreValue] = array_pad(explode(',', $field), 3, null); + // Split the field parameters and pad the array to ensure three elements. + [$field, $extraField, $extraValue] = array_pad(explode(',', $field), 3, null); - // Break the dbGroup, table and field apart from the field string + // Parse the field string to extract dbGroup, table, and field. $parts = explode('.', $field, 3); $numParts = count($parts); @@ -203,21 +205,15 @@ public function is_unique($str, string $field, array $data): bool throw new InvalidArgumentException('The field must be in the format "table.field" or "dbGroup.table.field".'); } - $row = Database::connect($dbGroup ?? $data['DBGroup'] ?? null) + // Connect to the database. + $dbGroup ??= $data['DBGroup'] ?? null; + $builder = Database::connect($dbGroup) ->table($table) ->select('1') - ->where($field, $str) + ->where($field, $value) ->limit(1); - if ( - $ignoreField !== null && $ignoreField !== '' - && $ignoreValue !== null && $ignoreValue !== '' - && ! preg_match('/^\{(\w+)\}$/', $ignoreValue) - ) { - $row = $row->where("{$ignoreField} !=", $ignoreValue); - } - - return $row->get()->getRow() === null; + return [$builder, $extraField, $extraValue]; } /** diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php index 5c2825f1db04..7327b54bb856 100644 --- a/system/Validation/StrictRules/Rules.php +++ b/system/Validation/StrictRules/Rules.php @@ -16,7 +16,6 @@ use CodeIgniter\Helpers\Array\ArrayHelper; use CodeIgniter\Validation\Rules as NonStrictRules; use Config\Database; -use InvalidArgumentException; /** * Validation Rules. @@ -147,36 +146,7 @@ public function is_not_unique($str, string $field, array $data): bool return false; } - // Grab any data for exclusion of a single row. - [$field, $whereField, $whereValue] = array_pad(explode(',', $field), 3, null); - - // Break the dbGroup, table and field apart from the field string - $parts = explode('.', $field, 3); - $numParts = count($parts); - - if ($numParts === 3) { - [$dbGroup, $table, $field] = $parts; - } elseif ($numParts === 2) { - [$table, $field] = $parts; - } else { - throw new InvalidArgumentException('The field must be in the format "table.field" or "dbGroup.table.field".'); - } - - $row = Database::connect($dbGroup ?? $data['DBGroup'] ?? null) - ->table($table) - ->select('1') - ->where($field, $str) - ->limit(1); - - if ( - $whereField !== null && $whereField !== '' - && $whereValue !== null && $whereValue !== '' - && ! preg_match('/^\{(\w+)\}$/', $whereValue) - ) { - $row = $row->where($whereField, $whereValue); - } - - return $row->get()->getRow() !== null; + return $this->nonStrictRules->is_not_unique($str, $field, $data); } /** @@ -215,36 +185,7 @@ public function is_unique($str, string $field, array $data): bool return false; } - // Grab any data for exclusion of a single row. - [$field, $ignoreField, $ignoreValue] = array_pad(explode(',', $field), 3, null); - - // Break the dbGroup, table and field apart from the field string - $parts = explode('.', $field, 3); - $numParts = count($parts); - - if ($numParts === 3) { - [$dbGroup, $table, $field] = $parts; - } elseif ($numParts === 2) { - [$table, $field] = $parts; - } else { - throw new InvalidArgumentException('The field must be in the format "table.field" or "dbGroup.table.field".'); - } - - $row = Database::connect($dbGroup ?? $data['DBGroup'] ?? null) - ->table($table) - ->select('1') - ->where($field, $str) - ->limit(1); - - if ( - $ignoreField !== null && $ignoreField !== '' - && $ignoreValue !== null && $ignoreValue !== '' - && ! preg_match('/^\{(\w+)\}$/', $ignoreValue) - ) { - $row = $row->where("{$ignoreField} !=", $ignoreValue); - } - - return $row->get()->getRow() === null; + return $this->nonStrictRules->is_unique($str, $field, $data); } /** From 7ddbd13fcb066e5c4ea2c240e1084fd35a7d2713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amel=20Junuzovi=C4=87?= Date: Thu, 14 Nov 2024 07:58:41 +0100 Subject: [PATCH 9/9] rector and phpstan fix --- system/Validation/Rules.php | 6 +++--- system/Validation/StrictRules/Rules.php | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/system/Validation/Rules.php b/system/Validation/Rules.php index 7cf8570a9317..30fab3539ecd 100644 --- a/system/Validation/Rules.php +++ b/system/Validation/Rules.php @@ -178,9 +178,9 @@ public function is_unique($str, string $field, array $data): bool /** * Prepares the database query for uniqueness checks. * - * @param mixed $value The value to check. - * @param string $field The field parameters. - * @param array $data Additional data. + * @param mixed $value The value to check. + * @param string $field The field parameters. + * @param array $data Additional data. * * @return array{0: BaseBuilder, 1: string|null, 2: string|null} */ diff --git a/system/Validation/StrictRules/Rules.php b/system/Validation/StrictRules/Rules.php index 7327b54bb856..e836f4e4c127 100644 --- a/system/Validation/StrictRules/Rules.php +++ b/system/Validation/StrictRules/Rules.php @@ -15,7 +15,6 @@ use CodeIgniter\Helpers\Array\ArrayHelper; use CodeIgniter\Validation\Rules as NonStrictRules; -use Config\Database; /** * Validation Rules.