skip to Main Content

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?

2

Answers


  1. 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.

    Login or Signup to reply.
  2. Just pass the array element in and then implement the type check in your rule:

    'shapes.*' => [
                   'required',
                    new CoordinatesValidator(),
                  ],
    

    Then, in the rule, the $value will contain the type and the coordinates.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search