Eloquent ORM - 多型态关联

通常关联都是两两张资料表之间的关系,而多型态关联则是打破这个限制让一张表可以同时关连到两张以上的资料表。

以官方的范例为例,假设我们有 user 跟 post 两种资料,而他们各自有所属的 image 资料表。

像这种情况,如果对於 image 的资料结构、逻辑处理在两边都相差无几,就可以考虑应用多型态关联,将表单合成一张。

可以看到 images 资料表有两个用於参照的栏位

  • imageable_type : 指定是关联 users 资料表还是 posts 资料表
  • imageable_id : 关联的主键值

这麽做的好处是可以合一管理 image 的处理逻辑,像是 CRUD 的操作,反过来说就是耦合提高了,变动逻辑的时候要十分小心会不会影响到某一边的运作。所以在应用多型态关联的时候一定要考虑清楚合并到一块的模型是否真的能够共用逻辑,未来会不会有很多的改动。

一对一关联

多型态关联的应用情境相对少,这边就直接借用官方的范例做说明了。

以上面 users , posts , images 的架构,对应的 Eloquent Model 会长这样

<?php

namespace App\Models;

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(Image::class, 'imageable');
    }
}

class User extends Model
{

    public function image()
    {
        return $this->morphOne(Image::class, 'imageable');
    }
}

可以看到新出现的 morphOne,morphTo 方法,可以想成是 hasOne , belongsTo 的多型态版本。

morphOne

morphOne 跟 hasOne 相比可以看到在目标类别之後多了一个 name 参数 imageable。

Eloquent 会根据这个 name 预设目标表单上的查询栏位,以 imageable 为例的话就是查询 imageable_type 跟 imageable_id 。

也可以自订目标的查询栏位名称

// morphOne(目标表单名称,多形名称,目标的型别栏位名称,目标的外键栏位名称,自己的关联键)
 $this->morphOne(Image::class, 'imageable','imageable_type','imageable_id','id');

imageable_id 会用於储存关联的 id 值,就不用多介绍了,至於 imageable_type 则会储存 User 跟 Post 的类别名称,具体来说就是 'App\Models\User' 跟 'App\Models\Post' 这两种字串。

morphTo

使用 morphTo 要注意函式的名称。

public function imageable()
{
    return $this->morphTo();
}

如果没有带入参数的话预设会用函式的名称产出预设名称,像这里的函式名称是 imageable ,那查询时就会以型别栏位 imageable_type 查对应的资料表,以及键值栏位 imageable_id 查资料。

可以自订用来产生预设名称的 name

$this->morphTo('imageable');

或是乾脆自订查询栏位的名称

// morphOne(多形名称,型别栏位名称,外键栏位名称)
$this->morphTo('imageable','imageable_type','imageable_id');

关联定义好之後使用方法就跟普通的 hasOne , belongsTo 相同了。

一对多关联

morphMany

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Image extends Model
{
    public function imageable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{

    public function image()
    {
        return $this->morphMany(Image::class, 'imageable');
    }
}

class User extends Model
{

    public function image()
    {
        return $this->morphMany(Image::class, 'imageable');
    }
}

跟一对一的关联差不多,只差在 morphOne 改成 morphMany ,查询出来的资料不是一笔而是一组。

多对多关联

将官方的范例画成图比较好理解

跟普通的多对多关联相比,中介表单成了多型态的样子。

morphToMany

多型的一方,以 post 为例,会用 morphToMany 方法。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * Get all of the tags for the post.
     */
    public function tags()
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }
}

morphToMany 就想成是 belongsToMany 的多型态版,跟 morphMany 一样要带上多型的名称参数才能查资料。

// morphOne(目标表单名称,多形名称)
 $this->morphToMany(Tag::class, 'taggable');

又像 belongsToMany 一样要自订名称的话会变成超长一串。

// morphToMany(目标表单名称,多形名称,中介表单名称,中介表单上参照自己的外键,中介表单上参照目标的外键,目标的关联键,自己的关联键)
$this->morphToMany(Tag::class, 'taggable','taggables','taggable_id','tag_id','id','id');

morphedByMany

反向的关联就用 morphedByMany 方法。一样要带上多型的名称。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
    /**
     * Get all of the posts that are assigned this tag.
     */
    public function posts()
    {
        return $this->morphedByMany(Post::class, 'taggable');
    }

    /**
     * Get all of the videos that are assigned this tag.
     */
    public function videos()
    {
        return $this->morphedByMany(Video::class, 'taggable');
    }
}

自订也是超长一串

// morphedByMany(目标表单名称,多形名称,中介表单名称,中介表单上参照目标的外键,中介表单上参照自己的外键,自己的关联键,目标的关联键)
$this->morphedByMany(Post::class, 'taggable','taggables','taggable_id','tag_id','id','id');

<<:  端点防护软件 - 其他注意事项(除旧布新)

>>:  【Day 21】卷积神经网路(Convolutional Neural Network, CNN)(上)

{DAY 29} Seaborn

前言 Seaborn是比matplotlib功能更强大的绘图套件 是建立在matplotlib的基础...

RelativeLayout - 3

今天把这几天的东西都和起来吧 <?xml version="1.0" en...

我想成为架构师-前言(屁话篇~慎入!!!)

此篇将会是一个长篇大论废话篇,也是对我自己目标的一个定义,目前进入软件业到现今应该只有差不多一年半左...

Array of arrays

Recursion + Loop let array = [[["c",[&qu...

Day 25-Unit Test 应用於 Async Code-1 (情境及应用-5)

Unit Test 应用於 Async Code-1 - 前言 今天的文章内容是参考於 Testin...