【Laravel】連想配列よりDTO(Data Transfer Object)を使うメリットとは?

西山秀治 / 2025年12月25日

UX / UI のデザインに強いWebシステムの開発と、BtoB Webマーケを支援するWeb制作を提供するN’s Creates (エヌズクリエイツ) 株式会社の西山です。

PHP、そしてLaravel開発において、私たちは毎日のように「連想配列」を使います。
['name' => 'John', 'email' => '...'] という形式は非常に手軽で、柔軟です。

しかし、プロジェクトが大きくなるにつれて、この便利さが牙を剥く瞬間があります。
ControllerからService、ServiceからJobへと、中身の不明な配列がバケツリレーされていく…いわゆる「闇鍋(やみなべ)」状態です。

今回は、この問題を解決するための設計パターン「DTO(Data Transfer Object)」について、コード比較を交えて解説します。


よくある光景:連想配列によるデータ受け渡し

まずは、DTOを使わない(連想配列を使った)一般的なコードを見てみましょう。

❌ 悪い例:中身がわからない「配列」

class UserService
{
    /**
     * ユーザー登録処理
     * @param array $data ← ここが諸悪の根源
     */
    public function register(array $data)
    {
        // $dataの中に何が入っているのか、コードを見ただけでは分からない
        // 'email' キーが存在する保証はない(Undefined indexエラーのリスク)
        
        $user = User::create([
            'name'  => $data['name'],
            'email' => $data['email'], // ここでタイポしたら終わり
            'role'  => $data['role'] ?? 'member',
        ]);

        // ... メール送信などの処理
    }
}

このコードには、以下の3つの大きなデメリットがあります。

  1. 何が入っているか不明: register メソッドを使う人は、どんなキー(name? user_name?)を渡せばいいのか、中身を読んで解読する必要があります。
  2. 補完が効かない: IDE(エディタ)は $data['...'] の中身を知らないため、自動補完が効きません。タイポ(入力ミス)が実行時エラーになるまで発覚しません。
  3. 型の保証がない: email に文字列ではなく配列が渡されてきても、PHPは実行するまで気づけません。

✅ 良い例:DTOによる「型のある」受け渡し

DTOとは、単純に「データを運ぶためだけのクラス」のことです。
PHP 8.2以降なら、readonly クラスを使って非常にシンプルに書けます。

1. DTOクラスを作成



namespace App\DataTransferObjects;

use Illuminate\Http\Request;

readonly class UserRegistrationData
{
    public string $name;
    public string $email;
    public string $role;

    public function __construct(
        string $name,
        string $email,
        string $role
    ) {
        $this->name = $name;
        $this->email = $email;
        $this->role = $role;
    }

    // リクエストからDTOを作る静的メソッドを用意すると便利
    public static function fromRequest(Request $request): self
    {
        // 実際には FormRequest の validated() データを使うのがベストですが、
        // ここでは汎用的に input() を使用しています。
        return new self(
            name: $request->input('name'),
            email: $request->input('email'),
            role: $request->input('role') ?? 'member',
        );
    }
}

2. Service層でDTOを受け取る

class UserService
{
    /**
     * ユーザー登録処理
     * @param UserRegistrationData $data ← 何が来るか明確!
     */
    public function register(UserRegistrationData $data)
    {
        // IDEの補完がバリバリ効く
        // プロパティが存在することが保証されている
        
        $user = User::create([
            'name'  => $data->name,
            'email' => $data->email,
            'role'  => $data->role,
        ]);

        // ...
    }
}

DTO導入のメリット・デメリット比較

配列とDTO、それぞれの特徴を表にまとめました。

比較項目 連想配列 (Array) DTO (Object)
実装コスト 低い(書くだけ) 中(クラス定義が必要)
IDE補完 × 全く効かない ◎ 完璧に効く
可読性 × 処理を追わないと不明 ◎ 定義を見れば一目瞭然
安全性 × キーの存在保証なし ◎ 型と存在が保証される
リファクタリング × 影響範囲が探せない ◎ プロパティ名変更も容易

結論:いつDTOを使うべきか?

「たった数行のデータ受け渡し」であれば、連想配列でも問題ありません。
しかし、以下の条件に当てはまる場合は、迷わずDTOの導入をおすすめします。

  • データが3層以上(Controller → Service → Job など)をまたいで移動する場合
  • 配列のキー(user_id なのか id なのか)を毎回確認している自分に気づいた時
  • チーム開発で、他のメンバーが書いたメソッドを呼び出す場合

DTOは、開発者の認知負荷(脳のメモリ)を減らすための強力な武器です。
「配列地獄」から脱出し、型安全で快適なLaravelライフを送りましょう。

UX / UI のデザインに強いWebシステムの開発と、BtoB Webマーケを支援するWeb制作を提供する
N's Creates 株式会社は、神戸三宮オフィスまで週1出社(それ以外はリモートワーク)できる「デザイナー」「エンジニア」を募集しています。

興味のある方は、カジュアル面談しますので気軽にお問い合わせください!

同じテーマの記事