Dai Chong's blog

一对一

 什么是一对多?

 在程序角度讲:一对多基本上都是用来描述两个数据表之间的关系。

 假设现有两个数据表:(1)文章表posts (2)评论表comment。一个文章可以有多个评论,但一个评论只能属于一个文章,相对于评论来说它与文章的关系是多对一,相对与文章来说它与评论的关系是一对多,这也就是laravel里反向关联的基础。

文章表posts

idtitletime
1什么是一对一?2019-11-07
2什么是一对多?2019-11-08
3什么是多对多?2019-11-06



评论表comment

idinfouser_id
1我知道什么是一对一。10086
2我知道什么是一对多。10085
3我知道什么是多对多。10084
4这谁不知道啊!10084

中间表posts_comment(也可以叫做关系表)

idpost_idcomment_id
111
222
333
434

 从表的结构可以看出来,ID=1、2的文章只有一条评论,ID=3的文章有两条评论。

 那么这是要有个业务,已知文章的ID=3,请使用laravel的模型关联找出该文章下的所有评论。

 首先确认两个表之间的关系,一个评论在中间表只有一条数据,中间表里的comment_id肯定是唯一的,不可能出现一个评论同时属于两个文章,所以它们之间的关系是一对一但是,你会发现使用hasMany(多对多)关联方法获取的数据居然是一样的

模型关联
1
2
3
4
5
6
7
8
9
10
# 一对多写法
public function comments()
{
return $this->hasMany('App\Models\PostComment', 'comment_id', 'id');
}
# 一对一写法
public function comment()
{
return $this->hasOne('App\Models\PostComment', 'comment_id', 'id');
}
查询写法使用(with)
1
2
3
4
5
6
7
8
public function list()
{
$postId = 3;
$data = \App\Models\Comment::with('comments')->whereHas("comments", function ($query) use ($postId) {
return $query->where("post_id", $postId);
})->get();
dump($data->toArray());
}
获取的数据结果(使用with)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# with('comments);
array:2 [▼
0 => array:4 [▼
"id" => 3
"info" => "\t我知道什么是多对多"
"user_id" => 10084
"comments" => array:1 [▼
0 => array:3 [▼
"id" => 3
"post_id" => 3
"comment_id" => 3
]
]
]
1 => array:4 [▼
"id" => 4
"info" => "这谁不知道啊!"
"user_id" => 10084
"comments" => array:1 [▼
0 => array:3 [▼
"id" => 4
"post_id" => 3
"comment_id" => 4
]
]
]
]

# with('comment);
array:2 [▼
0 => array:4 [▼
"id" => 3
"info" => "\t我知道什么是多对多"
"user_id" => 10084
"comment" => array:3 [▼
"id" => 3
"post_id" => 3
"comment_id" => 3
]
]
1 => array:4 [▼
"id" => 4
"info" => "这谁不知道啊!"
"user_id" => 10084
"comment" => array:3 [▼
"id" => 4
"post_id" => 3
"comment_id" => 4
]
]
]
执行的SQL(使用with)
1
2
3
4
5
6
7
8
9
10
11
12
13
# 两次sql是一样的
array:2 [▼
0 => array:3 [▼
"query" => "select * from `comment` where exists (select * from `post_comment` where `comment`.`id` = `post_comment`.`comment_id` and `post_id` = ?)"
"bindings" => array:1 [▶]
"time" => 12.67
]
1 => array:3 [▼
"query" => "select * from `post_comment` where `post_comment`.`comment_id` in (3, 4)"
"bindings" => []
"time" => 0.21
]
]

 我们分析一下laravel底层的处理逻辑,从上边的sql可以看出使用模型关联第一步会使用一个嵌套查询找出comment在post_comment里存在的结果集,最终的得到的结果是3、4。随后会做一个IN查询得到最终comment.id=3、4的数据最后合并成结果集返回。

查询写法使用(不使用with)
1
2
3
4
5
6
7
8
9
public function list()
{
$postId = 3;

$data = \App\Models\Comment::whereHas("comment", function ($query) use ($postId) {
return $query->where("post_id", $postId);
})->get();
dump($data->toArray());
}
执行的SQL(不使用with)
1
2
3
4
5
6
7
8
# 两次执行sql也是一样的
array:1 [▼
0 => array:3 [▼
"query" => "select * from `comment` where exists (select * from `post_comment` where `comment`.`id` = `post_comment`.`comment_id` and `post_id` = ?)"
"bindings" => array:1 [▶]
"time" => 2.82
]
]
查询的结果(不使用with)
1
2
3
4
5
6
7
8
9
10
11
12
13
# 得到的结果也是一样的
array:2 [▼
0 => array:3 [▼
"id" => 3
"info" => "\t我知道什么是多对多"
"user_id" => 10084
]
1 => array:3 [▼
"id" => 4
"info" => "这谁不知道啊!"
"user_id" => 10084
]
]

 两种写法的端异想必大家都能看得出来,就不再介绍了。

那么问题来了,为什么要使用模型关联呢?

 假设某一个文章被评论了,在不使用模型关联的情况下需要这样写:

普通写法
1
2
3
4
5
6
7
8
9
$mode = new \App\Models\Comment();
$mode->info = '测试';
$mode->user_id = 11;
$mode->save();

\App\Models\PostComment::create([
'post_id' => 3,
'comment_id' => $mode->id
]);
模型关联写法
1
2
3
4
5
6
7
8
$mode = new \App\Models\Comment();
$mode->info = '测试';
$mode->user_id = 11;
$mode->save();

$mode->comment()->create([
'post_id' => 3,
]);

 而且我的公司有规定不能使用left join写法,而且模型中不允许写有关业务的代码,这样大大限制了随心所欲开发,降低了项目的维护成本。并且在使用模型关联后,一切的代码变得简单了,可读性提高,倒是性能这块我没有真正的测试过,之后肯定会研究一下。


 评论