Laravel

Laravelで多対多のリレーションをして保存、表示する方法【belongsToMany】

いつもご利用ありがとうございます。このブログは、広告費によって運営されています。

オススメ本
Web技術を勉強するなら、かなりオススメの雑誌です。毎月新しい発見があります。ついに最終号・・・、みなさん買いましょう!!
読んで損することはない名著。命名で悩むことが多い人はこの本がオススメです。

⇨ Laravel 記事の目次はこちら

この記事では、Laravel で多対多のリレーションをモデルに定義すること。多対多の中間テーブルにデータを保存する方法、多対多のデータを表示する方法について書いていきます。

リレーションとは?

データベースのテーブル間で関係性のあるデータのことを指します。

今回の例で言えば、user_id を保存しておけば、users テーブルから user データを参照することができるようなことです。

Laravel では、少しの定義でこれを簡単に実装することができます。

この記事の内容は公式ドキュメントの要約となります

検証環境

Laravel 6

テーブル設計

リレーション設計

posts テーブルと、tags テーブルがあって、posts に紐づく tags が複数あるケースの話になります。

簡単にいうと、「ハッシュタグを複数つけられるようにしたい」ってことです。

そういう時には、多対多のリレーションを使う必要があります。

多対多には中間テーブルが必要

中間テーブルというのは、今回の画像でいうところの、

post_tag テーブルに当たります。

中間テーブル名は、単数系_単数系じゃないとダメ

まず、中間テーブルとして紐づけたいテーブルとテーブルの単数系をアンダーバーでつなぐ必要があります。

posts と tags テーブルの中間テーブルなので、post_tag となります。

中間テーブル名は、a-z 順で早い方が左側にこないとダメ

a-z 順という書き方をしてしまいましたが、abcd 順という意味です。

今回は、posts の p が、tags の t より abcd 順で先に来るので、post_tag というテーブル名になります。

users と tags の中間テーブルなら、t の方が u より先に来るので、tag_user になります。

モデルに定義する

中間テーブル用のモデルは不要です。

その代わり、Posts テーブルのモデルと、Tags テーブルのモデルの2つに記述する必要があります。

モデル Post.php

    public function tags()
    {
        return $this->belongsToMany('App\Tag');
    }

モデル Tag.php

    public function posts()
    {
        return $this->belongsToMany('App\Post');
    }

App\Post のところはモデルがあるところを定義してあげてください(App\Models\Post の人もいると思います。)

リレーションしたデータを取り出す方法

Post の一覧に複数タグをリレーションさせるためには、with を使うと良いです。

Controller

use App\Post;

~~~~~~~~
    public function index()
    {
        $posts = Post::with('tags')->get();
        //dd($posts);
        return view('home');
    }

次は、表示です。

タグは複数あるはずなので foreach で回してあげます。

views

@foreach($posts as $post)
  {{ $post->title }}
  @foreach($post->tags as $tag)
    {{ $tag->name }}
  @endforeach
@endforeach

保存する方法

$post に対して、タグを紐づけるときの書き方です。

注意して欲しいのは、新しいタグを生成するのと、タグを紐づけるのは別であることです。

新しいタグを新規作成しながら、タグを紐づける

新しいタグを作るときは、普通に new して save すれば作ることができます。その、作った ID を attach すればタグを紐づけることができます。

    public function tag(Request $request)
    {
        //新しいタグを生成する場合
        $tag = new Tag;
        $tag->name = $request->name;
        $tag->save();

        //新しいポストの作成(findして既存のpostに紐づけるでもOK)
        $post = new Post;
        $post->user_id = 1;
        $post->title = 'title';
        $post->content = 'content';
        $post->save();

        //タグを紐づける(引数にタグのIDさえ渡れば良い)
        //tags()はPostモデルで作ったfunctionの名前
        //新しくタグを生成していたら、こんな感じ。
        $post->tags()->attach($tag->id);
    }

既存のタグを、新規の Post に紐づける

    public function tag(Request $request)
    {
        //新しいタグを生成しない場合、tagのidだけrequestして送っても良いです。
        $tag_id = $request->tag_id;

        //新しいポストの作成(findして既存のpostに紐づけるでもOK)
        $post = new Post;
        $post->user_id = 1;
        $post->title = 'title';
        $post->content = 'content';
        $post->save();

        //既存のタグのIDだけ送ったパターン。
        $post->tags()->attach($tag_id);
    }

大事なのは、

  $post->attach($tag->id);

この部分です。

すでにモデルでリレーションを定義していれば、これだけの記述で中間テーブルにデータが作成されます。

中間テーブルの削除

attach と同様に、detach という記述で簡単に済みます。

//狙った1つのタグだけを外したい
$post->detach($tag_id);
//postに紐づく全ての中間テーブルを削除したい
$post->detach();

となります。

まとめ

以上です。

多対多のリレーションについて、僕なりにわかりやすくまとめた記事となります。

中間テーブルの設計など、ちょっとややこしいところだと思いますが、できるだけ簡潔に書いてみました。

感想や誤字などありましたら、TwitterDM からご連絡お願いします。

人気記事

PHP7.4 + Laravel6 のプロジェクトを AWS EC2 にデプロイする

関連記事

Laravel で1対多のリレーションをして保存、表示する方法【hasMany】

Laravel で多対1のリレーションをして保存、表示する方法【belongsTo】

Laravel で1対1のリレーションをする方法。【hasOne】

一番滑っている食洗機の記事