Eloquent 模型关系
Overview
当您使用关系数据库时,Eloquent ORM 将模型存储为与模型类对应的表中的行。 当您使用MongoDB时,Laravel 集成会将模型存储为与模型类对应的集合中的文档。
要定义关系,请在模型类中添加一个函数,用于调用适当的关系方法。 此函数允许您将相关模型作为动态属性进行访问。 动态属性允许您使用与访问模型属性相同的事务语法来访问相关模型。
以下部分描述了 Laravel 集成中可用的 Laravel Eloquent 和MongoDB特定关系,并举例说明了如何定义和使用这些关系:
一对一关系
模型之间的一对一关系由与另一种类型的模型记录完全相关的模型记录组成。
当您添加一对一关系时,Eloquent 允许您使用动态属性访问模型,并将模型的文档 ID 存储在相关模型上。
在 Laravel MongoDB 中,您可以使用hasOne()
方法或belongsTo()
方法定义一对一关系。
当您使用belongsTo()
方法添加反向关系时,Eloquent 允许您使用动态属性访问模型,但不添加任何字段。
要了解有关一对一关系的更多信息,请参阅 一对一 在 Laravel 文档中。
一对一示例
以下示例类展示了如何使用hasOne()
方法在Planet
和Orbit
模型之间定义HasOne
一对一关系:
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);
// Document in the "planets" collection { _id: ObjectId('65de67fb2e59d63e6d07f8b8'), name: 'Earth', diameter_km: 12742, // ... } // Document in the "orbits" collection { _id: ObjectId('65de67fb2e59d63e6d07f8b9'), period: 365.26, direction: 'counterclockwise', planet_id: '65de67fb2e59d63e6d07f8b8', // ... }
以下示例代码展示了如何使用示例类中定义的动态属性来访问相关模型:
$planet = Planet::first(); $relatedOrbit = $planet->orbit; $orbit = Orbit::first(); $relatedPlanet = $orbit->planet;
一对多关系
模型之间的一对多关系由作为父模型的模型和一个或多个相关的子模型记录组成。
当您添加一对多关系方法时,Eloquent 允许您使用动态属性访问模型,并在每个子模型文档中存储父模型的文档 ID。
在 Laravel MongoDB 中,您可以通过在父类上添加hasMany()
方法以及在子类上添加belongsTo()
方法(可选)来定义一对多关系。
当您使用belongsTo()
方法添加反向关系时,Eloquent 允许您使用动态属性访问父模型,而无需添加任何字段。
要了解有关一对多关系的更多信息,请参阅 一对多 在 Laravel 文档中。
一对多示例
以下示例类展示了如何使用hasMany()
方法在Planet
父模型和Moon
子模型之间定义HasMany
一对多关系:
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);
// Parent document in the "planets" collection { _id: ObjectId('65dfb0050e323bbef800f7b2'), name: 'Jupiter', diameter_km: 142984, // ... } // Child documents in the "moons" collection [ { _id: ObjectId('65dfb0050e323bbef800f7b3'), name: 'Ganymede', orbital_period: 7.15, planet_id: '65dfb0050e323bbef800f7b2', // ... }, { _id: ObjectId('65dfb0050e323bbef800f7b4'), name: 'Europa', orbital_period: 3.55, planet_id: '65dfb0050e323bbef800f7b2', // ... } ]
以下示例代码展示了如何使用示例类中定义的动态属性来访问相关模型。
$planet = Planet::first(); $relatedMoons = $planet->moons; $moon = Moon::first(); $relatedPlanet = $moon->planet;
多对多关系
多对多关系由两种不同模型类型之间的关系组成,其中,对于每种模型类型,该模型的一个实例都可以与另一种类型的多个实例相关。
在 Laravel MongoDB 中,您可以通过将belongsToMany()
方法添加到两个相关类来定义多对多关系。
在关系数据库中定义多对多关系时,Laravel 会创建一个数据透视表来追踪这些关系。 当您使用 Laravel 集成时,它会省略数据透视表的创建,并将相关文档ID 添加到从相关模型类名称派生的文档字段中。
提示
由于 Laravel 集成使用文档字段而不是数据透视表,因此请从belongsToMany()
构造函数中省略数据透视表参数或设立其设置为null
。
要了解有关 Laravel 中多对多关系的更多信息,请参阅 Many to Many 在 Laravel 文档中。
以下部分举例说明了如何在模型类之间创建多对多关系。
多对多示例
以下示例类展示了如何使用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);
// Documents in the "planets" collection [ { _id: ObjectId('65e1043a5265269a03078ad0'), name: 'Earth', // ... space_explorer_ids: [ '65e1043b5265269a03078ad3', '65e1043b5265269a03078ad4', '65e1043b5265269a03078ad5' ], }, { _id: ObjectId('65e1043a5265269a03078ad1'), name: 'Mars', // ... space_explorer_ids: [ '65e1043b5265269a03078ad4', '65e1043b5265269a03078ad5' ] }, { _id: ObjectId('65e1043b5265269a03078ad2'), name: 'Jupiter', // ... space_explorer_ids: [ '65e1043b5265269a03078ad3', '65e1043b5265269a03078ad5' ] } ] // Documents in the "space_explorers" collection [ { _id: ObjectId('65e1043b5265269a03078ad3'), name: 'Tanya Kirbuk', // ... planet_ids: [ '65e1043a5265269a03078ad0', '65e1043b5265269a03078ad2' ] }, { _id: ObjectId('65e1043b5265269a03078ad4'), name: 'Mark Watney', // ... planet_ids: [ '65e1043a5265269a03078ad0', '65e1043a5265269a03078ad1' ] }, { _id: ObjectId('65e1043b5265269a03078ad5'), name: 'Jean-Luc Picard', // ... planet_ids: [ '65e1043a5265269a03078ad0', '65e1043a5265269a03078ad1', '65e1043b5265269a03078ad2' ] } ]
以下示例代码展示了如何使用示例类中定义的动态属性来访问相关模型。
$planet = Planet::first(); $explorers = $planet->visitors; $spaceExplorer = SpaceExplorer::first(); $explored = $spaceExplorer->planetsVisited;
嵌入式文档模式
在 MongoDB 中,嵌入式文档模式将相关模型的数据添加到父模型中,而不是保留外键引用。 使用此模式可以满足以下一项或多项要求:
将关联的数据保存在单个集合中
对文档的多个字段和关联数据执行原子更新
减少获取数据所需的读取次数
在 Laravel MongoDB 中,您可以通过添加以下方法之一来定义嵌入式文档:
embedsOne()
嵌入单个文档embedsMany()
嵌入多个文档
注意
这些方法返回 Eloquent 集合,与查询构建器对象不同。
要学习;了解有关MongoDB嵌入式文档模式的更多信息,请参阅以下MongoDB Server教程:
以下部分举例说明如何使用嵌入式文档模式。
嵌入式文档示例
以下示例类展示了如何使用embedsMany()
方法在SpaceShip
和Cargo
模型之间定义EmbedsMany
一对多关系:
declare(strict_types=1); namespace App\Models; use MongoDB\Laravel\Eloquent\Model; use MongoDB\Laravel\Relations\EmbedsMany; class SpaceShip extends Model { protected $connection = 'mongodb'; 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);
// Document in the "space_ships" collection { _id: ObjectId('65e207b9aa167d29a3048853'), name: 'The Millenium Falcon', // ... cargo: [ { name: 'spice', weight: 50, // ... _id: ObjectId('65e207b9aa167d29a3048854') }, { name: 'hyperdrive', weight: 25, // ... _id: ObjectId('65e207b9aa167d29a3048855') } ] }
跨数据库关系
Laravel MongoDB中的跨数据库关系是存储在关系数据库中的模型与存储在MongoDB database中的模型之间的关系。
添加跨数据库关系时,Eloquent 允许您使用动态属性访问相关模型。
Laravel 集成支持以下跨数据库关系方法:
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); } }
以下示例代码展示了如何在MySQL数据库中创建 SpaceShip
模型和在MongoDB database中创建相关的 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);
-- Row in the "space_ships" table +------+----------+ | id | name | +------+----------+ | 1234 | Nostromo | +------+----------+ // Document in the "passengers" collection [ { _id: ObjectId('65e625e74903fd63af0a5524'), name: 'Ellen Ripley', space_ship_id: 1234, // ... }, { _id: ObjectId('65e625e74903fd63af0a5525'), name: 'Dwayne Hicks', space_ship_id: 1234, // ... } ]