Laravel JSON validation failed with postman - php

I am sending a post request using postman to my Laravel API.
{
"title" : "abc",
"body" : [
{"type":"paragraph","data":{"text":"aSADAd"}},
{"type":"header","data":{"text":"Heading......","level":2}},
{"type":"paragraph","data":{"text":"This is a para"}},
{"type":"list","data":{"style":"ordered","items":["ABC","XYZ"]}}
],
"status" : 1
}
The body field is a JSON data field - the JSON.stringify output from the editor js.
My laravel validation rules are -
$validator = Validator::make($request->all(), [
'title' => 'required|string|max:255',
'body' => 'required|json',
'status' => 'required|boolean'
]);
But I m getting this error -
{
"validation_error": {
"body": [
"The body must be a valid JSON string."
]
}
}

try array
$validator = Validator::make($request->all(), [
'title' => 'required|string|max:255',
'body' => 'required|array',
'status' => 'required|boolean'
]);
this will work if you set your model
/**
* The attributes that has json type.
*
* #var array
*/
protected $casts = [
'body' => 'array'
];
like this you can work with json mysql in laravel
NOTE : The array cast type is particularly useful when working with columns that are stored as serialized JSON. For example, if your database has a JSON or TEXT field type that contains serialized JSON, adding the array cast to that attribute will automatically deserialize the attribute to a PHP array when you access it on your Eloquent model:
Ref links
https://laravel.com/docs/8.x/eloquent-mutators#array-and-json-casting
https://laraveldaily.com/working-with-mysql-json-columns-in-laravel-custom-properties-example/
https://laracasts.com/discuss/channels/laravel/saving-an-array-into-a-json-column-in-a-mysql-database

Related

Creating a table in BigQuery using the PHP api with schema

I'm trying to create a table in bigquery using the php api.
I can create a table without a schema just fine, but when I provide a schema I get errors. It looks like I'm using the wrong syntax, however I tried just about any formatting I could think of and couldn't find a single example of what I'm trying to achieve.
I use a string literal for the fields parameter for testing. My code looks like this:
$bigQuery = new BigQueryClient([
'keyFilePath' => [keyfilepath],
'projectId' => [projectid],
'location' => [location]
]);
/** #var Dataset $dataSet */
$dataSet = $bigQuery->dataset('my-data-set');
$fieldString = '{"name": "myfield","type": "STRING","mode": "REQUIRED"}' . "\n" . '{"name": "anotherfield", "type": "STRING", "mode": "REQUIRED"}';
$options = [
'fields' => $fieldString
];
$dataSet->createTable('mytable', $options);
Which gives the error:
"Invalid field selection {\"name\":\"myfield\""
Or alternatively, when I format the "$fieldString" like this:
$fieldString = '[{"name": "myfield","type": "STRING","mode": "REQUIRED"}, {"name": "anotherfield", "type": "STRING", "mode": "REQUIRED"}]';
I get the error:
Invalid FieldMask '[{\"name\":\"myfield\",\"type\":\"STRING\",\"mode\":\"REQUIRED\"},{\"name\":\"anotherfield\",\"type\":\"STRING\",\"mode\":\"REQUIRED\"}]'. Map keys should be represented as [\"some_key\"].
I've also tried to create the table first and then update it like so:
$table = $dataSet->createTable('mytable');
$table->update($options);
But I get the same errors. Even when I use the the json representation exactly like shown here the problem persists.
What am I doing wrong here?
UPDATE:
I actually first tried this, before I switched to a string literal for the fields:
$fields = [
['name'=> 'myfield', 'type' => 'INTEGER', 'mode' => 'REQUIRED'],
['name'=> 'anotherfield', 'type' => 'INTEGER', 'mode' => 'REQUIRED']
];
$options = [
'schema' => $fields
];
$dataSet->createTable('mytable', $options);
This yields the error:
"Invalid JSON payload received. Unknown name \"schema\" at 'table': Proto field is not repeating, cannot start list."
Then I edited the code to look like this:
$fields = [
['name'=> 'myfield', 'type' => 'INTEGER', 'mode' => 'REQUIRED'],
['name'=> 'anotherfield', 'type' => 'INTEGER', 'mode' => 'REQUIRED']
];
$options = [
'fields' => $fields
];
$dataSet->createTable('mytable', $options);
Which gives:
Warning: rawurlencode() expects parameter 1 to be string, array given
I didn't mention this in my question before because I thought it wasn't relevant. In hindsight it probably is, but my problem still persists.
There is no such thing as $fieldString that is $fields array
like this:
$fields = [
[
'name' => 'field1',
'type' => 'string',
'mode' => 'required'
],
[
'name' => 'field2',
'type' => 'integer'
],
];
then
$schema = ['fields' => $fields];
$table = $dataset->createTable($tableId, ['schema' => $schema]);
See example here.

Validating input parameters against route bound model

I have a small question concerning validation.
there is an api route POST /api/document/{document}/link it accepts an array of document IDs ({"ids": [1, 2, 3]}) to be linked to the Document bound to the route. I validate this array as follows
public function rules()
{
return [
'ids' => 'required|array',
'ids.*' => 'numeric|exists:documents,id'
];
}
The thing is the Document model has a partner attribute and it's not possible to link together documents from different partners. What I want is to check if the documents passed (by their IDs) belong to the same partner as the bound Document. I would like to validate this within the FormRequest. Is it possible?
You can use these for your rules:
'ids' => [
'required',
'array'
],
'ids.*' => [
'required',
'exists:documents,id'
],
'ids.*.partner_id' => [
Rule::in([$document->partner_id])
]
this wil validate your id matches with the numbers in the array, since we only put the id from the route given $document in there it should match or return failed.
So, here is what I ended up with:
public function rules()
{
/** #var Document $document */
$document = $this->route('document');
return [
'ids' => ['required', 'array'],
'ids.*' => ['required', 'numeric', Rule::exists('documents','id')->where('partner_id', $document->partner_id)],
];
}
As it turned out the case is described in Laravel docs here https://laravel.com/docs/5.8/validation#rule-exists. I just needed to customize the query executed to ensure that both the passed id and partner_id exist.

PHP / Laravel - Foreach store into database (\Grammar::parameterize() error)

I have an array, which looks like this:
array:3 [▼
"field" => array:2 [▼
0 => "fromName"
1 => "from"
]
"operator" => array:2 [▼
0 => "="
1 => "="
]
"value" => array:2 [▼
0 => "Oliver"
1 => "oliver#mywebsite.com"
]
]
I am trying to save the above array, into my database table called email_rules:
Below is my code.
StreamEmailRulesController.php:
public function store(Stream $stream)
{
//Validate the request.
//Validate the request.
$attributes = request()->validate([
'field' => 'required|array|min:1',
'field.*' => [
'required', 'string',
Rule::in(['fromName', 'from']),
],
'operator' => 'required|array|min:1',
'operator.*' => [
'required', 'string',
Rule::in(['=', '!=', 'matches']),
],
'value' => 'required|array|min:1',
'value.*' => 'required|string|min:3|max:255',
]);
//Add the document to the database.
$stream->addRules($attributes);
//Return back.
return redirect()->back();
}
Now the $stream->addRules() function is responsible for saving the data to the database.
Stream.php:
/**
* A stream can have many rules.
*/
public function rules()
{
return $this->hasMany(EmailRule::class);
}
/**
* Add Email Rules(s) to the stream
*
* #return Illuminate\Database\Eloquent\Model
*/
public function addRules(array $attributes)
{
return $this->rules()->create($attributes);
}
Now, above does not work. I get below error:
Argument 1 passed to Illuminate\Database\Grammar::parameterize() must be of the type array, int given,
What am I doing wrong?
If you dump $attributes you may be getting an int (bool) as a pass or fail or even json, depending on what's going in, from the validation. This might just be a matter of changing syntax from
$attributes = request()->validate([...
to
$attributes= $this->validate(request(), [...
I believe your issue is that you're trying to save an array as a singular value. IE those attributes need to be iterated over to create a new set of rules for each one, instead. Normally, I'd expect to see the array ready to create individual objects. In this case, though it looks like it is structured to create individual fields (field, operator, value), so looping through those may not do what you wish either -- it provides multiple fields to the create construct, rather than a full set of object params for a new rule(). I think Laravel is hinting that you may wish to change your request/return structure to match the model format.
I think it could be the array structure. Can you modify the array to?:
[
[
"field" => "fromName",
"operator" => "=",
"value" => "Oliver"
],
[
"field" => "from",
"operator" => "=",
"value" => "oliver#mywebsite.com"
],
]
EDIT:
In the Controller add a loop like this:
...
foreach ($attributes as $key => $value) {
foreach ($value as $k => $v) {
$data [$k][$key] = $v;
}
}
//Add the document to the database.
$stream->addRules($data);
The problem was that Laravels create or createMany expect an array with key => pair values, where the key corresponds to the database columns.
This article from Adam Wathan helped me out a lot.
This is what I ended up doing:
$requestData = collect(request()->only('field', 'operator', 'value'));
$rules = $requestData->transpose()->map(function ($ruleData) {
return new EmailRule([
'field' => $ruleData[0],
'operator' => $ruleData[1],
'value' => $ruleData[2],
]);
})->toArray();
//Add the rules to the database.
$stream->addRules($rules);

Laravel array object rule

I send an array to a REST API. How can I add a rule for the array?
Also I want to add field_name_id, field_input_type and field_caption as required fields.
I don't know how can I access the array in Laravel rules. Can someone help me?
$rules = [
'name' => 'required',
'forms' => 'array'
]
Laravel uses dot notation to validate arrays and it's nested fields.
$rules = [
'forms.field_name_id' => 'required',
'forms.field_input_type'=> 'required',
'forms.field_caption' => 'required',
]
You can also validate each value within the array. For example, If you want the caption to be unique:
$rules = [
'forms.*.field_caption' => 'unique:captions,caption',
]
Here are the docs for more information on how to use them

Converting Laravel validation message dot syntax into array

I'am using Laravel on server side. Let's imagine our controller receive two fields url [string] and data [array with index head]. We can validate data and customize errors messages with
$this->validate($request, [
'url' => 'required',
'data.head' => 'required',
], [
'url.required' => 'The :attribute field is required',
'data.head.required' => 'The :attribute field is required',
]);
If validation fails, Laravel send back response with json data
{
"url": ["The url field is required"],
"data.head": ["The data.head field is required"]
}
How we can convert response data to send json, as below?
{
"url": ["The url field is required"],
"data": {
"head": ["The data.head field is required"]
}
}
In javascript
Loop on errors
error: function (errors) {
$.each(errors['responseJSON']['errors'], function (index, error) {
var object = {};
element = dotToArray(index);
object[index] = error[0];
validator.showErrors(object);
});
}
convert in dot notation into array notation. i.e abc.1.xyz into abc[1][xyz]
function dotToArray(str) {
var output = '';
var chucks = str.split('.');
if(chucks.length > 1){
for(i = 0; i < chucks.length; i++){
if(i == 0){
output = chucks[i];
}else{
output += '['+chucks[i]+']';
}
}
}else{
output = chucks[0];
}
return output
}
Laravel has an helper called array_set that transform a dot based notation to array.
I don't know how you send the errors via ajax, but you should be able to do something like that:
$errors = [];
foreach ($validator->errors()->all() as $key => $value) {
array_set($errors, $key, $value);
}
Edit:
But apparently, you should be able to not use the dot notation by Specifying Custom Messages In Language Files like this example:
'custom' => [
'email' => [
'required' => 'We need to know your e-mail address!',
],
],
I don't know if it's still a valid question but to create a custom validation using dot notation in laravel, you can specify the array like this in your validation.php
'custom' => [
'parent' => [
'children' => [
'required' => 'custom message here'
]
]
This will be the parent.children property.
see ya.
key = key.replace(/\./g, '[') + Array(key.split('.').length).join(']');

Categories