のぐそんブログ

暗いおじさんがシコシコ書くブログです。

Laravelでモデルのリレーションのメモ

モデルのリレーションとは、複数のテーブルを組み合わせて利用することです。
例えば以下のようなテーブルがあるとします。

◎messagesテーブル(従テーブル)

参照用の外部キーを持つ側を従テーブルといいます。
外部キーは主テーブル名の単数_idにする必要があります。
別の名前をつけたい場合は、リレーションの設定をする際に指定が必要です(後述)。

今回の例でいくと、テーブル名がusersなので、外部キーのカラム名user_idになります。

id message user_id
1 こんにちは 2
2 こんばんは 7

◎usersテーブル(主テーブル)

外部キーを持たいない参照される側のテーブルを主テーブルといいます。

id name mail age
2 佐藤一郎 sato@sample.com 30
7 鈴木次郎 jsuzuki@sample.com 40

messagesテーブルに、メッセージを書いたユーザーidを入れておくことで、ユーザーの詳細情報をusersテーブルから取得することができます。

ユーザーの関連したメッセージを表示してみる

usersテーブルと、messagesテーブルをリレーションして、ユーザーに関連したメッセージを表示してみます。

Controller

/app/Http/Controllers/UserController.php

<?php

namespace App\Http\Controllers;
use App\User;

use Illuminate\Http\Request;


class UserController extends Controller
{
    public function index(Request $request)
    {
        $items = User::all();
        return view('user.index', ['items' => $items]);
    }
}

Model

User

/app/User.php

外部キーを持たない、Userテーブル(主テーブル)に、従テーブルを取得する為の定義を追加します。

1対1の関連づけの場合はhasOneを利用します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $guarded = array('id');

    public function message(){
        return $this->hasOne('App\Message');
    }
}

以下のようなメソッドを追加します。
hasOneは引数に指定したモデルへの関連付けを設定し、関連あるモデルのインスタンスを取得できるようになります。

    public function message(){
        return $this->hasOne('App\Message');
    }

上のほうで、従テーブルの外部キーは主テーブル名の単数_idにする必要があると書きましたが、外部キーを指定することもできます。

ちょっとややこしいのですが、
第二引数に外部キーのカラム名(foreignKey)を指定し、第三引数にローカルキー(localKey)を指定します。これでusersテーブルのlocal_keymessagesテーブルのforeign_keyをリレーションします。

$this->hasOne('App\Message', 'foreign_key(外部キーのカラム名)', 'local_key(ローカルキーのカラム名)');

Message

/app/Message.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    protected $guarded = array('id');

    public function getData()
    {
        return $this->message;
    }
}

View

/resources/views/user/index.blade.php

Userモデルで定義したmessage()はプロパティとしてアクセスすることができます。
プロパティとしてアクセスした、messageにはMessageモデルのインスタンが入っているので、インスタンスのメソッドも自由に利用することができます。

<table>
    <tr>
        <th>name</th>
        <th>mail</th>
        <th>age</th>
        <th>message</th>
    </tr>
    @foreach ($items as $item)
        <tr>
            <td>{{$item->name}}</td>
            <td>{{$item->mail}}</td>
            <td>{{$item->age}}</td>
            <td>{{$item->message->getData()}}</td>
        </tr>
    @endforeach
</table>

表示

結果

name mail age message
佐藤一郎 sato@sample.com 30 こんにちは
鈴木次郎 jsuzuki@sample.com 40 こんばんは

hasMany

hasOneは1だけの情報を取得しました。
複数の関連情報がある場合はhasManyを利用します。

以下のような従テーブルがあるとします。

id message user_id
1 こんにちは 2
2 さようなら 2
3 こんばんは 7
4 はじめまして 7

Model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $guarded = array('id');

    public function messages(){
        return $this->hasMany('App\Message');
    }
}

view

<table>
    <tr>
        <th>name</th>
        <th>mail</th>
        <th>age</th>
        <th>message</th>
    </tr>
    @foreach ($items as $item)
        <tr>
            <td>{{$item->name}}</td>
            <td>{{$item->mail}}</td>
            <td>{{$item->age}}</td>
            <td>
                <ul>
                    @foreach($item->messages as $obj)
                        <li>{{$obj->getData()}}</li>
                    @endforeach
                </ul>
            </td>
        </tr>
    @endforeach
</table>

結果

name mail age message
佐藤一郎 sato@sample.com 30 こんにちは
さようなら
鈴木次郎 jsuzuki@sample.com 40 こんばんは
はじめまして

belongsTo

hasOneやhasManyは主テーブルから外部キー(user_id)が設定されている従テーブルの情報を取得していました。
belongsToは逆に従テーブルから主テーブルの情報を取得します。

belongsToは1つ(この場合は該当のuser情報)のデータを取得します。

Model

Message

belongsToでUserモデルと結合します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    protected $guarded = array('id');

    public function user(){
        return $this->belongsTo('App\User');
    }
}

User

getDataメソッドを用意します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $guarded = array('id');

    public function getData(){
        return $this->name;
    }
}

Controller

リレーションをとることで、userモデルのインスタンスを取得できるので、userモデルのgetDataメソッドを利用できます。

<table>
    <tr>
        <th>user_id</th>
        <th>message</th>
        <th>user</th>
    </tr>
    @foreach ($items as $item)
        <tr>
            <td>{{$item->user_id}}</td>
            <td>{{$item->message}}</td>
            <td>{{$item->user->getData()}}</td>
        </tr>
    @endforeach
</table>

表示

user_id message user
2 こんにちは 佐藤一郎
2 さようなら 佐藤一郎
7 こんばんは 鈴木次郎
7 はじめまして 鈴木次郎

その他

指定のリレーション値を持っているもののみ取得

モデル::has(リレーション名)->get();

class UserController extends Controller
{
    public function index(Request $request)
    {
        $items = User::has('message')->get();
        return view('user.index', ['items' => $items]);
    }
}

指定のリレーションの値をもたないもののみ取得

モデル::doesntHave(リレーション名)->get();

class UserController extends Controller
{
    public function index(Request $request)
    {
        $items = User::doesntHave('message')->get();
        return view('user.index', ['items' => $items]);
    }
}