skip to Main Content

I am developing dynamic query builder for the admin users and for the same I am implementing POST API request to store all conditions together in database.

I am stuck with the validation of POST json. I tried different ways to prepare rule array.
Below is the code snippet for validation part.

Controller Action

public function saveConditions(ConditionRequest $request, Action $action): JsonResponse
{
    dd($request, $action);
}

ConditionRequest.php

use AppAdminHttpRequestsFormRequest;
use AppRulesConditionsRecursiveConditionRule;
class ConditionRequest extends FormRequest
{
    public function rules()
    {
        return [
            'conditions' => ['required', 'array'],
            'conditions.*' => [new RecursiveConditionRule()],
        ];
   }

}

RecursiveConditionRule.php

use IlluminateContractsValidationValidationRule;
use IlluminateSupportFacadesValidator;
use IlluminateValidationRule;
use IlluminateValidationValidationException;
use Closure;
class RecursiveConditionRule implements ValidationRule
{
    public function validate($attribute, $value, Closure $fail): void
    {
        $this->validateRecursive($attribute, $value);
    }
    protected function validateRecursive($attribute, $value): void
    {
        $conditionRules = [
            $attribute => 'array',
            "$attribute.type_slug" => ['required', Rule::in(ConditionType::all())],
            "$attribute.subjected_table" => ['required_if:type_slug,simple', 'string'],
            "$attribute.table_alias" => ['required_if:type_slug,simple', 'string'],
            "$attribute.logical_operator" => ['required', Rule::in(Operator::all())],
            "$attribute.relation_and_column" => ['nullable', 'string'],
            "$attribute.column_type" => ['nullable', 'string', Rule::in(ColumnType::all())],
            "$attribute.filter_criteria" => ['nullable', 'string', Rule::in(FilterCriteria::all())],
            "$attribute.filter_value" => ['nullable', 'string'],
            "$attribute.sub_conditions" => ['required_if:type_slug,group', 'array'],
        ];
        $validator = Validator::make([$attribute => $value], $conditionRules);
        if ($validator->fails()) {
            throw new ValidationException($validator);
        }
        if (isset($value['sub_conditions']) && is_array($value['sub_conditions'])) {
            foreach ($value['sub_conditions'] as $index => $subCondition) {
                $this->validateRecursive("$attribute.sub_conditions.$index", $subCondition);
            }
        }
    }
    public function message()
    {
        return 'The :attribute is invalid.';
    }
}

and below is my JSON request sample (It is with dummy data.)

{
    "conditions": [
        {
            "type_slug": "simple",
            "subjected_table": "users",
            "table_alias": "u",
            "logical_operator": "and",
            "relation_and_column": "name",
            "column_type": "string",
            "filter_criteria": "contains",
            "filter_value": "john"
        },
        {
            "type_slug": "group",
            "logical_operator": "or",
            "sub_conditions": [
                {
                    "type_slug": "simple",
                    "subjected_table": "orders",
                    "table_alias": "o",
                    "logical_operator": "and",
                    "relation_and_column": "invoice.total",
                    "column_type": "numeric",
                    "filter_criteria": "greater_than",
                    "filter_value": 100
                },
                {
                    "type_slug": "simple",
                    "subjected_table": "orders",
                    "table_alias": "o",
                    "logical_operator": "and",
                    "relation_and_column": "status",
                    "column_type": "string",
                    "filter_criteria": "contains",
                    "filter_value": "completed"
                },
                {
                    "type_slug": "group",
                    "logical_operator": "or",
                    "sub_conditions": [
                        {
                            "type_slug": "simple",
                            "subjected_table": "orders",
                            "table_alias": "o",
                            "logical_operator": "and",
                            "relation_and_column": "total",
                            "column_type": "numeric",
                            "filter_criteria": "greater_than",
                            "filter_value": 100
                        },
                        {
                            "type_slug": "simple",
                            "subjected_table": "orders",
                            "table_alias": "o",
                            "logical_operator": "and",
                            "relation_and_column": "status",
                            "column_type": "string",
                            "filter_criteria": "contains",
                            "filter_value": "completed"
                        }
                    ]
                }
            ]
        },
        {
            "type_slug": "simple",
            "subjected_table": "products",
            "table_alias": "p",
            "logical_operator": "or",
            "relation_and_column": "title",
            "column_type": "string",
            "filter_criteria": "contains",
            "filter_value": "apple"
        },
        {
            "type_slug": "group",
            "logical_operator": "and",
            "sub_conditions": [
                {
                    "type_slug": "simple",
                    "subjected_table": "orders",
                    "table_alias": "o",
                    "logical_operator": "and",
                    "relation_and_column": "total",
                    "column_type": "numeric",
                    "filter_criteria": "greater_than",
                    "filter_value": 200
                },
                {
                    "type_slug": "simple",
                    "subjected_table": "products",
                    "table_alias": "p",
                    "logical_operator": "or",
                    "relation_and_column": "title",
                    "column_type": "string",
                    "filter_criteria": "contains",
                    "filter_value": "banana"
                }
            ]
        }
    ]
}

It gives me below response::

{
    "message": "This field is required. (and 1 more error)",
    "errors": {
        "conditions.0.type_slug": [
            "This field is required."
        ],
        "conditions.0.logical_operator": [
            "This field is required."
        ]
    }
}

It seems validator is not receiving attribute and values properly for the recursive call. How can I fix this?

2

Answers


  1. I think your validation key should be: "$attribute.*.type_slug" rather than "$attribute.type_slug"

    However personally, I would focus in building the correct rules array from the input rather than trying to validate recursively. So you can loop through the input recursively and input rules into $rules depending on the given fields.

    Login or Signup to reply.
  2. Your validator fails since, given your $rules array, it expects input data in the form of:

    $input = [
      "conditions" => [
        ["type_slug": "simple", ...], [...], [...],
      ]
    ];
    

    but instead you are providing it with data in the form of:

    $input = [
      "conditions.0" => ["type_slug": "simple", ...],
      "conditions.1" => ["type_slug": "simple", ...],
    ];
    

    A simple work around it to remove the "dot" syntax from your input keys.

        protected function validateRecursive($attribute, $value): void
        {
            // replace "." with whatever making "conditions.0" into "conditions_0"
            // now Laravel does not assume you are using dot syntax
            $attribute = str_replace('.', '_', $attribute);
            $conditionRules = [
                $attribute => 'array',
                "$attribute.type_slug" => ['required', Rule::in(ConditionType::all())],
                "$attribute.subjected_table" => ['required_if:type_slug,simple', 'string'],
                "$attribute.table_alias" => ['required_if:type_slug,simple', 'string'],
                "$attribute.logical_operator" => ['required', Rule::in(Operator::all())],
                "$attribute.relation_and_column" => ['nullable', 'string'],
                "$attribute.column_type" => ['nullable', 'string', Rule::in(ColumnType::all())],
                "$attribute.filter_criteria" => ['nullable', 'string', Rule::in(FilterCriteria::all())],
                "$attribute.filter_value" => ['nullable', 'string'],
                "$attribute.sub_conditions" => ['required_if:type_slug,group', 'array'],
            ];
            $validator = Validator::make([$attribute => $value], $conditionRules);
            if ($validator->fails()) {
                throw new ValidationException($validator);
            }
            if (isset($value['sub_conditions']) && is_array($value['sub_conditions'])) {
                foreach ($value['sub_conditions'] as $index => $subCondition) {
                    $this->validateRecursive("$attribute.sub_conditions.$index", $subCondition);
                }
            }
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search