Repository: codeigniter4/CodeIgniter4 (PHP)

Issue:
Bug: Custom validation error is cleared when setting rule twice
### PHP Version

7.4

### CodeIgniter4 Version

4.2.1

### CodeIgniter4 Installation Method

Composer (using `codeigniter4/appstarter`)

### Which operating systems have you tested for this bug?

Linux

### Which server did you use?

fpm-fcgi

### Database

_No response_

### What happened?

Using the Built-in Validation class,
custom validation error message previously set is cleared when setting another rule.


### Steps to Reproduce

```php
$va = \Config\Services::validation();
$va->setRule("field1", "Field 1", ["required"], ["required" => "You must input field 1"] );
$va->setRule("field2", "Field 2", ["required"], ["required" => "You must input field 2"] );
$va->run([]);
$errors = $va->getErrors();
echo $errors["field1"] . "/" . $errors["field2"];
```


### Expected Output

```
You must input field 1/You must input field 2
```

but

```
The field1 is required. / You must input field 2
```


### Anything else?

It seems that the `setRule()` method calls `getRules()` internally to get rules already set, but it does not supply `customErrors`.




Discussion:
Thank you for good bug reporting.

I sent a PR https://github.com/codeigniter4/CodeIgniter4/pull/6241
Please check if you can.




Patch:
diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php
index eb91114ca218..2b190ef2963a 100644
--- a/system/Validation/Validation.php
+++ b/system/Validation/Validation.php
@@ -399,7 +399,7 @@ public function setRule(string $field, ?string $label, $rules, array $errors = [
             $ruleSet[$field]['errors'] = $errors;
         }
 
-        $this->setRules($ruleSet + $this->getRules());
+        $this->setRules($ruleSet + $this->getRules(), $this->customErrors);
 
         return $this;
     }


Test patch:
diff --git a/tests/system/Validation/StrictRules/ValidationTest.php b/tests/system/Validation/StrictRules/ValidationTest.php
index 865d3a026d9c..ba0e8c6ec0c5 100644
--- a/tests/system/Validation/StrictRules/ValidationTest.php
+++ b/tests/system/Validation/StrictRules/ValidationTest.php
@@ -202,17 +202,50 @@ public function testRunReturnsLocalizedErrors(): void
 
     public function testRunWithCustomErrors(): void
     {
-        $data = ['foo' => 'notanumber'];
-
+        $data = [
+            'foo' => 'notanumber',
+            'bar' => 'notanumber',
+        ];
         $messages = [
             'foo' => [
                 'is_numeric' => 'Nope. Not a number.',
             ],
+            'bar' => [
+                'is_numeric' => 'No. Not a number.',
+            ],
         ];
+        $this->validation->setRules(['foo' => 'is_numeric', 'bar' => 'is_numeric'], $messages);
+        $this->validation->run($data);
+
+        $this->assertSame('Nope. Not a number.', $this->validation->getError('foo'));
+        $this->assertSame('No. Not a number.', $this->validation->getError('bar'));
+    }
 
-        $this->validation->setRules(['foo' => 'is_numeric'], $messages);
+    /**
+     * @see https://github.com/codeigniter4/CodeIgniter4/issues/6239
+     */
+    public function testSetRuleWithCustomErrors(): void
+    {
+        $data = [
+            'foo' => 'notanumber',
+            'bar' => 'notanumber',
+        ];
+        $this->validation->setRule(
+            'foo',
+            'Foo',
+            ['foo'        => 'is_numeric'],
+            ['is_numeric' => 'Nope. Not a number.']
+        );
+        $this->validation->setRule(
+            'bar',
+            'Bar',
+            ['bar'        => 'is_numeric'],
+            ['is_numeric' => 'Nope. Not a number.']
+        );
         $this->validation->run($data);
+
         $this->assertSame('Nope. Not a number.', $this->validation->getError('foo'));
+        $this->assertSame('Nope. Not a number.', $this->validation->getError('bar'));
     }
 
     public function testCheck(): void
diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php
index c43b7d997d88..867dd6475d8e 100644
--- a/tests/system/Validation/ValidationTest.php
+++ b/tests/system/Validation/ValidationTest.php
@@ -305,17 +305,50 @@ public function testRunReturnsLocalizedErrors(): void
 
     public function testRunWithCustomErrors(): void
     {
-        $data = ['foo' => 'notanumber'];
-
+        $data = [
+            'foo' => 'notanumber',
+            'bar' => 'notanumber',
+        ];
         $messages = [
             'foo' => [
                 'is_numeric' => 'Nope. Not a number.',
             ],
+            'bar' => [
+                'is_numeric' => 'No. Not a number.',
+            ],
         ];
+        $this->validation->setRules(['foo' => 'is_numeric', 'bar' => 'is_numeric'], $messages);
+        $this->validation->run($data);
+
+        $this->assertSame('Nope. Not a number.', $this->validation->getError('foo'));
+        $this->assertSame('No. Not a number.', $this->validation->getError('bar'));
+    }
 
-        $this->validation->setRules(['foo' => 'is_numeric'], $messages);
+    /**
+     * @see https://github.com/codeigniter4/CodeIgniter4/issues/6239
+     */
+    public function testSetRuleWithCustomErrors(): void
+    {
+        $data = [
+            'foo' => 'notanumber',
+            'bar' => 'notanumber',
+        ];
+        $this->validation->setRule(
+            'foo',
+            'Foo',
+            ['foo'        => 'is_numeric'],
+            ['is_numeric' => 'Nope. Not a number.']
+        );
+        $this->validation->setRule(
+            'bar',
+            'Bar',
+            ['bar'        => 'is_numeric'],
+            ['is_numeric' => 'Nope. Not a number.']
+        );
         $this->validation->run($data);
+
         $this->assertSame('Nope. Not a number.', $this->validation->getError('foo'));
+        $this->assertSame('Nope. Not a number.', $this->validation->getError('bar'));
     }
 
     public function testCheck(): void