コンテンツにスキップ

Eloquentリレーション

Laravelと同じように、スキーマでEloquentのリレーションを定義できます。

例えば、以下のようなモデルを定義した場合:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Post extends Model
{
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }

    public function author(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

単に、リレーションシップと同じ名前のフィールド(author, comments)をGraphQLのタイプとして追加すれば良いです

type Post {
  author: User
  comments: [Comment!]
}

Laravelのリレーションはモデル上の通常のプロパティと同様にアクセスすることができます。このため、デフォルトのフィールドリゾルバでも問題なく動作します。

N+1パフォーマンス問題を回避する

Eloquentのリレーションにプロパティとしてアクセスする場合、リレーションシップのデータは"遅延ロード"されます。 これは、最初にプロパティへアクセスするまでは、リレーションのデータは実際にはロードされないということを意味します。

これは、GraphQLクエリのネストされた性質から来る、一般的なパフォーマンスの落とし穴につながります。 いわゆるN+1クエリ問題です。詳しくはこちら

Lighthouseが組み込みで持つ([Comment!]などの)リレーションのディレクティブを使ってリレーションフィールドをデコレーションすると、クエリは自動的に結合されます。 ディレクティブでリレーションフィールドを装飾すると、Eloquentのバッチローディングの仕組みでクエリが自動的に結合されます。 これはつまり、特に追加の作業をしなくても、データベースへのクエリの回数が減り、パフォーマンスが向上するのです。

バッチローディングは、すべてのユースケースで理想的なパフォーマンスを提供するわけではありません。設定オプションで設定オプション batchload_relationsfalseに設定することで、これをオフにすることができます。

一対一のリレーション

スキーマの2つの型の間に一対一の関係を定義するには、@hasOneディレクティブを使用します。

type User {
  phone: Phone @hasOne
}

逆は @belongsTo ディレクティブで定義できます。

type Phone {
  user: User @belongsTo
}

一対多のリレーション

一対多のリレーションを定義するには @hasMany ディレクティブを使用します。

type Post {
  comments: [Comment!]! @hasMany
}

繰り返しになりますが、逆のパターンは @belongsTo ディレクティブで定義できます。

type Comment {
  post: Post! @belongsTo
}

多対多のリレーション

多対多のリレーションは、Laravelでは設定に少し手間がかかりますが、Lighthouseで定義するのは簡単です。 @belongsToManyディレクティブで定義します。

type User {
  roles: [Role!]! @belongsToMany
}

逆も同様です。

type Role {
  users: [User!]! @belongsToMany
}

リレーションの名称変更

リレーションを定義するとき、Lighthouseはフィールドとリレーションのメソッド名が同じであると仮定します。フィールドに別の名前を付ける必要がある場合は、メソッドの名前を指定する必要があります。

type Post {
  author: User! @belongsTo(relation: "user")
}

これは次のようなEloquentモデルで動作します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Post extends Model
{
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}