Laravel学习笔记基础系列--(二十八)Laravel 模型间的多样化关联关系

作者: 温新

分类: 【Laravel】

阅读: 1858

时间: 2021-07-29 13:33:35

作者:温新

时间:2021-07-02

hi,我是温新,一名PHPer

关于多样化关联关系,我不知道如何来介绍,通过案例需求来说看吧。

多样化一对一关联关系

什么是多样化一对关联关系?

有这样一个需求,文章封面缩略图与用户头像共用一张缩略图,关于这个需求如何实现?现在就来看看多样化一对一是如何实现这个需求的。

  • 1)一个用户只能设置一个头像,因此用户与头像之间是一对一的关系;
  • 2)一篇文章只能有一个封面缩略图,因此文章与缩略图之间也是一对一的关系;
  • 3)可以看到,用户与图片之间存在关系,因此把图像独立出来,使用一个字段用户标识图像是谁的,这样就可以建立一个多样化的一对一关系出来。

已知现在有users表与posts表,现在还需要一个用户存放图片的images表。images表需要两个字段才能建立起多样化一对一关联关系。第一个字段是存放模型完整路径名称;第二个字段是用于存放用户ID或文章ID。

正向多样化一对一关联关系

准备工作

第一步:创建图像模型及迁移文件

php artisan make:model Image -m

第二步:编写迁移文件

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 0, 0)">Schema</span>::<span style="box-sizing: border-box;color: rgb(0, 0, 0)">create</span>(<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'images'</span>, <span style="box-sizing: border-box;color: rgb(119, 0, 136)">function</span> (<span style="box-sizing: border-box;color: rgb(0, 0, 0)">Blueprint</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170)">$table</span>) {</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(0, 85, 170)">$table</span><span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">increments</span>(<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'id'</span>);</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(0, 85, 170)">$table</span><span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">string</span>(<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'url'</span>)<span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">comment</span>(<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'图片URL'</span>);</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(0, 85, 170)">$table</span><span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">morphs</span>(<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'imageable'</span>);</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(0, 85, 170)">$table</span><span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">timestamps</span>();</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">});</span>

注意:$table->morphs('imageable');用于创建imageable_idimageable_type字段。

imageable_type字段用于存放模型,如app\Models\Post;

imageable_id字段用于存放对应的模型实例ID。对于本案例来讲,就是文章主键ID与用户主键ID。

第三步:执行迁移文件

php artisan migrate --path=/database/migrations/2021_07_02_145619_create_images_table.php

第四步:自行填充数据

如:我这里使用的数据是

正向一对一关联关系的使用

方法:morphTo($name = null, $type = null, $id = null, $ownerKey = null)

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 0, 0)">参数一:关联关系方法名。本例关联关系方法名是</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">imageable</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 0, 0)">参数二、三需要配合</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">参数一</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">用于构建关联字段;本例中参数二是imageable_type,参数三是imageable_id</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 0, 0)">参数四:当前模型主键ID</span></span>

案例:通过图片查询用户或文章信息

第一步:定义模型方法

文件:app/Models/Image.php

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(119, 0, 136)">public</span> <span style="box-sizing: border-box;color: rgb(119, 0, 136)">function</span> <span style="box-sizing: border-box;color: rgb(0, 0, 255)">imageable</span>()</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(119, 0, 136)">return</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170)">$this</span><span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">morphTo</span>(<span style="box-sizing: border-box;color: rgb(34, 17, 153)">__FUNCTION__</span>, <span style="box-sizing: border-box;color: rgb(170, 17, 17)">'imageable_type'</span>, <span style="box-sizing: border-box;color: rgb(170, 17, 17)">'imageable_id'</span>,<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'id'</span>);</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">}</span>

第二步:控制器调用

文件:DemoController.php

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(170, 85, 0)">// 通过Image实例获取其归属模型实例</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 85, 170)">$image</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26)">=</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">Image</span>::<span style="box-sizing: border-box;color: rgb(0, 0, 0)">find</span>(<span style="box-sizing: border-box;color: rgb(17, 102, 68)">2</span>)<span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">imageable</span>;</span>

此时返回的就是App\Models\Post实例,若find(1)返回的则是App\Models\User实例。

相对多样化一对一关联关系

方法:morphOne($related, $name, $type = null, $id = null, $localKey = null)

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 0, 0)">参数一:相关联的模型类。这里是Image模型;</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 0, 0)">参数二:关联关系方法名。这里是image;</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 0, 0)">参数三、四需要配置</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">参数二</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">用于构建关联字段。本例中参数三为imageable_type,参数四为</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">imageable_id</span>;</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 0, 0)">参数五:当前模型主键ID</span></span>

在实际开发,最为常见的是获取用户头像或谋篇文章的缩略图。

第一步:定义模型方法

文件:app/Models/User.php

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(170, 85, 0)">// 相对多样化一对一</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(119, 0, 136)">public</span> <span style="box-sizing: border-box;color: rgb(119, 0, 136)">function</span> <span style="box-sizing: border-box;color: rgb(0, 0, 255)">image</span>()</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">{</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(119, 0, 136)">return</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170)">$this</span><span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">morphOne</span>(<span style="box-sizing: border-box;color: rgb(0, 0, 0)">Image</span>::<span style="box-sizing: border-box;color: rgb(119, 0, 136)">class</span>, <span style="box-sizing: border-box;color: rgb(170, 17, 17)">'imageable'</span>,<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'imageable_type'</span>,<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'imageable_id'</span>,<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'id'</span>);</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">}</span>

文件:app\Models/Post.php

// 相对多样化一对一
public function image()
{
    return $this->morphOne(Image::class, 'imageable', 'imageable_type','imageable_id','id');
}

第二步:控制器调用

文件:DemoController.php中的demo方法

$imagePost = Post::find(1)->image;
$imageUser = User::find(1)->image;

多样化一对多关联关系

什么是多样化一对多关联关系

多样化一对多关联与普通的一对多关系是类似的,不同的是,目标模型可以在一个关联中从属于多个模型。

以博客系统为例,文章一般有两种,一是普通文章页面,二是一个单页页面。这两个页面都可以有评论。实际开发中不会建2张数据结构完全一样表来存储不同页面的评论内容。做法是,一张评论表中加上用于区分不同页面的评论作为标识,这样就可以区分是文章页面还是单独的独立页面。

准备工作

第一步:创建评论表模型及迁移文件

php artisan make:model Comment -m

第二步:编写迁移文件

public function up()
{
    Schema::create('comments', function (Blueprint $table) {
        $table->increments('id');
        $table->text('content')->comment('评论内容');
        $table->integer('user_id')->unsigned()->default(0);
        $table->morphs('commentable');
        $table->index('user_id');
        $table->softDeletes();
        $table->timestamps();
    });
}

第三步:创建独立页面模型及迁移文件

php artisan make:model Page -m

第四步:编写迁移文件

public function up()
{
    Schema::create('pages', function (Blueprint $table) {
        $table->increments('id');
        $table->string('title');
        $table->string('slug')->unique();
        $table->text('content');
        $table->integer('user_id')->unsigned()->default(0);
        $table->index('user_id');
        $table->timestamps();
    });
}

第五步:执行迁移文件

php artisan migrate --path=/database/migrations/2021_07_02_165923_create_comments_table.php
php artisan migrate --path=/database/migrations/2021_07_02_170056_create_pages_table.php

评论表的迁移文件中使用了morphs,这不是多样化一对一使用的方法吗?现在来分析下,一个评论只会对应一个页面(文章页/独立页面),我们需要使用morphs生成两个用于标识不同页面评论的字段。因此仍旧使用morphTo方法定义文页与独立页面的关联关系。

第六步:自行填充数据

以下我用的测试数据

comments

pages

多样化一对多关系

第一步:定义模型方法

文件:app/Models/Comment.php

public function commentable()
{
    return $this->morphTo(__FUNCTION__, 'commentable_type','commentable_id','id');
}

在评论模型中使用morphTo方法定义与Post模型和Page模型之间的多样化一对多关联。

第二步:控制器调用

// 获取某个单页的所有评论
$pages = Page::find(1)->comments;

相对多样化一对多关联关系

方法:morphMany($related, $name, $type = null, $id = null, $localKey = null)

参数含义与morphOne完全一致。

一般的情况下,都是使用文章来获取评论。假如现在是想通过评论来获取文章信息呢?此时需要在Post模型和Page模型中使用morphMany方法实现。

第一步:模型中定义方法

文件:Post模型

public function comments()
{
    return $this->morphMany(Comment::class,'commentable','commentable_type','commentable_id','id');
}

文件:Post模型

public function comments()
{
    return $this->morphMany(Comment::class,'commentable','commentable_type','commentable_id','id');
}

第二步:控制器调用

$postsComment = Post::with('comments')->find(1);

多样化多对多关联关系

什么是多样化多对多关联关系

与普通的多对多类似,不同的是多样化多对多关系的中间表存的不再是post_idtag_id,而是换成的模型名,用于标识标签是谁的。普通的对多对是ID进行关联,而多样化多对多则是与模型名进行关联。仍旧用到了一对一时用到的方法morphs

仍旧用文章与标签模型举例,当需要添加视频、音频等页面时,他们也是有标签的,他们表结构是一样的,因此完全可以共用一张标签表。面对多样化的内容,使用ID来存储是不够的。那么多样化多对多就来了,用于解决这样的问题。

准备工作

之前所建议的post_tag中间表,这里就用不上,这里就需要新建一个中间表来完成多样化多对多关系。

第一步:创建中间表

php artisan make:migration create_taggables_table --create=taggables

第二步:编辑迁移文件

Schema::create('taggables', function (Blueprint $table) {
    $table->increments('id');
    $table->integer('tag_id');
    $table->morphs('taggable');
    $table->index('tag_id');
    $table->unique(['tag_id', 'taggable_id', 'taggable_type']);
    $table->timestamps();
});

morphs方法用于生成标识标签归属模型

第三步:运行迁移文件

php artisan migrate --path=database/migrations/2021_07_03_062420_create_taggables_table.php

第四步:自行填充数据

如下数据是我使用的案例

多样化多对多关联

方法:morphedByMany($related, $name, $table = null, $foreignPivotKey = null,$relatedPivotKey = null,$parentKey = null, $relatedKey = null)

参数一:需要关联的模型类。如Post、Page模型;
参数二:关联的名称。需要和迁移文件时morphs方法指定的值一致,如taggable;
参数三:中间表名称。这里是 taggables 表;
参数四:当前模型类在中间表中的外键。这是 taggables表中的 tag_id;
参数五:中间表中的关联字段。这里是 taggables表中 taggable_id;
参数六:当前模型类的主键ID。如当前模型Tag中的id;
参数七:关联模型的主键ID。这里就是参数一模型中的主键。

第一步:Tag模型中定义多样化多对对方法

文件:app\Models\Tag.php

// 文章多样化多对多
public function manyPostTag()
{
    return $this->morphedByMany(Post::class,'taggable','taggables','tag_id','taggable_id','id','id');
}
// 单页多样化多对多
public function manyPageTag()
{
    return $this->morphedByMany(Page::class, 'taggable','taggables','tag_id','taggable_id','id','id');
}

第二步:控制器调用

// 通过标签来获取文章
$tags = Tag::with('manyPostTag','manyPageTag')->find(1);
$tags->manyPostTag;
$tags->manyPageTag;

相对多样化多对多关联

方法:morphToMany($related, $name, $table = null, $foreignPivotKey = null,$relatedPivotKey = null, $parentKey = null,$relatedKey = null, $inverse = false)

morphToMany方法的参数与morphedByMany的参数含义完全是一样的。不同的是参数一(需要关联的模型)与参数七(需要关联的模型的主键)不同。如这个案例是通过文件来获取标签,因此我们使用的Post模型需要与Tag模型进行关联,那么参数一就要换成Tag模型,若Tag模型的主键ID不是id,那么就需要修改参数七。

第一步:定义模型方法

文件:app\Models\Post.php

public function mangTags()
{
    return $this->morphToMany(Tag::class,'taggable','taggables','tag_id','taggable_id','id','id');
}

第二步:控制器调用

$postTag = Post::with('mangTags')->find(1);

到这里,多样化关联关系就记录完了。

更新完成时间:7月3日

关于多样化关联关系,我在实际开发中并没有用到。很多参数的含义已经忘记,得查看文档,算是当做一个复习了。如果参数不写全,时间久了,会是一脸懵逼。因此还是那句话,参数一定要写全。

顺便再说一句,我们习惯了使用id作为自增主键,而在实际开发中,不使用自增ID作为主键的情况非常多。因此,参数一定要写全。

我是温新

每天进步一点点,就一点点

请登录后再评论