LaravelのEloquentで列の型がJSON型の列を取得するときにちょっとハマったので調査しました。
$post = Post::find(1);
echo $post->json_column->col1; // エラー
echo $post->json_column['col1'] // エラーにならない
TL;DR
結論からいうとJSON型の列を取得する場合は
モデルクラスの$casts
にjsonであることを明示しておき、
$model->json_column['property']
というように連想配列でアクセスすれば取得できます。
環境
名前 | バージョン |
---|---|
PHP | 7.1.6 |
Laravel | 5.5.14 |
配列でのアクセスになるのはなぜか
モデルは以下のとおり。
Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model as Eloquent;
class Post extends Eloquent
{
use \Illuminate\Database\Eloquent\SoftDeletes;
protected $casts = [
'json_column' => 'json', // ★これ
];
protected $fillable = [
'json_column',
];
}
$casts
をjsonにしているのでjson_decodeされた結果が返ってくると思ったので
プロパティアクセス($post->json_column->col1
)でいけると思ったのですがエラーになります。
配列でアクセス($post->json_column['col1']
)するとエラーになりません。
ひょっとしてjson_decode($string, true)
で連想配列にしてjsonをパースしてるのではと思ってソースを見てみました。
Illuminate\Database\Eloquent\Concerns\HasAttributes.php
/**
* Cast an attribute to a native PHP type.
*
* @param string $key
* @param mixed $value
* @return mixed
*/
protected function castAttribute($key, $value)
{
if (is_null($value)) {
return $value;
}
switch ($this->getCastType($key)) {
case 'int':
case 'integer':
return (int) $value;
case 'real':
case 'float':
case 'double':
return (float) $value;
case 'string':
return (string) $value;
case 'bool':
case 'boolean':
return (bool) $value;
case 'object':
return $this->fromJson($value, true);
case 'array':
case 'json':
return $this->fromJson($value); // ★ここ
case 'collection':
return new BaseCollection($this->fromJson($value));
case 'date':
return $this->asDate($value);
case 'datetime':
return $this->asDateTime($value);
case 'timestamp':
return $this->asTimestamp($value);
default:
return $value;
}
}
/**
* Decode the given JSON back into an array or object.
*
* @param string $value
* @param bool $asObject
* @return mixed
*/
public function fromJson($value, $asObject = false)
{
return json_decode($value, ! $asObject); // ★ここでjsonを連想配列にしている
}
json_decodeの第2引数をtrue、つまりassocがtrueなので連想配列形式で返却されます。
モデルクラスのプロパティで$casts
をjsonにしていた場合は連想配列でキャストされたものが返却されます。