php - なぜ json_decode を 2 回使用する必要があるのか​​理解できません。 Laravel の移行とモデル

okwaves2024-01-25  8

そこで、次の移行を検討してください。

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateAdventureLogs extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('adventure_logs', function (Blueprint $table) {
            $table->id();
            $table->bigInteger('character_id')->unsigned();
            $table->foreign('character_id')
                  ->references('id')->on('characters');
            $table->bigInteger('adventure_id')->unsigned();
            $table->boolean('in_progress')->nullable()->default(false);
            $table->boolean('complete')->nullable()->default(false);
            $table->integer('last_completed_level')->nullable();
            $table->json('logs')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('adventure_logs');
    }
}

私が作成した json 列に注目してください。 $table->json('logs')->nullable();

ここから、モデルを作成しましょう:

use Illuminate\Database\Eloquent\Model;

class AdventureLog extends Model
{

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'character_id',
        'adventure_id',
        'complete',
        'in_progress',
        'last_completed_level',
        'logs',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'complete'             => 'boolean',
        'in_progress'          => 'boolean',
        'last_completed_level' => 'integer',
    ];

    public function getLogsAttribute($value) {
        return json_decode(json_decode($value));
    }

    public function setLogsAttribute($value) {
        $this->attributes['logs'] = json_encode($value);
    }

    public function character() {
        return $this->belongsTo(Character::class);
    }

    public function adventure() {
        return $this->hasOne(Adventure::class, 'id', 'adventure_id');
    }
}

このメソッドに注目してください:

public function getLogsAttribute($value) {
    return json_decode(json_decode($value));
}

これは間違っています。当然です。

保存される json は次のとおりです。

[{"adventure":"sample"}]

$character->adventureLogs()->first()->logs を呼び出すと、次の結果が得られます: [{"adventure":"sample"}]。

しかし、関数を想定どおりに変更すると次のようになります。

public function getLogsAttribute($value) {
    return json_decode($value);
}

次に、「[{"adventure":"sample"}]" を取得します。

次のようにしてデータを保存します。

AdventureLog::create([
   // .... Other attributes
   'logs' => ['adventure' => 'sample']
]);

では、最初の json_decode を別の json_decode にラップしなければならないという点で、私は何を間違っているのでしょうか?そんなことをする必要はないはずです。

その関数で単純な var_dump($value) を実行して、実際にそのフィールドに何が入っているかを確認できますか

– リッグスフォリー

2020 年 9 月 3 日 17:29

@RiggsFolly これはダンプで示される内容です: ""[{\"adventure\":\"sample\"}]""

– TheWebs

2020 年 9 月 3 日 17:34

では、getLogsAttribute($value) はどこで呼び出されるのでしょうか?そして、その関数に渡す前に $value をどこから取得しますか?

– リッグスフォリー

2020 年 9 月 3 日 17:37

@RiggsFolly モデルのドキュメント属性を読みます。

– TheWebs

2020 年 9 月 3 日 19:02



------------------------

ドキュメントより (https://laravel.com/docs/7.x/eloquent-mutators#array-and-json-casting):

...データベースにシリアル化された JSON を含む JSON または TEXT フィールド タイプがある場合、その属性にキャストされた配列を追加すると、Eloquent モデルでアクセスするときに属性が自動的に PHP 配列に逆シリアル化されます。

それでは追加するだけです

protected $casts = [
    ...
    'logs' => 'array',
];

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。