课程内容

介绍

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

多对多

通过中间表来找到对应关系。
需要三个数据库表: usersrolesrole_userrole_user 表的命名是由关联的两个模型按照字母顺序来的,并且包含了 user_idrole_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');
    }
}
  • 第三个参数表示中间模型的外键名,
  • 第四个参数表示最终模型的外键名。
  • 第五个参数表示本地键名,
  • 第六个参数表示中间模型的本地键名

一对一 (多态)

博客 PostUser 可能共享一个关联到 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');
        }
    }

多对多(多态)

博客 PostVideo 模型能够共享关联到 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',
    ]);

可以在 AppServiceProviderboot 函数中注册 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();

如果需要更多功能,可以使用 whereHasorWhereHas 方法将「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} 在约束预加载时,不能使用 limittake 查询构造器方法。

课件

https://github.com/kong-qi/laravel_study_video_course
下载课件:
https://github.com/kong-qi/laravel_study_video_course/releases

评论区 (0)

没有记录
支持 markdown,图片截图粘贴拖拽都可以自动上传。