laravel -- Eloquent 模型关联

来源:互联网 发布:免费qq软件下载 编辑:程序博客网 时间:2024/05/22 00:52

定义关联

一对一

一个 User 模型会关联一个 Phone 模型。

# class User extends Modelpublic function phone() {    return $this->hasOne('App\Phone');}
$phone = User::find(1)->phone;# 它会自动假设 Phone 模型拥有 user_id 外键。如果你想要重写这个约定,则可以传入第二个参数到 hasOne 方法里。return $this->hasOne('App\Phone', 'foreign_key');# Eloquent 假设外键会和上层模型的 id 字段(或者自定义的 $primaryKey)的值相匹配。否则,请return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
定义反向关联
<?php# class Phone extends Model public function user() {    return $this->belongsTo('App\User');   // 关联自动设置为 方法名 + _id}
public function user() {     return $this->belongsTo('App\User', 'foreign_key');}
public function user() {    return $this->belongsTo('App\User', 'foreign_key', 'other_key');}

belongsTo 关联允许定义默认模型

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');}
$comments = App\Post::find(1)->comments;foreach ($comments as $comment) {    //}
$comments = App\Post::find(1)->comments()->where('title', 'foo')->first();
return $this->hasMany('App\Comment', 'foreign_key');return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
定义反向关联
<?php# class Comment extends Model public function post() {    return $this->belongsTo('App\Post');}
$comment = App\Comment::find(1);echo $comment->post->title;
return $this->belongsTo('App\Post', 'foreign_key');return $this->belongsTo('App\Post', 'foreign_key', 'other_key');

多对多

一个用户可能拥有多种身份,而一种身份能同时被多个用户拥有。

需要使用三个数据表:usersrolesrole_userrole_user 表命名是以相关联的两个模型数据表来依照字母顺序命名,并包含了 user_idrole_id 字段。

<?php# class User extends Modelpublic 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();

如前文提到那样,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');}
<?phpnamespace App;use Illuminate\Database\Eloquent\Relations\Pivot;class UserRole extends Pivot{    //}
使用中间表来过滤关联数据

你可以使用 wherePivotwherePivotIn 来增加中间件表过滤条件:

return $this->belongsToMany('App\Role')->wherePivot('approved', 1);return $this->belongsToMany('App\Role')->wherePivotIn('priority', [1, 2]);

远层一对多

一个 Country 模型可能通过中间的 Users 模型关联到多个 Posts 模型。

countries    id - integer    name - stringusers    id - integer    country_id - integer    name - stringposts    id - integer    user_id - integer    title - string
<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Country extends Model {    /**     * 获取该国家的所有文章。     */    public function posts() {        return $this->hasManyThrough('App\Post', 'App\User');    }}
class Country extends Model{    public function posts()    {        return $this->hasManyThrough(            'App\Post', 'App\User',            'country_id', 'user_id', 'id'        );    }}
多态关联

用户可以「评论」文章和视频。

posts    id - integer    title - string    body - textvideos    id - integer    title - string    url - stringcomments    id - integer    body - text    commentable_id - integer    commentable_type - string   // posts or videos
<?phpnamespace 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 ,或者创建一个独立且满足你要求的服务提供者。
多态多对多关联

博客的 PostVideo 模型可以共用多态关联至 Tag 模型。post 有多个标签,一个标签有多个 post 。

posts    id - integer    name - stringvideos    id - integer    name - stringtags    id - integer    name - stringtaggables    tag_id - integer    taggable_id - integer    taggable_type - string
<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Post extends Model{    /**     * 获取该文章的所有标签。     */    public function tags(){        return $this->morphToMany('App\Tag', 'taggable');    }}/***********************************************/<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Video extends Model{    /**     * 获取该文章的所有标签。     */    public function tags(){        return $this->morphToMany('App\Tag', 'taggable');    }}
<?phpnamespace 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();
// 获取所有至少有三条评论的文章...$posts = Post::has('comments', '>=', 3)->get();
// 获取所有至少有一条评论被评分的文章...$posts = Post::has('comments.votes')->get();
# 如果你想要更高级的用法,则可以使用 whereHas 和 orWhereHas 方法,在 has 查找里设置「where」条件。此方法可以让你增加自定义条件至关联条件中,例如对评论内容进行检查://  获取那些至少有一条评论包含 foo 的文章$posts = Post::whereHas('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;

预加载

$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\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');}]);

插入 & 更新关联模型

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.',]);

save 允许传入一个完整的 Eloquent 模型实例,但 create 只允许传入原始的 PHP 数组。

你需要先在你的模型上定义一个 fillableguarded 属性,因为所有的 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]);
在中间表上保存额外数据
App\User::find(1)->roles()->save($role, ['expires' => $expires]);
更新中间表记录
# 这个方法接收中间记录的外键和属性数组进行更新$user = App\User::find(1);$user->roles()->updateExistingPivot($roleId, $attributes);

连动父级时间戳

当一个模型 belongsTobelongsToMany 另一个模型时,像是一个 Comment 属于一个 Post。这对于子级模型被更新时,要更新父级的时间戳相当有帮助。举例来说,当一个 Comment 模型被更新时,你可能想要「连动」更新 Post 所属的 updated_at 时间戳。

<?phpnamespace 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();
原创粉丝点击