12

I have a POST endpoint on my Laravel 5.5 API, I used postman to test my route and here is the problem. I want to send an array of the same kind of object, like this:

[
  { 
   "name":"test",
   "tag":"TEST"
  },
  {
   "name":"test2",
   "tag":"TEST_2"
  }
]

But I can't manage to validate it properly.

in my controller, I have this:

$validator = Validator::make($requests->all(), [
 
   'name' => 'required|string',
   'tag' => 'required|string'
]);

if($validator->fails()) 
   //Warn user for errors 

I also tried with the simple

$this->validate($requests->all() .... )

I tried renaming the rules with '.name' and '*.name' but no success.

I tried replacing $request->all() with $request->input()but no success.

I also tried to loop over it but I get exceptions

foreach($request as $req){

  $validator = Validator::make($req ....) 

  //rest of the code
}

On the other hand, I can retrieve the data like this $datas = $request->all() and store them but there is no validation.

The only solution that seems to be working is naming my array:

{
 "data" : [
      { 
       "name":"test",
       "tag":"TEST"
      },
      {
       "name":"test2",
       "tag":"TEST_2"
      }
    ]
}

And then name the rules with 'data.*.name' but this obliges the API user to parse the array. Let suppose I have an array $array which I want to store, I need to do

$arrayParsed = ['data' => $array]

and call the API with $arrayParsed, which I think is a little redundant.

Is there a better way to handle the problem?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Irindul
  • 398
  • 1
  • 6
  • 21

3 Answers3

25

You can try to circumvent the issue by first doing:

$data = [ 'data' => $requests->all() ];

Then you can use the rule you suggested:

$validator = Validator::make($data, [
   'data.*.name' => 'required|string',
   'data.*.' => 'required|string'
]);

Not the most elegant solution but it solves your issue...

Niraj Shah
  • 15,087
  • 3
  • 41
  • 60
  • I like this solution but I'm new to APIs, is it better to let the user place the 'data': array or should I do it myself as you suggested? What is the correct way to send an array of objects? – Irindul Sep 15 '17 at 06:49
  • I've worked with APIs that require `data` to be sent in the request, and I've seen others that don't. It's really up to you how you implement the APIs. Developers will follow your documentation in any case if they want to use the APIs. But the easier to make it, the more likely they will use it. – Niraj Shah Sep 16 '17 at 09:51
  • Found this answer, looks like Lumen is looking for Request object not an array now in 5.6 – zeros-and-ones Aug 13 '18 at 21:16
7

I was able to do this in laravel 6 like the following via App\Http\Requests CustomRequestClass

public function rules()
{
    return [
        '*.name' => 'required',
        '*.tag' => 'required'
    ];
}
K-Alex
  • 380
  • 1
  • 3
  • 17
3

The approved answer works if your posting an array, however to take it a step further, I am needing to save multiple arrays. While that approach would work if I make two separate endpoints, what if I want to save everything inside one DB::transaction?

Viola:

POST:

{
"array1": [
    { "key1": "string", "key2": 1 },
    { "key1": "string", key2": 0 }
  ],
"array2": [
    { "key3": "string", "key4": 1 },
    { "key3": "string", "key4": 0 }
  ]
}

SERVER:

$this->validate($request, [
    'array1' => 'present|array',
    'array2' => 'present|array',
    'array1.*.key1' => 'required|string',
    'array1.*.key2' => 'required|integer',
    'array2.*.key3' => 'required|string',
    'array2.*.key4' => 'required|integer'
]);

DB::transaction(function() use($request) {
    foreach($request['array1'] as $x){
        ...do stuff here
    };
});

Note: 'present|array' accepts empty arrays whereas 'required|array' would reject them.