Overview
リレーショナルデータベースを使用する場合、Eloquet ORM はモデル クラスに対応するテーブルの行としてモデルを保存します。 MongoDB を使用すると、Lambda 統合は、モデル クラスに対応するコレクション内のドキュメントとしてモデルを保存します。
関係を定義するには、適切な関係メソッドを呼び出す関数をモデル クラスに追加します。 この関数を使用すると、関連するモデルを動的プロパティとしてアクセスできます。 動的プロパティを使用すると、モデル上のプロパティにアクセスするのと同じ構文を使用して関連モデルにアクセスできます。
次のセクションでは、Lalavel 統合で利用可能な Lambel Eloquet と MongoDB 固有の関係について説明し、その定義方法と使用方法の例を示します。
1 対 1 の関係。
hasOne()メソッドとその逆のbelongsTo()を使用して作成1 対多の関係。
hasMany()とその逆のbelongsTo()を使用して作成多対多の関係。
belongsToMany()メソッドを使用して作成埋め込みドキュメント パターン : MongoDB 固有の関係で、1 対 1 または 1 対多の関係を表すことができ、 メソッドまたは メソッドを使用して作成されます。
embedsOne()embedsMany()クロスデータベース関係。MongoDB と SQL モデル間の関係を作成する場合に必要です
関係を定義した後、このガイドの関連モデルの集計セクションで説明されているように、関連モデルのカウントやその他の集計値を計算できます。
1 対 1 の関係
モデル間の 1 対 1 の関係は、他の 1 つのタイプのモデル レコードと完全に関連するモデル レコードで構成されます。
1 と 1 の関係を追加すると、Eloquen は 動的プロパティを使用してモデルにアクセスし、モデルのドキュメント ID を関連モデルに保存できるようにします。
Lambda MongoDBでは、hasOne() メソッドまたは belongsTo() メソッドを使用して 1 対 1 の関係を定義できます。
belongsTo()メソッドを使用して関係の逆を追加すると、Eloquet は 動的プロパティを使用してモデルにアクセスできますが、フィールドは追加されません。
1 対 1 の関係の詳細については、Lambel ドキュメントの「 1 から 1 へ 」を参照してください。
1 から 1 の例
HasOnePlanet次のサンプルクラスは、Orbit メソッドを使用して、 モデルと モデル間で の 1 対 1hasOne() の関係を定義する方法を示しています。
declare(strict_types=1);
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;use MongoDB\Laravel\Relations\HasOne;
class Planet extends Model{ protected $connection = 'mongodb';
public function orbit(): HasOne { return $this->hasOne(Orbit::class); }}次のサンプルクラスは、 belongsTo()メソッドを使用して、 OrbitとPlanetの間の逆BelongsTo関係を定義する方法を示しています。
declare(strict_types=1);
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;use MongoDB\Laravel\Relations\BelongsTo;
class Orbit extends Model{ protected $connection = 'mongodb';
public function planet(): BelongsTo { return $this->belongsTo(Planet::class); }}次のサンプル コードは、各クラスのモデルをインスタンス化し、それら間の関係を追加する方法を示しています。 コードの実行によって作成されたデータを確認するには、[ VIEW OUTPUT ] ボタンをクリックします。
$planet = new Planet();$planet->name = 'Earth';$planet->diameter_km = 12742;$planet->save();
$orbit = new Orbit();$orbit->period = 365.26;$orbit->direction = 'counterclockwise';
$planet->orbit()->save($orbit);次のサンプル コードは、サンプル クラスで定義されている動的プロパティを使用して関連モデルにアクセスする方法を示しています。
$planet = Planet::first();$relatedOrbit = $planet->orbit;
$orbit = Orbit::first();$relatedPlanet = $orbit->planet;1 対多の関係
モデル間の 1 対多の関係は、親であるモデルと、関連する子モデル レコードの 1 つ以上で構成されます。
1 対多の関係メソッドを追加すると、Eloquen は 動的プロパティを使用してモデルにアクセスし、親モデルのドキュメント ID を各子モデル ドキュメントに保存できます。
MongoDBでは、親クラスに hasMany() メソッドを追加し、オプションで子クラスに belongsTo() メソッドを追加することで、1 対多の関係を定義できます。
belongsTo()メソッドを使用して関係の逆を追加すると、Eloquen はフィールドを追加せずに 動的プロパティ を使用して親モデルにアクセスできるようになります。
1 対多の関係の詳細については、Lambel ドキュメントの「 1 から多への 」を参照してください。
1 対多の例
次のサンプルクラスは、 hasMany()メソッドを使用して、 Planet親モデルとMoon子モデル間でHasManyの 1 対多関係を定義する方法を示しています。
declare(strict_types=1);
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;use MongoDB\Laravel\Relations\HasMany;
class Planet extends Model{ protected $connection = 'mongodb';
public function moons(): HasMany { return $this->hasMany(Moon::class); }}次のサンプル クラスは、 belongsTo()メソッドを使用して、 Moon子モデルとPlanet親モデルの間で逆のBelongsTo関係を定義する方法を示しています。
declare(strict_types=1);
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;use MongoDB\Laravel\Relations\BelongsTo;
class Moon extends Model{ protected $connection = 'mongodb';
public function planet(): BelongsTo { return $this->belongsTo(Planet::class); }}次のサンプル コードは、各クラスのモデルをインスタンス化し、それら間の関係を追加する方法を示しています。 コードの実行によって作成されたデータを確認するには、[ VIEW OUTPUT ] ボタンをクリックします。
$planet = new Planet();$planet->name = 'Jupiter';$planet->diameter_km = 142984;$planet->save();
$moon1 = new Moon();$moon1->name = 'Ganymede';$moon1->orbital_period = 7.15;
$moon2 = new Moon();$moon2->name = 'Europa';$moon2->orbital_period = 3.55;
$planet->moons()->save($moon1);$planet->moons()->save($moon2);次のサンプル コードは、サンプル クラスで定義されている動的プロパティを使用して、関連するモデルにアクセスする方法を示しています。
$planet = Planet::first();$relatedMoons = $planet->moons;
$moon = Moon::first();$relatedPlanet = $moon->planet;多対多の関係
多対多の関係は、2 つの異なるモデルタイプ間の関係で構成されており、各タイプのモデルのインスタンスは、他のタイプの複数のインスタンスに関連付けることができます。
Lambda MongoDBでは、関連するクラスの両方に belongsToMany() メソッドを追加することで、多対多の関係を定義できます。
リレーショナルデータベースで多対多の関係を定義すると、Lambda は関係を追跡するためのピボットテーブルを作成します。 Lambda 統合を使用すると、ピボットテーブルの作成が省略され、関連するモデル クラス名から派生したドキュメント フィールドに関連するドキュメント ID が追加されます。
Tip
Lambda 統合ではピボット テーブルの代わりにドキュメント フィールドが使用されるため、 belongsToMany()コンストラクターからピボット テーブル パラメーターを省略するか、 nullに設定します。
Lambel の多対多の関係の詳細については、Lambel ドキュメントの多対多を参照してください。
次のセクションでは、モデル クラス間の多対多の関係を作成する方法の例を示します。
多対多の例
次のサンプルクラスは、 belongsToMany()メソッドを使用して、 PlanetとSpaceExplorerモデル間でBelongsToManyの多対多の関係を定義する方法を示しています。
declare(strict_types=1);
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;use MongoDB\Laravel\Relations\BelongsToMany;
class Planet extends Model{ protected $connection = 'mongodb';
public function visitors(): BelongsToMany { return $this->belongsToMany(SpaceExplorer::class); }}次のサンプルクラスは、BelongsToMany SpaceExplorerメソッドを使用して、 モデルとPlanet モデル間で逆 多対多の関係を定義する方法を示しています。belongsToMany()
declare(strict_types=1);
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;use MongoDB\Laravel\Relations\BelongsToMany;
class SpaceExplorer extends Model{ protected $connection = 'mongodb';
public function planetsVisited(): BelongsToMany { return $this->belongsToMany(Planet::class); }}次のサンプル コードは、各クラスのモデルをインスタンス化し、それら間の関係を追加する方法を示しています。 コードの実行によって作成されたデータを確認するには、[ VIEW OUTPUT ] ボタンをクリックします。
$planetEarth = new Planet();$planetEarth->name = 'Earth';$planetEarth->save();
$planetMars = new Planet();$planetMars->name = 'Mars';$planetMars->save();
$planetJupiter = new Planet();$planetJupiter->name = 'Jupiter';$planetJupiter->save();
$explorerTanya = new SpaceExplorer();$explorerTanya->name = 'Tanya Kirbuk';$explorerTanya->save();
$explorerMark = new SpaceExplorer();$explorerMark->name = 'Mark Watney';$explorerMark->save();
$explorerJeanluc = new SpaceExplorer();$explorerJeanluc->name = 'Jean-Luc Picard';$explorerJeanluc->save();
$explorerTanya->planetsVisited()->attach($planetEarth);$explorerTanya->planetsVisited()->attach($planetJupiter);$explorerMark->planetsVisited()->attach($planetEarth);$explorerMark->planetsVisited()->attach($planetMars);$explorerJeanluc->planetsVisited()->attach($planetEarth);$explorerJeanluc->planetsVisited()->attach($planetMars);$explorerJeanluc->planetsVisited()->attach($planetJupiter);次のサンプル コードは、サンプル クラスで定義されている動的プロパティを使用して、関連するモデルにアクセスする方法を示しています。
$planet = Planet::first();$explorers = $planet->visitors;
$spaceExplorer = SpaceExplorer::first();$explored = $spaceExplorer->planetsVisited;埋め込みドキュメント パターン
MongoDB では、埋め込みドキュメント パターンは、外部キー参照を保持する代わりに、関連モデルのデータを親モデルに追加します。 このパターンを使用して、次の要件の 1 つ以上を満たす。
関連するデータを単一のコレクションにまとめて保持
ドキュメントと関連するデータの複数のフィールドに対してアトミックな更新を実行
データの取得に必要な読み取り回数を減らす
Lambda MongoDB では、次のいずれかの方法を追加して埋め込みドキュメントを定義できます。
embedsOne(): 単一のドキュメントを埋め込みembedsMany(): 複数のドキュメントを埋め込み
注意
これらのメソッドは、クエリ ビルダ オブジェクトとは異なる Eloent コレクションを返します。
自動インストール
埋め込みリレーションにアクセスすると、Lambda 統合は埋め込みデータをデフォルトで 、かずのモデルインスタンスではなく、raw オブジェクトとして返します。埋め込みドキュメントが raw オブジェクトとして返される場合、次の動作が適用されます。
idフィールドはObjectId型で、string ではありません。日付フィールドは、形式が設定された文字列ではなく、
UTCDateTimeオブジェクトです。モデル属性のキャスト、メソッド、
toArray()直列化は適用されません。
埋め込みドキュメントをハイライトされたモデル インスタンスとして検索するには、親モデルの $withプロパティに関係名を追加します。このプロパティは、すべてのクエリの関係を自動的にロードすることで、Laravel 統合は埋め込みドキュメントをモデル インスタンスとして機能します。
次の例では、埋め込まれた addresses 関係をロードするために $with を使用する User モデルを定義します。
declare(strict_types=1);
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;use MongoDB\Laravel\Relations\EmbedsMany;
class User extends Model{ protected $with = ['addresses'];
public function addresses() { return $this->embedsMany(Address::class); }}モデルに $with = ['addresses'] が含まれているため、Laravel 統合は埋め込みアドレスを Address モデル インスタンスとして返します。toArray() メソッドは、idフィールドを string として、日付フィールドを形式化された string として直列化します。すべての Address モデル キャストとメソッドも利用できます。
MongoDB 埋め込みドキュメント パターンの詳細については、次の MongoDB Server チュートリアルを参照してください。
次のセクションでは、埋め込みドキュメントパターンの使用方法の例を示します。
埋め込みドキュメントの例
次のサンプルクラスは、EmbedsMany SpaceShipCargoメソッドを使用して、 モデルと モデル間で の 1embedsMany() 対多の関係を定義する方法を示しています。
declare(strict_types=1);
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;use MongoDB\Laravel\Relations\EmbedsMany;
class SpaceShip extends Model{ protected $connection = 'mongodb'; protected $with = ['cargo'];
public function cargo(): EmbedsMany { return $this->embedsMany(Cargo::class); }}埋め込みモデル クラスでは、次のCargoモデル クラスに示すように、関係の定義が省略されています。
declare(strict_types=1);
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;
class Cargo extends Model{ protected $connection = 'mongodb';}次のサンプル コードは、 SpaceShipモデルを作成し、複数のCargoモデルと、コードの実行によって作成された MongoDB ドキュメントを埋め込む方法を示しています。 コードの実行によって作成されたデータを確認するには、 VIEW OUTPUTボタンをクリックします。
$spaceship = new SpaceShip();$spaceship->name = 'The Millenium Falcon';$spaceship->save();
$cargoSpice = new Cargo();$cargoSpice->name = 'spice';$cargoSpice->weight = 50;
$cargoHyperdrive = new Cargo();$cargoHyperdrive->name = 'hyperdrive';$cargoHyperdrive->weight = 25;
$spaceship->cargo()->attach($cargoSpice);$spaceship->cargo()->attach($cargoHyperdrive);クロスデータベースの関係
Lambda MongoDB の クロスデータベース関係 とは、リレーショナルデータベースに保存されているモデルと MongoDB データベースに保存されているモデルとの間の関係です。
クロスデータベースの関係を追加すると、Eloquet は 動的プロパティを使用して関連するモデルにアクセスできるようにします。
Lambda 統合は、次のデータベース間の関係メソッドをサポートしています。
hasOne()hasMany()belongsTo()
クロスデータベース関係を定義するには、関係データベースに保存されている クラスにMongoDB\Laravel\Eloquent\HybridRelationsパッケージをインポートする必要があります。
次のセクションでは、データベース間の関係を定義する方法の例を示します。
クロスデータベース関係の例
次のサンプルクラスは、リレーショナルデータベースに保存されているSpaceShipモデルと MongoDB database に保存されているPassengerモデルとの間でHasManyの関係を定義する方法を示しています。
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\HasMany;use MongoDB\Laravel\Eloquent\HybridRelations;
class SpaceShip extends Model{ use HybridRelations;
protected $connection = 'sqlite';
public function passengers(): HasMany { return $this->hasMany(Passenger::class); }}次のサンプルクラスは、 belongsTo()メソッドを使用して、 PassengerモデルとSpaceshipモデルの間で逆のBelongsTo関係を定義する方法を示しています。
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo;use MongoDB\Laravel\Eloquent\Model;
class Passenger extends Model{ protected $connection = 'mongodb';
public function spaceship(): BelongsTo { return $this->belongsTo(SpaceShip::class); }}Tip
関係データベースのテーブル スキーマで定義されているプライマリキーが、モデルで使用されているプライマリキーと一致していることを確認します。 Lambda のプライマリキーとスキーマ定義の詳細については、 Lambda ドキュメントの次のページを参照してください。
次のサンプル コードは、MySQL データベースにSpaceShipモデルを作成し、MongoDB データベースに関連するPassengerモデルを作成する方法と、コードの実行によって作成されたデータを示しています。 コードの実行によって作成されたデータを確認するには、 VIEW OUTPUTボタンをクリックします。
$spaceship = new SpaceShip();$spaceship->id = 1234;$spaceship->name = 'Nostromo';$spaceship->save();
$passengerEllen = new Passenger();$passengerEllen->name = 'Ellen Ripley';
$passengerDwayne = new Passenger();$passengerDwayne->name = 'Dwayne Hicks';
$spaceship->passengers()->save($passengerEllen);$spaceship->passengers()->save($passengerDwayne);関連モデルの集計
このセクションでは、関連するモデルを読み込まずに、それらのモデルの集計値を計算する方法について説明します。親モデルをクエリするときに with...() 集計メソッドを使用して集計を計算するか、対応する load...() メソッドを使用して、すでに検索したモデルインスタンスで計算します。次の表では、これらの方法について説明します。
クエリメソッド | インスタンス メソッド | 説明 | 関連するモデルがない場合の戻り値 |
|---|---|---|---|
|
| 関連するモデルの数をカウントします |
|
|
| 関連するモデルが存在するかどうかを示します。 |
|
|
| 関連するモデル全体で指定されたフィールドの値を加算します |
|
|
| 関連するモデル全体で、指定されたフィールドの平均を計算します |
|
|
| 関連するモデル全体で、指定されたフィールドの最小値を返します |
|
|
| 関連するモデル全体で、指定されたフィールドの最大値を返します |
|
各メソッドは、親モデルに属性を追加します。属性名は、関係名、集計関数、そして withSum()、withAvg()、withMin()、withMax() メソッドの場合はフィールド名を組み合わせたものになります。例として、withCount('moons') は moons_count 属性を追加します。独自の属性名を設定するには、このセクションの 例 に示すように、関係名にエイリアスを追加します。
Tip
これらのメソッドについてさらに学ぶには、Laravel ドキュメントの関連モデルの集計を参照してください。
集計の動作と制限
関連するモデルを独自のコレクションに保存する場合、Laravel Integrationは親モデルを検索するのと同じクエリで集計を計算できません。その代わりに、リクエストする集計ごとに余計なクエリを1つ実行し、各結果をそれが属する親モデルに割り当てます。増やす必要があるクエリについては、次の代替案を検討してください。
集計された値を親ドキュメントに保存します。例として、
planetsコレクションの各ドキュメントにmoons_countフィールドを保存し、月を追加または除く際に更新します。$lookup集計ステージを使用して、1 つのクエリ内でコレクションを結合します。詳細については、集計操作ガイドを参照してください。
Laravel Integrationは、embedsOne()メソッドやembedsMany()メソッドで定義されたような埋め込み関係の集計を、余計なクエリを実行することなく親ドキュメントから計算します。
Laravel Integrationは、以下の集計動作をサポートしておらず、それらを使用すると例外が発生します。
集計属性によるクエリ結果の並べ替え
データベース間の関係を集計する
MorphTo関係の集計埋め込み関係への制約(制限を含む)の適用
集計の例
この例では、1対多の関係セクションで定義されているPlanetモデルとMoonモデルを使用して、コレクション内の最初の惑星について次の情報を検索します。
惑星に衛星があるかどうか
周回する月の数
衛星の最長公転周期
$planet = Planet::withExists('moons') ->withCount('moons') ->withMax('moons', 'orbital_period') ->first();
echo 'Has moons: ' . ($planet->moons_exists ? 'yes' : 'no') . PHP_EOL;echo 'Number of moons: ' . $planet->moons_count . PHP_EOL;echo 'Longest orbital period: ' . $planet->moons_max_orbital_period . PHP_EOL;次の例では、エイリアスを使用して、デフォルトの moons_count 属性の代わりにカウントを total_moons 属性に保存します:
$planet = Planet::withCount('moons as total_moons')->first();次の例では、loadCount() メソッドを使用して、コードが既に検索したモデルインスタンスのカウントを計算します。
$planet = Planet::first();$planet->loadCount('moons');
echo 'Number of moons: ' . $planet->moons_count . PHP_EOL;