株式会社WR

株式会社WR

WEB TOTAL CONSULTING

PostgreSQLのJSON型カラムをLaravelで活用する
ブログ一覧へ
技術ブログ

PostgreSQLのJSON型カラムをLaravelで活用する

PostgreSQLのJSON/JSONB型カラムを使うと、スキーマを変えずに柔軟なデータを保存できます。Laravelでのマイグレーション・保存・クエリの方法を解説します。

JSONBとは——PostgreSQL最強のデータ型

PostgreSQLには JSONJSONB という2種類のJSON型があります。

比較項目 JSON JSONB
保存形式 テキスト(入力そのまま) バイナリ(パース済み)
挿入速度 速い やや遅い
検索速度 遅い 速い
インデックス 不可 GINインデックス対応
キー順序 保持 保持しない
重複キー 保持 最後のみ残る

実務ではJSONB一択です。検索・インデックスが使えるため、JSONデータに対してSQLで柔軟にクエリが書けます。


Laravelでのマイグレーション

Schema::create('products', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->decimal('price', 10, 2);
    $table->jsonb('attributes')->default('{}'); // JSONB型カラム
    $table->jsonb('images')->default('[]');
    $table->timestamps();
});

モデルの設定

$casts'array' を指定するだけで、PHPの配列として透過的に扱えます。

class Product extends Model
{
    protected $casts = [
        'attributes' => 'array',
        'images'     => 'array',
    ];
}

データの保存と取得

// 保存
Product::create([
    'name'  => 'ワイヤレスイヤホン',
    'price' => 12800,
    'attributes' => [
        'color'    => 'ブラック',
        'weight'   => '45g',
        'battery'  => '8時間',
        'bluetooth' => '5.3',
    ],
]);

// 取得(PHPの配列として使える)
$product = Product::find(1);
echo $product->attributes['color']; // "ブラック"
echo $product->attributes['battery']; // "8時間"

// 部分更新(マージ)
$product->attributes = array_merge(
    $product->attributes,
    ['bluetooth' => '5.4']
);
$product->save();

JSONBのクエリ——Laravelから検索する

特定キーの値で絞り込み

// 'attributes->color' が 'ブラック' の商品を検索
$products = Product::where('attributes->color', 'ブラック')->get();

// JSONBの値で数値比較(->演算子はテキストを返すのでキャスト必要)
$products = Product::whereRaw(
    "(attributes->>'weight')::numeric < ?", [50]
)->get();

GINインデックスで高速化

// マイグレーションでGINインデックスを追加
DB::statement("CREATE INDEX products_attributes_gin ON products USING GIN (attributes)");

// @> 演算子(含む)でインデックスが使われる
$products = Product::whereRaw(
    "attributes @> ?::jsonb",
    [json_encode(['color' => 'ブラック'])]
)->get();

実践:商品スペック管理システム

ECサイトでは商品カテゴリによってスペック(属性)が異なります。家電・衣料・食品では全く異なる属性があり、JSONB型はこのような可変スキーマに最適です。

// 家電の場合
$electronics = Product::create([
    'category' => '家電',
    'attributes' => [
        'voltage'     => '100V',
        'power'       => '1200W',
        'dimensions'  => ['width' => 30, 'height' => 20, 'depth' => 25],
        'warranty'    => '1年',
    ],
]);

// 衣料の場合(同じテーブル・同じカラム)
$clothing = Product::create([
    'category' => '衣料',
    'attributes' => [
        'size'        => ['S', 'M', 'L', 'XL'],
        'material'    => '綿95% ポリエステル5%',
        'washable'    => true,
        'color_codes' => ['#000000', '#FFFFFF', '#1a73e8'],
    ],
]);

注意点——アンチパターン

JSONB内を頻繁にソート・集計するのはNG

JSONB内の値でソートや集計が頻繁に必要なら、それはテーブルのカラムとして切り出すべきです。JSONBは「柔軟な属性」に向いており、「集計の軸になるデータ」には向きません。

// NG:価格をJSONB内に入れてソートしようとする
// OK:価格は専用カラムで管理
$table->decimal('price', 10, 2); // 正規カラムとして持つ

まとめ

PostgreSQLのJSONB型とLaravelの組み合わせは、リレーショナルDBの堅牢性とドキュメントDBの柔軟性を両立できる強力な手段です。弊社では商品属性・設定情報・外部APIのレスポンスキャッシュなど、様々な場面でJSONBを活用しています。

PostgreSQL・LaravelでのWebシステム開発についてはお気軽にご相談ください。

Category 技術ブログ

Related Posts

関連記事

開発・技術のご相談はお気軽に

お見積りは無料です。まずはお気軽にご相談ください。

お問い合わせ →