laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.66k stars 11.04k forks source link

Json field updating inconsistency #53493

Closed emresudo closed 1 week ago

emresudo commented 1 week ago

Laravel Version

11.31.0

PHP Version

8.3.10

Database Driver & Version

MySQL 8.4.3 for Docker

Description

There is JSON field update issue on project. I already added comment about problem in the controller.

If we are using json updating for data->language (with arrow), code generate built in json_set method. It is clever.

And I know other method is override old data column because we are actually providing an recursive array.

Of course we can do it something in this time, for example;

  1. We can set default value of json column is '{}', and we set it can not be null on migration. I think it is works because we will know we do not have null value in that column. (It works if the only problem is null value) Maybe json type column might haven't nullable property. $table->json('data')->nullable(); -> Old $table->json('data')->default(new Expression('(JSON_ARRAY())')); -> It doesn't work $table->json('data')->default(new Expression('(JSON_OBJECT())')); -> It works
  2. We can write an helper function for this problem, it can check always current data.

But JSON field updating must be more consistent.

I will be add more details and solution when I have time.

Steps To Reproduce

Controller

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class SharedController extends Controller
{
    public function language(Request $request, string $language)
    {
        if (!in_array($language, ['tr', 'en'])) {
            $language = 'en';
        }

        app()->setLocale($language);

        session()->put('language', $language);

        if ($request->user()) {

            // Scenario 1 (Does not works)

            User::query()->where('id', $request->user()->id)->update(
                [
                    'data->language' => $language
                ]
            );
            // It does not update when the `data` column is null
            //
            // `data`: null             !!-> it wont be updated
            // `data`: []                 !!-> it wont be updated
            // `data`: {}                 -> it will be updated
            // `data`: {"language": "en"} -> it will be updated
            // `data`: {"other": "en"}    -> it will be updated
            // Model->update method generate this sql code
            // "update `users` set `data` = json_set(`data`, '$."language"', ?), `users`.`updated_at` = ? where `id` = ?" // vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:3866
            // array:3 [▼ // vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:3866
            //   0 => "tr"
            //   1 => "2024-11-13 11:17:34"
            //   2 => 1
            // ]

            // Scenario 2 (Works)

            User::query()->where('id', $request->user()->id)->update(
                [
                    'data' => [
                        'language' => $language
                    ]
                ]
            );
            // It works regardless of the `data` column's value
            // Model->update method generate this sql code
            // "update `users` set `data` = ?, `users`.`updated_at` = ? where `id` = ?" // vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:3866
            // array:3 [▼ // vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:3866
            //   0 => "{"language":"tr"}"
            //   1 => "2024-11-13 11:16:31"
            //   2 => 1
            // ]
        }

        // return redirect()->back();
    }
}

App\Models\User

class User extends Authenticatable
{
    protected $fillable = [
        'data',
    ];

    protected function casts(): array
    {
        return [
            'data' => 'object',
        ];
    }
}
crynobone commented 1 week ago

Hey there,

While this may be a legitimate issue, can you first try posting your problem or question on one of the support channels below? If this issue can be definitively identified as a bug, feel free to open up a new issue with a link to the original one and we'll gladly help you out.

Thanks!