Home > Enterprise >  Laravel: Is it possible to apply different form validation rules to objects in an array
Laravel: Is it possible to apply different form validation rules to objects in an array

Time:10-11

I have a Laravel 9 API.

I am posting the following json to an endpoint.

{
  "shapes": [
    {
      "type": "Point",
      "coordinates": [1, 2]
    },
    {
      "type": "MultiPolygon",
      "coordinates": [
        [
          [
            [1, 2],
            [3, 4],
            [5, 6],
          ]
        ]
      ]
    }
  ]
}

I have some form validation (in a form request class) that needs to validate the coordinates given differently based on the type.

I.e. Apply the PointCoordinatesValidator rule if the shape's type is Point or apply the MultiPolygonCoordinatesValidator rule if the shape's type is MultiPolygon.

public function rules()
{
    return [
        'shapes' => 'required|array|min:1',
        'shapes.*.type' => [
            'required',
            'string',
            'in:Point,MultiPolygon',
        ],
        'shapes.*.coordinates' => [
            'required',
            request()->input('shapes.*.type') == 'Point' ?
                new PointCoordinatesValidator :
                new MultiPolygonCoordinatesValidator,
        ],
    ];
}

However, when running this, the custom MultiPolygonCoordinatesValidator rule is being applied to both shapes, not just the shape where it's type == MultiPolygon.

I can see that request()->input('shapes.*.type') is Point for shapes.0 and MultiPolygon for shapes.1

Am I expecting too much from the validation? Is it possible to validate the different items in the array differently based on a value in that array?

CodePudding user response:

It definitely is possible, but not by checking request()->input('shapes.*.type') == 'Point'; that * is a wildcard, and isn't really checked on each iteration.

You can construct the rules from the input, but it's a bit of an anti-pattern, and might have issues if type is null, or similar.

Give this a shot:

public function rules() {
  $rules = [
    'shapes' => 'required|array|min:1',
    'shapes.*.type' => [
      'required',
      'string',
      'in:Point,MultiPolygon',
    ]
  ];

  foreach(request()->input('shapes', []) as $index => $shape) {
    $rules["shapes.{$index}.coordinates"] = [
      'required',
      $shape['type'] == 'Point' ? new PointCoordinatesValidator() : new MultiPolygonCoordinatesValidator()
    ];
  }

  return $rules;
}

Using that, you'll get something like this:

[
  'shapes' => 'required|array|min:1',
  'shapes.*.type' => [
    'required',
    'string',
    'in:Point,MultiPolygon'
  ],
  'shapes.0.coordinates' => [
    'required',
    new PointCoordinatesValidator()
  ],
  'shapes.1.coordinates' => [
    'required',
    new MultiPolygonCoordinatesValidator()
  ]
  // And so on...
]

Your $rules array can get pretty large, depending on the number of shapes being uploaded, but this should allow you to explicitly target certain validation rules for each shape, depending on the type supplied.

  • Related