Eloquent 模型关系
概述
当你使用关系型数据库时,Eloquent ORM 将模型存储为对应模型类的表格中的行。当你使用 MongoDB 时,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); } }
以下示例代码展示了如何为每个类实例化一个模型并添加它们之间的关系。点击查看输出按钮以查看运行代码创建的数据
$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); } }
以下示例代码显示了如何为每个类实例化一个模型,并在它们之间添加关系。点击 查看输出 按钮以查看运行代码创建的数据。
$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 中的多对多关系的信息,请参阅 Laravel 文档中的 多对多。
以下部分展示了如何在模型类之间创建多对多关系的示例。
多对多示例
以下示例类展示了如何使用 belongsToMany
方法定义一个 Planet
和 SpaceExplorer
模型之间的多对多关系。
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
模型之间的逆向多对多关系。
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); } }
以下示例代码显示了如何为每个类实例化一个模型,并在它们之间添加关系。点击 查看输出 按钮以查看运行代码创建的数据。
$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 服务器教程:
以下部分展示了如何使用嵌入式文档模式的示例。
嵌入式文档示例
以下示例类展示了如何通过使用 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 文档。点击 查看输出 按钮查看运行代码创建的数据。
$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 数据库中的模型之间的关系。
当你添加跨数据库关系时,Eloquent 允许你通过动态属性访问相关模型。
Laravel 集成支持以下跨数据库关系方法:
hasOne()
hasMany()
belongsTo()
为了定义跨数据库关系,必须在关系数据库中存储的类中导入 MongoDB\Laravel\Eloquent\HybridRelations
包。
以下部分展示了如何定义跨数据库关系的示例。
跨数据库关系示例
以下示例类展示了如何定义存储在关系型数据库中的HasMany
关系和存储在MongoDB数据库中的Passenger
模型之间的关系
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数据库中创建相关的Passenger
模型,以及运行代码后创建的数据。点击查看输出按钮以查看运行代码创建的数据
$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, // ... } ]