一对一
什么是一对多?
在程序角度讲:一对多基本上都是用来描述两个数据表之间的关系。
假设现有两个数据表:(1)文章表posts (2)评论表comment。一个文章可以有多个评论,但一个评论只能属于一个文章,相对于评论来说它与文章的关系是多对一,相对与文章来说它与评论的关系是一对多,这也就是laravel里反向关联的基础。
文章表posts
id | title | time | 1 | 什么是一对一? | 2019-11-07 |
2 | 什么是一对多? | 2019-11-08 |
3 | 什么是多对多? | 2019-11-06 |
评论表commentid | info | user_id | 1 | 我知道什么是一对一。 | 10086 |
2 | 我知道什么是一对多。 | 10085 |
3 | 我知道什么是多对多。 | 10084 |
4 | 这谁不知道啊! | 10084 |
中间表posts_comment(也可以叫做关系表)
id | post_id | comment_id | 1 | 1 | 1 |
2 | 2 | 2 |
3 | 3 | 3 |
4 | 3 | 4 |
从表的结构可以看出来,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写法,而且模型中不允许写有关业务的代码,这样大大限制了随心所欲开发,降低了项目的维护成本。并且在使用模型关联后,一切的代码变得简单了,可读性提高,倒是性能这块我没有真正的测试过,之后肯定会研究一下。