Best way to store and load JSON from database in Laravel
Database
In your database migrations add:
$table->json('data'); // Recommended. Supported in MySQL since version 5.7.8
or
$table->text('data');
The JSON column type is recommended as it allows you to do SQL queries on JSON data. See MySQL JSON Data Type
Model: Casting the Attribute
The next issue is that you need to be able to cast your data into a PHP array.
This is done by modifying the the casts attribute in the model:
class Paste extends Model {
protected $casts = [
'data' => 'array'
];
}
See Array and JSON Casting for more information.
Now you can save data onto the attribute as a PHP array, and also assign it a PHP array.
$paste = Paste::first();
dump($paste); // Returns a PHP array
$paste->data = ['some-data' => 20, 'score' => 500];
$paste->save();
Internally, when it saves the data, it automatically would convert it into a JSON string and save it in the database in the correct format.
Store Method
When taking in input as JSON, it highly depends in how you want to pass the data,
1. Sending form data with JSON content type (recommended)
My recommendation is to send the entire data as JSON in the POST body like so:
Content-Type: application/json
Body:
{
"data": {
"name": "John",
"age": 31,
"city": "New York"
},
"someOtherField": "Hello!"
}
Your store()
method should now be (I've also added validation code):
public function store()
{
$this->validate($request, [
'data' => ['required', 'array'],
'data.*.name' => ['required', 'string'],
'data.*.age' => ['required', 'int'],
'data.*.city' => ['required', 'string'],
]);
$paste = new Paste();
$paste->uuid = Str::uuid()->toString();
$paste->data = $request->post('data'); // No need to decode as it's already an array
$paste->save();
return Redirect::to("/paste/{$paste->uuid}")
->with('success', 'Created');
}
2. Sending form data with form params
If however you insist in sending data through query params or form params, note these can only send strings. Therefore you need to send an encoded version of the JSON string to persists data types, as follows:
Form Params:
- data: '{"name": "John", "age": 31, "city": "New York"}'
- someOtherField: "Hello!"
The store method will now look like this:
$this->validate($request, [
'data' => ['required', 'json'], // I'm unsure if data is required
]);
$data = json_decode($request->post('data'), true, JSON_THROW_ON_ERROR); // Needs to be decoded
// validate $data is correct
Validator::make($data, [
'name' => ['required', 'string'],
'age' => ['required', 'int'],
'city' => ['required', 'string'],
])->validate();
$paste = new Paste();
$paste->uuid = Str::uuid()->toString();
$paste->data = $data;
$paste->save();
return Redirect::to("/paste/{$paste->uuid}")
->with('success', 'Created');
Show Method
Your show method needs no changes:
public function show($uuid)
{
$paste = Paste::where('uuid', $uuid)->first();
return response()->json($paste->data);
}
1- Your column need to be of type json type
$table->json('data');
2- in your Model you need to cast your column to an array
protected $casts = ['data' => 'array'];
3- sending data value to your controller must be an array so you can use array Laravel validation on it:
[
'data' => 'required|array',
'data.*.name' => 'required'
....
]
4- when you store your data it will be parsed automatically and the same when you retrieve your data column it will be converted to an array