記事広告で解決できることって?
記事広告で解決できることって?
2019.05.10
#49
バックエンドへの道

LaravelにおけるPolymorphic Relationshipについて

エリカ

こんにちは、ディレクターのエリカです。

Laravelでは Polymorphic Relationship についても簡単に利用できます。ひとつの関係で対象となるモデルを、複数のモデルと結びつけることができます。

たとえば、画像のライブラリのようなものを用意したとして、そこに登録された画像をブログ記事で利用したり、ユーザープロフィールで利用したりすることができるようになります。

それでは、この場合を例にして、利用方法をみていきます。いわゆる 多対多 の Polymorphic Relationship になります。

Polymorphic Relationship のテーブル定義

まず、必要なところだけを抜き出すと、それぞれのテーブル定義は以下のような形になるかと思います。

posts
    id - Integer
    title - String

users
    id - Integer
    name - String

images
    id - Integer
    url - String

これに対し、 Polymorphic Relationship で、関連づけるためのテーブルは以下のようになります。

imageables
    image_id - Integer
    imageable_id - Integer
    imageable_type - String

image_id には、imagesテーブルのidが格納されます。

imageable_id には、 posts もしくは users テーブルのidが格納されます。

imageable_type には、imageable_id が posts もしくは users どちらのテーブルの id かを示すモデル名が格納されます。

Polymorphic Relationship なモデル

それを実現するモデルはそれぞれ次のようになります。

class Post extends Model
{
    public function images()
    {
        return $this->morphToMany('App\Image', 'imageable');
    }
}

class User  extends Model
{
    public function images()
    {
        return $this->morphToMany('App\Image', 'imageable');
    }
}

class Image extends Model
{
    public function posts()
    {
        return $this->morphedByMany('App\Post', 'imageable');
    }
    
    public function users()
    {
        return $this->morphedByMany('App\User', 'imageable');
    }
}

これだけで、 Polymorphic Relationship が実現できます。

Polymorphic Relationship なデータの登録

データを登録してみます。

// 関連付けたい任意のImageのIDの準備
$images = [1,2]; 

// 任意のPostデータを準備
$post = Post::find(1);

// 任意のUserデータを準備
$user = User::find(1);

// 関連付け
$post->images()->sync($images);
$user->images()->sync($images);

上記のようなコードを実行するだけ完了です。

このとき、 imageables テーブルには、以下のようなデータが保存されます。

imageables
| image_id | imageable_id | imageable_type |
| 1 | 1 | App\Post |
| 2 | 1 | App\Post |
| 1 | 1 | App\User |
| 2 | 1 | App\User |

ここで、 imageable_type については、デフォルトでモデル名が格納されます。アプリケーションの内部構造と分離したい場合は、別の値を指定することもできます。

Polymorphic Relationship における識別名の指定

サービスプロバイダで以下のように指定することで、Polymorphic Relationship な識別名の指定が可能です。

use Illuminate\Database\Eloquent\Relations\Relation;

Relation::morphMap([
    'posts' => 'App\Post',
    'users' => 'App\User',
]);

このようにすると、先ほどのデータ登録は下記のようになります。

imageables
| image_id | imageable_id | imageable_type |
| 1 | 1 | posts |
| 2 | 1 | posts |
| 1 | 1 | users |
| 2 | 1 | users |

Polymorphic Relationship なデータの呼び出し

登録された Polymorphic Relationship なデータ参照するのも簡単です。

下記のように、それぞれ関連づけられた Image のデータにアクセスできます。

$post = Post::find(1);
foreach($post->images() as $image) {
    // $image->url;
}

$user = User::find(1);
foreach($user->images() as $image) {
    // $image->url;
}

まとめ

今回は、多対多な Polymorphic Relationship で、画像を例にしましたが、タグや、レビュー機能など、複数のモデルが対象になるような関連付けの際にとても有用かと思います。

Laravelなら Polymorphic Relationship も洗練されていますね。