14
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LaravelでPostgreSQLの配列型をcastしてみた

14
Posted at

Laravelでサポートされているcastのarrayを使えば配列型をarray取得できると思ってたが出来なかったので覚書

環境

Laravel 12.24.0
PostgreSQL 16.8

テーブル

CREATE TABLE hoge(
id SMALLSERIAL,
numlist DECIMAL(6,0)[],
create_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
delete_at TIMESTAMP,
PRIMARY KEY(id)
);

データ

INSERT INTO hoge (numlist, create_at,update_at)
VALUES
 (array[1, 2], CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
,(array[3, 4], CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);

中身はこのような感

select * from hoge;
id|numlist  |create_at              |update_at              |delete_at|
--+---------+-----------------------+-----------------------+---------+
 1|{1.0,2.0}|2026-09-16 19:11:49.011|2026-09-16 19:11:49.011|         |
 2|{3.0,4.0}|2026-09-16 19:11:49.011|2026-09-16 19:11:49.011|         |

modelのcastへarrayを指定

protected function casts(): array
{
    return [
        'numlist' => array,
    ];
}

これで配列取得できるかと思ったのですが返ってきたのは文字列 {1,2} でした。
公式の Array and JSON Casting を確認して気がつきました。
arrayは配列型ではなくJSONデータ型を想定されたものでした。
MySQLには配列型はなくJSONデータ型がありますし
PostgreSQLもJSONデータ型あるのでJSONデータ型使ってれば問題なかった。

だけどテーブルは配列型のままでやってみる

カスタムcastを作ればできるだろうと作ってみました。
まずは make:cast コマンドで作る

php artisan make:cast PostgresIntegerArray

App\Casts 配下に PostgresIntegerArray.php が生成された。
カスタムcastには getset を書く必要があるので以下のようにしてみた。

PostgresIntegerArray.php
<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

class PostgresIntegerArray implements CastsAttributes
{
    /**
     * Cast the given value.
     *
     * @param  array<string, mixed>  $attributes
     */
    public function get(Model $model, string $key, mixed $value, array $attributes): mixed
    {
        if (empty($value) || '{}' == $value) {
            return [];
        }

        return array_map('intval', str_getcsv(trim($value, '{}')));
    }

    /**
     * Prepare the given value for storage.
     *
     * @param  array<string, mixed>  $attributes
     */
    public function set(Model $model, string $key, mixed $value, array $attributes): mixed
    {
        if (!is_array($value) || empty($value)) {
            return '{}';
        }

        if (in_array(false, filter_var_array($value, FILTER_VALIDATE_INT), true)) {
            throw new \Exception('column is type numeric[] but expression.');
        }

        return '{' . implode(',', $value) . '}' ;
    }
}

※null考慮していませんのでこのままのコード利用時はご注意下さい。

modelへカスタムcastを適応

protected function casts(): array
{
    return [
        'numlist' => PostgresIntegerArray::class,
    ];
}

取得してみる

dd(Hoge::find(1)?->numlist);

無事に [1,2] が返って来ました。

まとめ

素直にカラムをJSONデータ型にすればわざわざカスタムCast作る必要が無かったのですが
配列型のままJOINしようと思っていたのでカラムを変更しませんでした。
使う前にはしっかり公式読みましょう。

14
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?