【ITニュース解説】Laravel Eloquent Serialization: Hidden, Appends, Dates
2026年10月24日に「Dev.to」が公開したITニュース「Laravel Eloquent Serialization: Hidden, Appends, Dates」について初心者にもわかりやすく解説しています。
ITニュース概要
Laravel Eloquentのシリアル化は、モデルをJSONに変換しAPIレスポンスを作るのに便利だ。`Hidden`でパスワードなど機密情報を隠し、`Appends`で算出値を追加できる。ただし、`Appends`はN+1問題に注意が必要だ。`makeVisible`は一時的な表示変更だが、モデルの再利用は避けるべき。日付フォーマットも制御でき、より複雑なAPIには専用リソースを使うと良い。
ITニュース解説
Laravelでは、データベースから取得したデータをWebサイトやアプリで利用する際、それをJSONという形式に自動的に変換してくれる便利な機能がある。これはEloquentと呼ばれる機能の一部で、例えばUser::all()のようにデータベースからユーザー情報をすべて取得し、そのまま返すと、Laravelが自動的にそのデータをJSONに変換して出力してくれるのだ。しかし、この手軽さの裏には、セキュリティ上の問題(機密情報が誤って公開される)や、パフォーマンスの問題(処理速度の低下)が潜んでいることがある。本解説では、これらの問題を避けつつ、データを適切にJSON形式で扱う方法について説明する。
まず、モデルをJSONに変換する基本的な方法について理解しておこう。データベースから取得したユーザー情報が格納された$userというモデルオブジェクトがあるとする。この$userを$user->toArray()とすると、ユーザーの属性情報と、そのユーザーに関連するデータ(例えば、ユーザーが書いたブログ記事など)が配列として取得できる。$user->attributesToArray()は、関連データを除外し、ユーザー自身の属性のみを配列で返す。$user->toJson()は、その情報をJSON形式の文字列として取得する。また、モデルオブジェクトを直接文字列として扱おうとすると、自動的にtoJson()が呼ばれてJSON文字列になる。ここで注意すべき点として、例えばblogPostsという関連データがあった場合、JSON出力時にはblog_postsのようにスネークケースという形式に自動的に変換される点がある。
次に、データベースから取得した情報の中に、パスワードのハッシュ値やトークンといった外部に見せてはいけない機密情報が含まれる場合がある。このような情報をJSON出力から隠すためには、#[Hidden(['password', 'remember_token'])]という属性をモデルに追加する。これにより、toArray()やtoJson()で出力されるJSONデータから、指定した属性が自動的に除外される。このHidden属性は、JSON出力時のみに影響し、モデルオブジェクト自体には情報が保持されているため、例えば$user->passwordとアクセスすれば、パスワードハッシュ値自体は取得できる点に注意が必要だ。また、関連データも名前を指定すれば非表示にできる。
もし、非表示にする属性をリストアップするのではなく、許可する属性だけをリストアップしたい場合は、#[Visible(['name', 'email'])]という属性を使用する。これは「ホワイトリスト」方式と呼ばれ、明示的に許可された属性以外はすべて出力されないため、将来的に新しいカラムが追加されても意図しない情報漏洩を防げるため、より安全な方法とされている。
モデルの表示・非表示の設定は、プログラムの実行中に一時的に変更することも可能だ。例えば、$user->makeVisible('password')->toArray()とすれば、一時的にパスワード情報も出力されるようになる。逆に$user->makeHidden('email')->toArray()でメールアドレスを隠すこともできる。また、$user->mergeHidden()で既存の非表示リストに追加したり、$user->setHidden()で非表示リストを完全に置き換えたりもできる。しかし、これらのメソッドはモデルオブジェクトそのものを変更してしまうため、「このモデルオブジェクトは、パスワードが表示可能な状態になっている」というように、そのオブジェクトの状態が変わってしまう。そのため、一度情報を公開したモデルオブジェクトをキャッシュして使い回したり、他の処理に渡したりすると、意図せず機密情報が公開されてしまう可能性があるので、使用後にはモデルを再取得するなどの注意が必要だ。
データベースのカラムには存在しないが、モデルの情報を元に計算して得られる値(例えば、ユーザーが管理者かどうかを示すis_adminなど)をJSONに含めたい場合もあるだろう。このような「算出値」を追加するには、まずその値を計算するための「アクセサ」と呼ばれるメソッドを作成し、次に#[Appends(['is_admin'])]という属性をモデルに追加する。これにより、JSON出力にis_admin: "yes"のような形で算出値を含めることができる。特定のレスポンスのためだけに一時的に追加したい場合は$user->append('is_admin')を使う。
ただし、この算出値(Appends)の利用には大きな注意点がある。アクセサの中でデータベースへの問い合わせを行うような処理を書くと、「N+1クエリ問題」が発生し、パフォーマンスが著しく低下する可能性がある。例えば、ユーザーごとに投稿数を計算するアクセサを追加し、それがデータベースに問い合わせる場合、100人のユーザーをJSON出力すると、ユーザー取得のための1回のクエリに加えて、各ユーザーの投稿数を数えるために100回のクエリが追加で発行されてしまう。合計101回のクエリとなり、非常に非効率的だ。この問題は、User::withCount('blogPosts')->get()->toArray()のように、最初のユーザー取得クエリの中で投稿数も一緒に数えるようにデータベースに指示することで回避できる。Appendsで追加する値は、できるだけモデルがすでに持っているデータを使って計算するようにし、データベースへの問い合わせは避けるべきだ。
日付や時刻の表示形式も、JSON出力時に制御できる。デフォルトではISO 8601形式という標準的な形式で出力されるが、例えばcreated_atを「2026-01-01」のように、年-月-日の形式で表示したい場合があるだろう。個別のカラムの日付フォーマットを変更するには、モデルのcasts()メソッド内で'birthday' => 'date:Y-m-d'のように指定する。これにより、誕生日が指定した形式で出力される。モデル内のすべての日付のフォーマットを一括で変更したい場合は、serializeDate()メソッドをオーバーライドし、その中で$date->format('Y-m-d')のように具体的なフォーマットを記述する。これにより、すべてのDateTimeInterface型のプロパティが指定した形式で出力されるようになる。これらのフォーマット変更は、JSON出力時にのみ影響し、データベースに保存されている日付の値自体は変更されない。
最後に、JSON出力の要件がより複雑になる場合、例えばAPIのバージョン管理、条件に応じたフィールドの表示・非表示、モデルの構造とは異なる安定したAPIレスポンスの形が必要になった場合は、Laravelが提供する「APIリソース」を利用することを検討すべきだ。シンプルなエンドポイントや内部ツールでは、ここまで説明したシリアライズ機能で十分だが、より堅牢で保守しやすいAPIを構築するためには、APIリソースの方が適している。
まとめると、以下の点を覚えておくと良いだろう。パスワードやトークンなどの機密情報はHidden属性で隠すか、より安全なVisible属性で許可リストを作る。makeVisible()やmakeHidden()はモデルオブジェクト自体を変更するので、使い回しには注意する。算出値を追加するAppendsは便利だが、そのアクセサ内でデータベースクエリが実行されるとN+1問題が発生し、パフォーマンスが低下する原因となるため、できるだけ避け、withCount()のような機能を利用する。日付の表示形式は、casts()でカラムごとに設定するか、serializeDate()をオーバーライドしてモデル全体に適用できる。これらの機能を適切に使いこなすことで、安全かつ効率的なデータ連携が可能になる。