Laravel6 全套入门实战Laravel 关联关系
课程内容
介绍
Laravel 内置了很多的关联,这样的话,我们可以很方便利用关联来完成联表的操作。增加程序的可读性,保持代码的优雅。
- 一对一
- 一对多
- 多对多
- 远程一对一
- 远程一对多
- 一对一 ( 多态 )
- 一对多(多态)
- 多对多(多态)
安装调试工具
composer require barryvdh/laravel-debugbar
一对一 hasOne,
一个对一个,比如一个人对应一个身份证
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 获取与用户关联的电话记录。
*/
public function phone()
{
return $this->hasOne('App\Phone');
}
}
调用
$phone = User::find(1)->phone;
反过来,身份证找到这个人,我们可以使用与 hasOne
方法对应的 belongsTo
方法来定义反向关联:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Phone extends Model
{
/**
* 获得拥有此电话的用户。
*/
public function user()
{
return $this->belongsTo('App\User');
}
}
以上都可以通过第二个参数配置键的对应关系
一对多 hasMany
一个人拥有多个评论
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* 获取博客文章的评论
*/
public function comments()
{
return $this->hasMany('App\Comment');
}
}
select * from `comments` where `comments`.`user_id` = 1 and `comments`.`user_id` is not null
反向关联belongsTo,通过评论获得所属文章的关联关系
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* 获取此评论所属文章
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}
多对多
通过中间表来找到对应关系。
需要三个数据库表: users
,roles
和 role_user
。role_user
表的命名是由关联的两个模型按照字母顺序来的,并且包含了 user_id
和 role_id
字段
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 用户拥有的角色
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
获取
$user = App\User::find(1);
foreach ($user->roles as $role) {
//
}
也可以使用模型一样
$roles = App\User::find(1)->roles()->orderBy('name')->get();
select `roles`.*, `role_users`.`user_id` as `pivot_user_id`, `role_users`.`role_id` as `pivot_role_id` from `roles` inner join `role_users` on `roles`.`id` = `role_users`.`role_id` where `role_users`.`user_id` = 1
反向关联 belongsToMany
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
/**
* 拥有此角色的用户
*/
public function users()
{
return $this->belongsToMany('App\User');
}
}
select `users`.*, `role_users`.`role_id` as `pivot_role_id`, `role_users`.`user_id` as `pivot_user_id` from `users` inner join `role_users` on `users`.`id` = `role_users`.`user_id` where `role_users`.`role_id` = 1
远程一对一关系
通过一个表关联关系,去找另外一个表关系
users
id - integer
supplier_id - integer
suppliers
id - integer
history
id - integer
user_id - integer
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Supplier extends Model
{
/**
* 供应商通过user 表关联找到用户的历史记录。
*/
public function userHistory()
{
return $this->hasOneThrough('App\History', 'App\User');
}
}
第三个参数表示中间模型的外键名,第四个参数表示最终模型的外键名 ,第五个参数表示本地键名
远程一对多关联 hasManyThrough
一个 Country
模型可以通过中间的 User
模型获得多个 Post
模型。在这个例子中,你可以轻易地收集给定国家的所有博客文章
countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
/**
* 当前国家所有文章。
*/
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User');
}
}
- 第三个参数表示中间模型的外键名,
- 第四个参数表示最终模型的外键名。
- 第五个参数表示本地键名,
- 第六个参数表示中间模型的本地键名
一对一 ( 多态 )
博客 Post
和 User
可能共享一个关联到 Image
模型的关系。使用一对一多态关联允许使用一个唯一图片列表同时用于博客文章和用户账户 .
posts
id - integer
name - string
users
id - integer
name - string
images
id - integer
url - string
imageable_id - integer
imageable_type - string
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Image extends Model
{
/**
* 获取拥有此图片的模型。
*/
public function imageable()
{
return $this->morphTo();
}
}
class Post extends Model
{
/**
* 获取文章图片。
*/
public function image()
{
return $this->morphOne('App\Image', 'imageable');
}
}
class User extends Model
{
/**
* 获取用户图片。
*/
public function image()
{
return $this->morphOne('App\Image', 'imageable');
}
}
一对多(多态)
posts
id - integer
title - string
body - text
videos
id - integer
title - string
url - string
comments
id - integer
body - text
commentable_id - integer
commentable_type - string
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* 获取拥有此评论的模型。
*/
public function commentable()
{
return $this->morphTo();
}
}
class Post extends Model
{
/**
* 获取此文章的所有评论。
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
class Video extends Model
{
/**
* 获取此视频的所有评论。
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
多对多(多态)
博客 Post
和 Video
模型能够共享关联到 Tag
模型的多态关系。使用多对多多态关联允许使用一个唯一标签在博客文章和视频间共享
posts
id - integer
name - string
videos
id - integer
name - string
tags
id - integer
name - string
taggables
tag_id - integer
taggable_id - integer
taggable_type - string
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* 获取文章的所有标签。
*/
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
}
反向关联
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
/**
* 获取被打上此标签的所有文章。
*/
public function posts()
{
return $this->morphedByMany('App\Post', 'taggable');
}
/**
* 获取被打上此标签的所有视频。
*/
public function videos()
{
return $this->morphedByMany('App\Video', 'taggable');
}
}
$post = App\Post::find(1);
foreach ($post->tags as $tag) {
//
}
自定义多态类型
use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
'posts' => 'App\Post',
'videos' => 'App\Video',
]);
可以在 AppServiceProvider
的 boot
函数中注册 morphMap
,或者创建一个单独的服务提供者。
查询关联
$user = App\User::find(1);
$user->posts;
//增加查询
$user->posts()->where('active', 1)->get()
//获取至少存在一条评论的所有文章.
$posts = App\Post::has('comments')->get();
// 获取评论超过三条的文章...
$posts = App\Post::has('comments', '>=', 3)->get();
还可以用 「点」语法构造嵌套的 has
语句。比如,可以获取拥有至少一条评论和投票的文章:
// 获取拥有至少一条带有投票评论的文章...
$posts = App\Post::has('comments.votes')->get();
如果需要更多功能,可以使用 whereHas
和 orWhereHas
方法将「where」 条件放到 has
查询上。这些方法允许你向关联加入自定义约束,比如检查评论内容:
use Illuminate\Database\Eloquent\Builder;
// 获取至少带有一条评论内容包含 foo% 关键词的文章...
$posts = App\Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'foo%');
})->get();
// 获取至少带有十条评论内容包含 foo% 关键词的文章...
$posts = App\Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'foo%');
}, '>=', 10)->get();
为预加载添加约束
有时,可能希望预加载一个关联,同时为预加载查询添加额外查询条件,就像下面的例子:
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
在这个例子中, Eloquent 将仅预加载那些 title
列包含 first
关键词的文章。也可以调用其它的 [ 查询构造器 ] 方法进一步自定义预加载操作:
$users = App\User::with(['posts' => function ($query) {
$query->orderBy('created_at', 'desc');
}])->get();
{note} 在约束预加载时,不能使用
limit
和take
查询构造器方法。