Не понимаю, почему я должен использовать json_decode дважды. Миграции Laravel и модель

#php #laravel

#php #laravel

Вопрос:

Итак, рассмотрим следующую миграцию:

 use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

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 IlluminateDatabaseEloquentModel;

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 в другое? Мне не нужно было этого делать.

Комментарии:

1. Можете ли вы сделать простое var_dump($value) в этой функции, чтобы мы могли видеть, что на самом деле в этом поле

2. @RiggsFolly это то, что показывает дамп: ""[{"adventure":"sample"}]""

3. Итак, где getLogsAttribute($value) вызывается? И откуда вы беретесь $value , прежде чем передать его этой функции

4. @RiggsFolly Прочитайте атрибуты документов для моделей.

Ответ №1:

Из документов (https://laravel.com/docs/7.x/eloquent-mutators#array-and-json-casting ):

… если ваша база данных имеет тип JSON или ТЕКСТОВОГО поля, который содержит сериализованный JSON, добавление приведения массива к этому атрибуту автоматически десериализует атрибут в массив PHP при доступе к нему в вашей модели Eloquent

Так что просто добавьте

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