0

I would like to validate the following json in CI4:

{
    "field1": "some value",
    "array1": {
        "2876": "4358",
        "9904": "17"
    }
}

Array 1 can reasonably contain up to 50 key value pairs.

I have validation rules for both the keys and values in array1, for the purposes of this question lets require them both to be numeric.

The code I have so far is:

        $validation = \Config\Services::validation();

        $validation->setRules([
            'field1'   => 'required|max_length[20]',
            'array1.*'   => 'required|numeric',
        ]);

        if (! $validation->withRequest($this->request)->run()) {
            return $this->failValidationErrors($validation->getErrors());
        }

Alternatively based on an answer I've seen elsewhere:

        $validation = \Config\Services::validation();

        $validation->setRules([
            'field1'   => 'required|max_length[20]'
        ]);

        $to_validate = $this->request->getPost();
        foreach ($to_validate['array1'] as $key => $value) {
            $validation->setRule('array1.' . $key , null, 'required|numeric');
        }

        if (! $validation->withRequest($this->request)->run()) {
            return $this->failValidationErrors($validation->getErrors());
        }

This code successfully rejects a value of "abcd" but how do I reject a string key?

I could pre-process by putting the keys into their own array which can be validated with an array2.* rule but this seems like a simple use case which should work out of the box.

Also how should I implement the array size limit check?

3 Answers 3

0

array1.* validates the values of array1 and indeed not the keys.

The idea you had to provide the keys upfront is not bad at all, as it would turn the keys into values and you then would be able to validate them with the standard rules:

$to_validate = $this->request->getPost();
$to_validate['array1_keys'] = array_keys($to_validate['array1'] ?? []);
...

Then it is possible to use array1_keys.* next to array1.*.

I would start with that as you can find out how far you get with the standard rules.

Furthermore if you would like to dig deeper towards custom rules, I'd suggest to look into rules for file uploads as they refer to array fields. Their implementation (read the source code of them) should show how you can access array data including keys (IIRC). You can then experiment creating your own rules.

Sign up to request clarification or add additional context in comments.

Comments

0

This works:

        $validation = \Config\Services::validation();

        $validation->setRules([
            'field1'           => 'required|max_length[20]',
            'array1'           => 'required',
            'array1.*'         => 'required|numeric',
            'array1_keys.*'    => 'required|numeric',
            'array1_count'     => 'required|numeric|greater_than_equal_to[1]|less_than_equal_to[50]'
        ]);

        $to_validate                 = $this->request->getPost();
        $to_validate["array1_keys"]  = array_keys($to_validate['array1'] ?? []);
        $to_validate["array1_count"] = count($to_validate["array1_keys"]);

        if (! $validation->run($to_validate)) {
            return $this->failValidationErrors($validation->getErrors());
        }

That said it is hardly elegant so I will happily upvote a more concise alternative.

Comments

0

Create a custom rule and check any array with the desired constraints. https://codeigniter4.github.io/userguide/libraries/validation.html#creating-a-rule-class

Sample class:

class IdRules
{
    /**
     * Use as 'required|valid_ids[100,intval,string]'
     */
    public function valid_ids($value = null, string $params, array $data = [], ?string $error = null): bool
    {
        $rules = explode(',', $params);

        if (! is_array($value)) {
            $error = 'Not array';

            return false;
        }

        if (count($value) > $rules[0]) {
            $error = 'The array is too large';

            return false;
        }

        foreach ($value as $k => $v) {
            // validation `ids` and return `bool`
            if (gettype($k) === $rules[1]) {}
            if (gettype($v) === $rules[2]) {}
        }

        return true;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.