Eloquent 关系图例(译)

在学习 Eloquent Model 关系的时候,看官网的文档感觉自己理解得不够透彻,基本上 1 对 1,1 对多,多对多有个模糊概念,当然,中文的翻译文档给的例子也很不错。找到了这么一篇文章,我觉得很好的解决了我的疑问,文章为:Eloquent Relationships Cheat Sheet

Laravel 5.5 的 Eloquent ORM 关系图

一对一#

Demo 描述#

在这个 Demo 中,我们有 2 个模型(Owner 和 Car)和 2 张表 (owners, cars)

关系描述#

车主 (Owner) 可以拥有 1 台车 (Car)
1 台车 (Car) 对应着 1 个车主 (Owner)

关系图表#

关系图描述#

Cars 表中需要存储 Owner ID 外键,用于查找对应的车主

Eloquent 模型#

class Owner
{
    public function car()
    {
       return $this->hasOne(Car::class);
    }
}
class Car
{
    public function owner()
    {
        return $this->belongsTo(Owner::class);
    }
}

数据表生成#

Schema::create('owners', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
});
Schema::create('cars', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->integer('owner_id')->unsigned()->index()->nullable();
    $table->foreign('owner_id')->references('id')->on('owners');
});

存储数据#

// 更新car和owner之间的关系
$owner->car()->save($car);
$car->owner()->associate($owner)->save();

注:两个的效果其实是一样的,都是更新Car列表中的owner_id。这样一来,我们就无需使用以下这样的更新方式。
Car::where('id',$car->id)->update(['owner_id' => $owner->id]);

获取数据#

//获取当前Owner拥有的car
$owner->car;
//获取当前car的Owner
$car->owner;

一对多关系#

Demo 描述#

注:原文用的是 Thief,但是我认为我们可以将上文的 Owner 拿来用,即 1 个 Owner 可以拥有多辆 Car,所以下面都进行了更改

在这边,我们有 2 个模型(Owner 和 Car)和 2 张表 (owners, cars)

关系描述#

车主 (Owner) 可以拥有多台车 (Car)
1 台车 (Car) 只能对应着 1 个车主 (Owner)

关系图表#

注:将 Thief 当作是 Owner

关系图描述#

Cars 表中需要存储 Owner ID 外键,用于查找对应的车主

Eloquent 模型#

class Owner
{
    public function cars()
    {
       return $this->hasMany(Car::class);
    }
}
class Car
{
    public function owner()
    {
        return $this->belongsTo(Owner::class);
    }
}

数据表生成#

Schema::create('owners', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
});
Schema::create('cars', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->integer('owner_id')->unsigned()->index()->nullable();
    $table->foreign('owner_id')->references('id')->on('owners');
});

存储数据#

// 更新car和owner之间的关系
这边和上面就有些不太一样了
$owner->cars()->saveMany([
   $car1, 
   $car2,
]);
一次性更新多台车的owner_id
$owner->cars()->save($car);
获取和上文一样,更新单个model

$car->owner()->associate($owner)->save();

获取数据#

//获取当前Owner拥有的car
$owner->cars;
//获取当前car的Owner
$car->owner;

多态一对多关系#

注:简单解释多态的概念,即一个模型在单个关联上属于多个其他模型,也就是 1 台车属于一个车主,但是车主可以是男或女两个模型。

Demo 描述#

在这个 demo 中,我们有 3 个模型 (Man, Woman, Car),3 张表 (men, women, cars)

关系描述#

Man (男买家) 可以卖多辆车 (Cars)
Man (女买家) 可以卖多辆车 (Cars)
一辆车只能属于一个买家 (Man or Woman)

关系图表#

关系图描述#

Car 表会储存 BuyerID 和 BuyerType,其中的 BuyerType 可以是 Man 或者 Woman 这两个 model name,但不局限于这两个。

Eloquent 模型#

class Man
{
    public function cars()
    {
        return $this->morphMany(Car::class, 'buyer');
    }
}
class Woman
{
    public function cars()
    {
        return $this->morphMany(Car::class, 'buyer');
    }
}
class Car
{
    public function buyer()
    {
        return $this->morphTo();
    }
}
从这边来说,我们将hasMany变成了morphMany,同时增加了一个buyer关系。而Car中的belongTo也变成了morphTo

数据表生成#

Schema::create('men', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
});
Schema::create('women', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
});
Schema::create('cars', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');

    $table->morphs(‘buyer’);
    //以下两句可以被上面这句所替代
    //$table->integer('buyer_id')->unsigned()->index()->nullable();
    //$table->string('buyer_type')->nullable();   
});

存储数据#

//修改Car表中的数据
/*
$man:   id=1
$car1: id =1, buyer_id=null, buyer_type= null
经过操作后
$car1: id = 1, buyer_id=1, buyer_type= man
*/
$man->cars()->saveMany([
   $car1, 
   $car2,
]);
$woman->cars()->saveMany([
   $car1, 
   $car2,
]);

// 以下为单Model操作
$man->cars()->save($car);
$woman->cars()->save($car);

// 同上,变动的是buyer_id和buyer_type
$car1->buyer()->associate($man)->save();
$car2->buyer()->associate($woman)->save();

获取数据#

//获取所属的cars
$men->cars
$women->cars
// 获取Car的buyer数据,Man或者Woman
$car->buyer

注:从上面几个例子可知,不管是一对一,一对多,多态一对多,我们始终修改的都是 Cars 这张表对应的 model 外键字段。

多对多关系#

Demo 描述#

在这个 demo 中,我们有 2 个模型 (Driver,Car), 3 张表 (drivers, cars,中间关联表 car_driver)

关系描述#

一个司机 (Driver) 可以开不同类型的车 (Cars)
一辆车 (car) 可以由不同人 (Drivers) 来驾驶

关系图表#

关系描述#

中间关联表 car_driver 存储了两张表的关联 id driver_idcar_id

Eloquent 模型#

class Driver
{
    public function cars()
    {
       return $this->belongsToMany(Car::class);
    }
}
class Car
{
    public function drivers()
    {
        return $this->belongsToMany(Driver::class);
    }
}

数据表生成#

Schema::create('drivers', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
});
Schema::create('cars', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
});
Schema::create('car_driver', function (Blueprint $table) {
    $table->increments('id');

    $table->integer('car_id')->unsigned()->index();
    $table->foreign('car_id')->references('id')->on('cars')->onDelete('cascade');

    $table->integer('driver_id')->unsigned()->index();
    $table->foreign('driver_id')->references('id')->on('drivers')->onDelete('cascade');
});

存储数据#

// 多数据存储,更新关联表的car_id
$driver->cars()->attach([
   $car1->id,
   $car2->id,
]);

// 反向操作,更新关联表的driver_id
$car->drivers()->attach([
   $driver1->id,
   $driver2->id,
]);

获取数据#

// 获取相关联的cars
$driver->cars
// 获取相关联的drivers
$car->drivers

多态多对多关系#

Demo 描述#

在这个 Demo 中,我们有 3 个模型 (Valet, Owner,Car),4 张表 (valets, owners, cars, drivers)
注:在这边的话,我认为 valet 和 owner 相对于 drivers 并不是特别妥当,但是你可以忽略它,当这两个是 drivers 的两个不同的普通类型,如 Dad 或者 Mom

关系描述#

Valet 和 Owner 都属于 Drivers
1 个 Driver 可以开多辆车 (Cars)
1 辆车也可以有不同的司机(Drivers,只是属于不同的类型 (Valet 或 Owner))

关系图表#

关系图描述#

由多对多可知,我们通过 car_driver 存储了 driver_id 和 car_id,对应 driver 和 car 表。但是多态多对多的情况下,我们需要的是 model 的对应关系是 (Car 和 Valet, Owner 直接的对应),所以中间关联表 drivers 需要包含 3 个字段:driver_id, driver_type, car_id。前两个是多态对应 Valet,Owner,后一个对应 Car

Eloquent 模型#

class Valet
{
    public function cars()
    {
        return $this->morphToMany(Car::class, 'driver');
    }
}
class Owner
{
    public function cars()
    {
        return $this->morphToMany(Car::class, 'driver');
    }
}
class Car
{
    public function valets()
    {
        return $this->morphedByMany(Valet::class, 'driver');
    }

    public function owners()
    {
        return $this->morphedByMany(Owner::class, 'driver');
    }
}

由上面可知,belongToMany更换成了morphToMany,传入了driver字段。而Car model则更换成了morphedByMany,分别对应着不同的model

存储数据#

// 多数据处理
$valet->cars()->saveMany([$car1, $car2]);
$owner->cars()->saveMany([$car1, $car2]);

//单数据处理
$valet->cars()->save($car1);
$owner->cars()->save($car1);

//以下的处理方式不同
//多数据处理
$car->valets()->attach([
    $valet1->id,
    $valet2->id,
]);
$car->owners()->attach([
    $owner1->id,
    $owner2->id,
]);

获取数据#

// 获取driver的相关cars
$valet->cars
$owner->cars
// 获取car的相关drivers
$car->owners
$car->valets

总结关系图#


来自原文章

个人总结#

如果对于关系概念不熟的,可以先看看 Laravel5.5 中文文档。这篇文章主要是整合了例子和代码,我们可以按照代码手动的操作一遍,加深自己的理解。

对于翻译这篇文章,感觉自己的底子薄弱,看英文理解不会太吃力,但是要想把它翻译成可理解阅读的中文实在是很困难,佩服那些翻译文档的人员。

本作品采用《CC 协议》,转载必须注明作者和本文链接
一入 php 深似海,从此 c++ 是路人
本帖由系统于 6年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。