13

I'm trying to store json into a db and load it back

I tried to store

{name: "John", age: 31, city: "New York"}

It stored correctly. I checked the db, it showed correctly.

{name: "John", age: 31, city: "New York"}

I kept getting on the view

"{name: \"John\", age: 31, city: \"New York\"}"

This is my code.

public function store()
{

    $paste             = new Paste;
    $paste->uuid       = Str::uuid()->toString();
    $paste->data       = trim(Request::get('data',''));
    $paste->save();

    return Redirect::to('/paste/'.$paste->uuid)->with('success', 'Created');

}

public function show($uuid)
{
    $paste  = Paste::where('uuid',$uuid)->first();
    return response()->json($paste->data);
}

Any hints for me ?

Reproducible here

https://www.bunlongheng.com/paste


Try # 2

If I did this

public function show($uuid)
{


    $paste  = Paste::where('uuid',$uuid)->first();
    return View::make('layouts.fe.pastes.show', get_defined_vars());

}

and in my view, I only have this 1 line

{!!$paste->data!!}

I get the same data as what I submitted now.

{name: "John", age: 31, city: "New York"}

BUT the browser detected it as text, not a response JSON which defeated the purpose of what I am trying to do.


Try # 3

public function show($uuid)
{
    $paste  = Paste::where('uuid',$uuid)->first();
    return response()->json(stripslashes($paste->data));
    
}

result

"{name: \"John\", age: 31, city: \"New York\"}"

Try # 4

public function show($uuid)
{
    $paste  = Paste::where('uuid',$uuid)->first();
    return View::make('layouts.fe.pastes.show', get_defined_vars());
}

view

{{ json_encode($paste->data, JSON_UNESCAPED_SLASHES) }}

result

"{name: \"John\", age: 31, city: \"New York\"}"

Try #5

I think the issue is lying on the storing ... not the loading and rendering.

I tried

return response()->json($paste);

My JSON parser detected it ...

enter image description here

{
"id": 11,
"status": 0,
"uuid": "0c40f97d-7d98-42c6-864e-71d3ed81eed3",
"name": "n6ou",
"password": "",
"expiration": "",
"type": "json",
"data": "{name: \"John\", age: 31, city: \"New York\"}",
"created_at": "2021-04-22T22:53:11.000000Z",
"updated_at": "2021-04-22T22:53:11.000000Z"
}

This is what I used to store

$paste->data       = trim(Request::get('data',''));
$paste->save();

Try #6

For those of you that doubt my data/content

I've tried pasting the same line in Pastebin

enter image description here

It's cleaned, you can see below.

https://pastebin.com/raw/r9akUK1v

code-8
  • 54,650
  • 106
  • 352
  • 604
  • 1
    Try using `return response()->json(stripslashes($paste->data));` – Ivan86 Apr 22 '21 at 22:13
  • 1
    Or, in your view try this: `{ json_encode($paste->data, JSON_UNESCAPED_SLASHES) }` – Ivan86 Apr 22 '21 at 22:17
  • Try both , slash still there. – code-8 Apr 22 '21 at 22:28
  • try `{ json_decode($paste->data) }` , this should work. – Ivan86 Apr 22 '21 at 22:34
  • what is the controller code look like ? – code-8 Apr 22 '21 at 22:34
  • Well you already have `response()->json($paste->data);` using `->json()` which should properly encode the JSON which is why you are ending up with `\"`. `json_decode()` in the view should solve that – Ivan86 Apr 22 '21 at 22:36
  • https://www.bunlongheng.com/paste -- please try. I uploaded your code. – code-8 Apr 22 '21 at 22:39
  • `return response()->json($paste->data);` && `{{ json_decode($paste->data) }}` – code-8 Apr 22 '21 at 22:39
  • I meant in your view do this: `{{ json_decode($paste->data) }}`, this is fine: `return response()->json($paste->data);` – Ivan86 Apr 22 '21 at 22:41
  • That’s exactly what I did. – code-8 Apr 22 '21 at 22:42
  • Are you using blade as the templating engine? – Ivan86 Apr 22 '21 at 22:43
  • Yes. I am. Using blade engine – code-8 Apr 22 '21 at 22:46
  • Hm. Well, I guess you can also try this, just for a test: `$data = json_decode($paste->data)`, then `return view::make('layouts.fe.pastes.show',$data)` or `return response()->json($data);` but I think this latter will not work since it would be decoding and then encoding again, but it's worth a try – Ivan86 Apr 22 '21 at 22:50
  • I think the issue is lying on the storing ... not the loading and rendering. – code-8 Apr 22 '21 at 22:56
  • Look at my try # 5 – code-8 Apr 22 '21 at 22:58
  • Then maybe try this: `$paste->data = trim(json_encode(Request::get('data',''), JSON_UNESCAPED_SLASHES));` before the `$paste->save();` – Ivan86 Apr 22 '21 at 23:01
  • Ok doing that now... – code-8 Apr 22 '21 at 23:03
  • doing that getting `"\"{name:\\\"John\\\", age:31,city:\\\"New York\\\"}\""` – code-8 Apr 22 '21 at 23:04
  • `$paste->data = trim(json_encode(Request::get('data',''), JSON_UNESCAPED_SLASHES));` & `return response()->json($paste->data);` – code-8 Apr 22 '21 at 23:05
  • try doing an echo of `$paste->data` after getting the data and before showing in the view. Also add a `die()` after that, maybe that will help isolate the problem. the `->json()` method seems to be escaping the `"` characters in your JSON string, so I'm not sure it's the storing that's the problem, but I'm not sure. It's hard for me to debug just by thinking about it :) – Ivan86 Apr 22 '21 at 23:09
  • Try that yourself on your end, if you have time, I want to get this working. I'm very curioys. – code-8 Apr 22 '21 at 23:10
  • I even tried, `return response()->json(str_replace('\\','',$paste->data));` and it's the slash is still there, it's like haunted me haha. – code-8 Apr 22 '21 at 23:11
  • Yeah, I can imagine. It's probably something simple. I don't have a Laravel set up on my side. Don't have the time to go through that now to make an example. Maybe later. What you should confirm is what does the string look like in the DB by somehow echoing that value. In my opinion it should be easiest to `echo $paste->data; die();` That should show you if the string is escaped in the DB or the `->json()` method does that which is what I think is happening. But I could be wrong. – Ivan86 Apr 22 '21 at 23:14
  • did you try storing like `{name: 'John', age: 31, city: 'New York'}` with single quotation? – Farhan Ibn Wahid Apr 23 '21 at 01:17
  • @Psycho Why did u ask that ? I would like to store whatever users provided in the input text area, and return whatever they entered. – code-8 Apr 23 '21 at 02:21
  • @Psycho Do you think I should use some kind of normalization. – code-8 Apr 23 '21 at 02:22
  • @Psycho I’m not sure if you know. I used to use a site name myjson.com , somehow they took it down. I want to mimic that. Able to share a JSON with someone very quickly – code-8 Apr 23 '21 at 02:23
  • I know this has already been answered, but I want to share the [**documentation**](https://laravel.com/docs/8.x/migrations#column-method-json) where it states and explains a little more about this so everyone knows where to go first and don't lose time – matiaslauriti Sep 15 '21 at 02:40
  • This can help if you have a multilingual slug. https://stackoverflow.com/a/71766061/13779574 – Akbarali Apr 06 '22 at 11:41

5 Answers5

18

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 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);
}
common sense
  • 3,775
  • 6
  • 22
  • 31
Yahya Uddin
  • 26,997
  • 35
  • 140
  • 231
4

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

PsyLogic
  • 639
  • 4
  • 10
  • Let me try that suggestion – code-8 Apr 25 '21 at 22:23
  • I think this method is the best way to work with JSON. One addition (assuming OP is using a MySQL DB): Older MySQL servers don"t support the JSON type. So MySQL <= 5.7 should use LONGTEXT instead. – Gordon Freeman Apr 26 '21 at 08:57
  • @GordonFreeman Sure for old versions, longText is the solution, but I'm not sure about elequont in this case – PsyLogic Apr 26 '21 at 13:29
  • @PsyLogic Eloquent handles this just fine. Had no issues with older projects. `protected $casts` does the magic regardless of the actual DB field type. The only drawback I can think of, it would not be possible to benefit from the JSON type field on DB level (search in the JSON, etc.). – Gordon Freeman Apr 26 '21 at 13:33
  • Thanks for the confirmation, but sure it will not work for search since JSON methods added to 5.7+, so you need to use a custom function in ur DB or returning the column value and do the search in ur backend (which is not recommended) – PsyLogic Apr 26 '21 at 13:41
2
  1. Using ->json() as the migration method to store JSON data (https://laravel.com/docs/8.x/migrations#column-method-json)
  2. Refer to "Array & JSON Casting" (https://laravel.com/docs/8.x/eloquent-mutators#array-and-json-casting) for how do you prepare the data

I know the answer is not in paragraphs as others, but I like to make it simple and straight. Is it the best solution? No one can tell you that nor prove that? Is this method going to work, no one can tell you that nor prove that, but at least it boosts up your success rate. Let me know if there is anything else I could help with! Good Luck

Jacky.S
  • 364
  • 5
  • 17
0

If you want to store data as json on DB and restore it just do the following (I always use this way):

1- Add your data to array:

$data["name"] = "John";
$data["age"] = 31;
$data["city"] = "New York";

2- Encode the array and add it to the database (you can store it as text)

$encodedData = json_encode($data);

3- If you want to add nested json data just make your array nested array.

4- When you restore the data just use json_decode to decode it

0

Just add this to your Model.

protected $casts = [
    'data' => 'object'
];

Then you can get in your view like this:

{{ $data->data->name }}
Abdur Rahim
  • 41
  • 2
  • 10