laravel 5.5 -- Eloquent 模型关联
2017-08-03 20:57
766 查看
一对一
正向关联
一个User模型关联一个
Phone模型。
# class User extends Model public function phone() { return $this->hasOne('App\Phone'); } public function phone() { # 它会自动假设 Phone 模型拥有 user_id 外键。如果你想要重写这个约定,则可以传入第二个参数到 hasOne 方法里。 return $this->hasOne('App\Phone', 'foreign_key'); } public function phone() { # Eloquent 假设外键会和上层模型的 id 字段(或者自定义的 $primaryKey)的值相匹配。否则,请 return $this->hasOne('App\Phone', 'foreign_key', 'local_key'); } # 允许定义默认模型 # 使用 $phone = User::find(1)->phone; # 当找不到 id=1 的 phone 记录,会查看是否有默认模型,没有则返回 null。 // select * from `by_users` where `by_users`.`id` = '1' limit 1 // select * from `by_phones` where `by_phones`.`user_id` = '1' and `by_phones`.`user_id` is not null limit 1 $email = App\Phone::where('content', '13334233434')->first()->user->email; // select * from `by_phones` where `content` = '13334233434' limit 1 // select * from `by_users` where `by_users`.`id` = '1' limit 1
反向关联
# class Phone extends Model public function user() { return $this->belongsTo('App\User'); // return $this->belongsTo('App\User', 'foreign_key'); // return $this->belongsTo('App\User', 'foreign_key', 'other_key'); } // 使用:同正向关联
关联默认模型
belongsTo,haoOne允许定义默认模型
public function user() { return $this->belongsTo('App\User')->withDefault(); // 空模型 # 或者 return $this->belongsTo('App\User')->withDefault([ 'name' => '游客', ]); # 或者 return $this->belongsTo('App\User')->withDefault(function ($user) { $user->name = '游客'; }); }
一对多
正向关联
一篇博客文章可能会有无限多个评论。# class Post extends Model public function comments() { return $this->hasMany('App\Comment'); # return $this->hasMany('App\Comment', 'foreign_key'); # return $this->hasMany('App\Comment', 'foreign_key', 'local_key'); } # Controller $comments = App\Post::find(1)->comments; // 返回满足条件的评论的集合 // select * from `by_posts` where `by_posts`.`id` = '1' limit 1 // select * from `by_comments` where `by_comments`.`post_id` = '1' and `by_comments`.`post_id` is not null foreach ($comments as $comment) { // } $comments = App\Post::find(1)->comments()->where('title', 'foo')->first();
反向关联
// 同一对一的反向关联 :语义化为 ( 一条评论只属于一篇文章 ) return $this->belongsTo('App\Post');
多对多
正向关联
一个用户可能拥有多种身份,而一种身份能同时被多个用户拥有。需要使用三个数据表:
users、
roles和
role_user。
role_user表命名是以相关联的两个模型数据表来依照字母顺序命名,并包含了
user_id和
role_id字段。
# class User extends Model public function roles() { return $this->belongsToMany('App\Role'); } // 使用:等同于 hasMany $roles = App\User::find(1)->roles; // select * from `by_users` where `by_users`.`id` = '1' limit 1 /* select `by_roles`.*, `by_role_user`.`user_id` as `p 4000 ivot_user_id`, `by_role_user`.`role_id` as `pivot_role_id` from `by_roles` inner join `by_role_user` on `by_roles`.`id` = `by_role_user`.`role_id` where `by_role_user`.`user_id` = '1' */
如前文提到那样,Eloquent 会合并两个关联模型的名称并依照字母顺序命名。当然你也可以随意重写这个约定。可通过传递第二个参数至
belongsToMany方法来实现:
return $this->belongsToMany('App\Role', 'role_user'); return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
反向关联
<?php # class Role extends Model public function users() { return $this->belongsToMany('App\User'); }
操作中间表
获取中间表字段$user = App\User::find(1); foreach ($user->roles as $role) { echo $role->pivot->created_at; } # 我们取出的每个 Role 模型对象,都会被自动赋予 pivot 属性。
# 默认情况下,pivot 对象只提供模型的键。如果你的 pivot 数据表包含了其它的属性,则可以在定义关联方法时指定那些字段: return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
# 如果你想要中间表自动维护 created_at 和 updated_at 时间戳,可在定义关联方法时加上 withTimestamps 方法: return $this->belongsToMany('App\Role')->withTimestamps();
自定义中间表模型
public function users() { return $this>belongsToMany('App\User')->using('App\UserRole'); }
namespace App; use Illuminate\Database\Eloquent\Relations\Pivot; class UserRole extends Pivot { // }
使用中间表来过滤关联数据
你可以使用
wherePivot和
wherePivotIn来增加中间件表过滤条件:
return $this->belongsToMany('App\Role')->wherePivot('approved', 1); return $this->belongsToMany('App\Role')->wherePivotIn('priority', [1, 2]);
重命名 pivot
你可以使用
wherePivot和
wherePivotIn来增加中间件表过滤条件:
return $this->belongsToMany('App\Podcast') ->as('subscription') ->withTimestamps(); // 使用 $users = User::with('podcasts')->get(); foreach ($users->flatMap->podcasts as $podcast) { echo $podcast->subscription->created_at; }
远层一对多
一个Country模型可能通过中间的
Users模型关联到多个
Posts模型。
countries id - integer name - string users id - integer country_id - integer name - string posts id - integer user_id - integer title - string
class Country extends Model { public function posts() { return $this->hasManyThrough( 'App\Post', 'App\User', // 'country_id', // Foreign key on users table... // 'user_id', // Foreign key on posts table... // 'id', // Local key on countries table... // 'id' // Local key on users table... ); } }
$posts = App\Country::find(1)->posts; // select * from `by_countries` where `by_countries`.`id` = '1' limit 1 // select `by_posts`.*, `by_users`.`country_id` from `by_posts` inner join `by_users` on `by_users`.`id` = `by_posts`.`user_id` where `by_users`.`country_id` = '1'
多态关联
用户可以「评论」文章和视频。数据库设计
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 // posts or videos
namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model { /** * 获取所有拥有的 commentable 模型。 */ 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 = App\Post::find(1); foreach ($post->comments as $comment) { // }
$comment = App\Comment::find(1); $commentable = $comment->commentable; # 返回 Post 或 Video 实例
自定义多态关联的类型字段
use Illuminate\Database\Eloquent\Relations\Relation; Relation::morphMap([ 'posts' => App\Post::class, 'videos' => App\Video::class, ]); # 你需要在你自己的 AppServiceProvider 中的 boot 函数注册这个 morphMap ,或者创建一个独立且满足你要求的服务提供者。
多态多对多关联
数据库设计
博客的Post和
Video模型可以共用多态关联至
Tag模型。post 有多个标签,一个标签有多个 post 。
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
正向关联
namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model{ /** * 获取该文章的所有标签。 */ public function tags(){ return $this->morphToMany('App\Tag', 'taggable'); } }
反向关联
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) { // }
$tag = App\Tag::find(1); foreach ($tag->videos as $video) { // }
查找关联
查找关联是否存在// 获取那些至少拥有一条评论的文章... $posts = App\Post::has('comments')->get(); // select * from `by_posts` where exists (select * from `by_comments` where `by_posts`.`id` = `by_comments`.`post_id`) $posts = App\Post::doesntHave('comments')->get(); // select * from `by_posts` where not exists (select * from `by_comments` where `by_posts`.`id` = `by_comments`.`post_id`)
// 获取所有至少有三条评论的文章... $posts = Post::has('comments', '>=', 3)->get(); // select * from `by_posts` where (select count(*) from `by_comments` where `by_posts`.`id` = `by_comments`.`post_id`) > 4
// 获取所有至少有一张卡的手机的用户... $posts = User::has('phones.cards')->get(); // select * from `by_users` where exists (select * from `by_phones` where `by_users`.`id` = `by_phones`.`user_id` and exists (select * from `by_cards` where `by_phones`.`id` = `by_cards`.`phone_id`))
# 如果你想要更高级的用法,则可以使用 whereHas 和 orWhereHas 方法,在 has 查找里设置「where」条件。此方法可以让你增加自定义条件至关联条件中,例如对评论内容进行检查: // 获取那些至少有一条评论包含 foo 的文章 $posts = Post::whereHas('comments', function ($query) { $query->where('content', 'like', 'foo%'); })->get(); // select * from `by_posts` where exists (select * from `by_comments` where `by_posts`.`id` = `by_comments`.`post_id` and `content` like 'foo%') $posts = Post::whereDoesntHave('comments', function ($query) { $query->where('content', 'like', 'foo%'); })->get();
关联数据计数
# 如果你想对关联数据进行计数,请使用 withCount 方法,此方法会在你的结果集中增加一个 {relation}_count 字段 $posts = App\Post::withCount('comments')->get(); foreach ($posts as $post) { echo $post->comments_count; }
$posts = Post::withCount(['votes', 'comments' => function ($query) { $query->where('content', 'like', 'foo%'); }])->get(); echo $posts[0]->votes_count; echo $posts[0]->comments_count;
$posts = Post::withCount([ 'comments', 'comments as pending_comments_count' => function ($query) { $query->where('approved', false); } ])->get(); echo $posts[0]->comments_count; echo $posts[0]->pending_comments_count;
预加载
$books = App\Book::all(); foreach ($books as $book) { echo $book->author->name; } // 若存在着 25 本书,则循环就会执行 26 次查找:1 次是查找所有书籍,其它 25 次则是在查找每本书的作者。
$books = App\Book::with('author')->get(); foreach ($books as $book) { echo $book->author->name; } // 对于该操作则只会执行两条 SQL 语句: # select * from books # select * from authors where id in (1, 2, 3, 4, 5, ...)
$books = App\Book::with('author', 'publisher')->get();
$books = App\Book::with('author.contacts')->get(); # 预加载所有书籍的作者,及所有作者的个人联系方式
$users = App\Book::with('author:id,name')->get(); # 这个方法,应该总是包含 id 字段
预加载条件限制
$users = App\User::with(['posts' => function ($query) { $query->where('title', 'like', '%first%'); }])->get(); # 在这个例子里,Eloquent 只会预加载标题包含 first 的文章
$users = App\User::with(['posts' => function ($query) { $query->orderBy('created_at', 'desc'); }])->get();
延迟预加载
有时你可能需要在上层模型被获取后才预加载关联。
$books = App\Book::all(); if ($someCondition) { $books->load('author', 'publisher'); }
$books->load(['author' => function ($query) { $query->orderBy('published_date', 'asc'); }]);
public function format(Book $book) { $book->loadMissing('author'); // 当没有加载时候,加载 return [ 'name' => $book->name, 'author' => $book->author->name ]; }
插入 & 更新关联模型
save 方法$comment = new App\Comment(['message' => 'A new comment.']); $post = App\Post::find(1); $post->comments()->save($comment);
$post = App\Post::find(1); $post->comments()->saveMany([ new App\Comment(['message' => 'A new comment.']), new App\Comment(['message' => 'Another comment.']), ]);
create 方法
$post = App\Post::find(1); $comment = $post->comments()->create([ 'message' => 'A new comment.', ]); // create 多个 $comment = $post->comments()->create( [ 'message' => 'A new comment.', ],[ 'message' => 'Another new comment.', ] );
save允许传入一个完整的 Eloquent 模型实例,但
create只允许传入原始的 PHP 数组。
你需要先在你的模型上定义一个
fillable或
guarded属性,因为所有的 Eloquent 模型都针对批量赋值(Mass-Assignment)做了保护。
更新「从属」关联
$account = App\Account::find(10); $user->account()->associate($account); $user->save();
删除一个
belongsTo关联时,使用
dissociate方法会置该关联的外键为空 (null) 。
$user->account()->dissociate(); $user->save();
多对多关联
一个用户可以拥有多个身份,且每个身份都可以被多个用户拥有。
附加一个规则至一个用户,并连接模型以及将记录写入至中间表,则可以使用
attach方法:
$user = App\User::find(1); $user->roles()->attach($roleId);
也可以传递一个需被写入至中间表的额外数据数组:
$user->roles()->attach($roleId, ['expires' => $expires]);
// 移除用户身上某一身份... $user->roles()->detach($roleId); // 移除用户身上所有身份... $user->roles()->detach();
# 为了方便,attach 与 detach 都允许传入 ID 数组: $user = App\User::find(1); $user->roles()->detach([1, 2, 3]); $user->roles()->attach([1 => ['expires' => $expires], 2, 3]);
更新关联
# sync 方法可以用数组形式的 IDs 插入中间的数据表。任何一个不存在于给定数组的 IDs 将会在中间表内被删除。操作完成之后,只有那些在给定数组内的 IDs 会被保留在中间表中。 $user->roles()->sync([1, 2, 3]);
$user->roles()->sync([1 => ['expires' => true], 2, 3]);
如果你不想删除现有的 IDs ,你可以
syncWithoutDetaching方法:
$user->roles()->syncWithoutDetaching([1, 2, 3]);
切换关联
// 存在就删除,不存在就创建 $user->roles()->toggle([1, 2, 3]);
在中间表上保存额外数据
App\User::find(1)->roles()->save($role, ['expires' => $expires]);
更新中间表记录
# 这个方法接收中间记录的外键和属性数组进行更新 $user = App\User::find(1); $user->roles()->updateExistingPivot($roleId, $attributes);
连动父级时间戳
当一个模型belongsTo或
belongsToMany另一个模型时,像是一个
Comment属于一个
Post。这对于子级模型被更新时,要更新父级的时间戳相当有帮助。举例来说,当一个
Comment模型被更新时,你可能想要「连动」更新
Post所属的
updated_at时间戳。
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model{ /** * 所有的关联将会被连动。 * * @var array */ protected $touches = ['post']; /** * 获取拥有此评论的文章。 */ public function post(){ return $this->belongsTo('App\Post'); } }
$comment = App\Comment::find(1); $comment->text = 'Edit to this comment!'; $comment->save();
相关文章推荐
- 深入理解 Laravel Eloquent(三)——模型间关系(关联)
- 深入理解 Laravel Eloquent(三)——模型间关系(关联)
- laravel eloquent 模型关联
- 深入理解 Laravel Eloquent(三)——模型间关系(关联)
- 深入理解 Laravel Eloquent(三)——模型间关系(关联) 在本篇文章中,我将跟大家一起学习 Eloquent 中最复杂也是最难理解的部分——模型间关系。官方英文文档中叫 Relatio
- 深入理解 Laravel Eloquent(三)——模型间关系(关联)
- laravel 5.5 -- Eloquent 入门
- laravel 之 Eloquent 模型修改器和序列化
- Laravel Eloquent模型分组查询并返回每个分组的数量 groupBy()
- Laravel之Eloquent ORM关联
- 每天laravel[035]-laravel 基础知识 --- Eloquent 模型之关系映射
- laravel的Eloquent模型
- Laravel 模型关联attach,save,sync方法参数类型验证
- laravel学习教程之关联模型
- PHP laravel之模型&Eloquent
- Laravel 5.1 文档攻略 —— Eloquent:模型对象序列化
- Laravel 关联表模型和多对多关系
- Laravel关联模型中过滤结果为空的结果集(has和with区别)
- 使用laravel的Eloquent模型获取数据库的指定列
- Laravel基础-Eloquent ORM 模型