Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
323
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Laravelでの基本的なリレーションシップもしくはJOIN

Laravelっぽくリレーションシップを使うのであればEloquentのRelationshipを使うことになるでしょう。
いくつかのパターンがありますが、ここでは利用頻度が高いOne To Many(hasManyとbelongsTo)を見てみます。

また、わざわざ定義をModelに書かなくても、普通にJOINも使えますのでそれも見てみます。

その他のパターンについては、本家サイトを見て下さい。

準備:テスト用のテーブル情報

まず、テスト用のテーブルを用意します。
ここでは、別の記事で利用したテーブルを利用して、テストをしてみたいと思います。create情報などもあるので、必要な人はどうぞ。

テストに利用するテーブルの情報は以下の通りです。

概要

  • 部署テーブル(depts)と社員テーブル(employees)が存在。
  • 社員テーブルでは所属部署をdept_idで管理(紐付け)している。

テーブルの構造と内容

部署テーブル(depts)

desc depts;

+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| dept_id   | int(11)     | NO   | PRI | NULL    |       |
| dept_name | varchar(32) | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+

このテーブルにはLaravelを利用する上で注意しなければならない点が2つります。
1つは、primary keyが"depts_id"となっていることです。Eloquentのリレーションシップは、各テーブルのキーが"id"であることを前提に動作しますので、その点についての対応が必要になります。
2つめとしては、timestampカラムがありませんので、その点についても対応が必要になります。

select * from detps;

+---------+-----------+
| dept_id | dept_name |
+---------+-----------+
|       1 | 営業部     |
|       2 | 経理部     |
|       3 | 技術部     |
|       4 | 法務部     |
+---------+-----------+

社員テーブル(employees)

desc employees;

+---------+-------------+------+-----+---------+----------------+
| Field   | Type        | Null | Key | Default | Extra          |
+---------+-------------+------+-----+---------+----------------+
| id      | int(11)     | NO   | PRI | NULL    | auto_increment |
| dept_id | int(11)     | YES  |     | NULL    |                |
| name    | varchar(32) | YES  |     | NULL    |                |
+---------+-------------+------+-----+---------+----------------+

select * from employees;

+----+---------+--------+
| id | dept_id | name   |
+----+---------+--------+
|  1 |       1 | 田中    |
|  2 |       2 | 玉木    |
|  3 |       3 | 鈴木    |
|  4 |       3 | 山本    |
|  5 |       2 | 斉藤    |
|  6 |       1 | 佐藤    |
|  7 |       1 | 小澤    |
|  8 |       2 | 関野    |
|  9 |       0 | 中村    |
+----+---------+--------+

Eloquentのテストようにはやや不適切ですが、どこにも所属していない中村さんがいます。

hasManyとbelongsTo

ではHasManyとbelongsToを見てみます。

利用目的

そもそもリレーションシップの利用目的はなんでしょうか?多くの場合、下記のようなものです。

  • ある部署に所属している社員一覧を得たい。
  • 社員一覧を照会した際、部署をidではなく日本語名で得たい。

要は結合です。
素のSQLでは正規化されたテーブルを結合するにはJOIN句を利用しますが、これをEloquentでより簡単にしたいということになります。

では、具体的にEloquentのリレーションシップの使い方を見てみます。

親テーブルと子テーブル?

技術上、2つのテーブルに親子関係はありませんが、利用上は部署テーブルが社員テーブルの親ということになるでしょう。つまり、部署の下に社員が存在しているという関係です。つまり、

  • 部署にはたくさんの社員がいる(部署 hasMany 社員の関係)
  • 社員はどこか1つの部署に所属している(社員 belongsTo 部署)

この関係をModelにて表現することで、リレーション機能を利用します。

モデルの定義

Eloquentでは、複数のテーブル名を単数にし、先頭文字を大文字にしたModelが、テーブルと紐付きます。ですので、ここではDeptモデルとEmployeeというモデルを用意します。

既存テーブルで複数になっていないものは、Model内のprotected $table = "tablename";で定義します。

モデルの雛形は、

php artisan make:model Dept

という感じでartisanで生成できます。

Dept

Dept hasMany Employeeの関係です。
通常、複数の社員情報が返る可能性があります。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Dept extends Model
{
    //timestamps利用しない
    public $timestamps = false;

    //primaryKeyの変更
    protected $primaryKey = "dept_id";

    //hasMany設定
    public function employees()
    {
        return $this->hasMany('App\Employee');
    }
}

紐付けに使うkeyはhasManyの第2、第3引数で制御することもできますが、$primaryKeyで設定するのが手間がないです。

Employee

Employee belongsTo Deptの関係ですね。
基本的に所属する部署1つの情報が返ります。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Employee extends Model
{
    //timestamps利用しない
    public $timestamps = false;

    //belongsTo設定
    public function dept()
    {
        return $this->belongsTo('App\Dept');
    }
}

これでModelの定義が終わりました。

使ってみる

では定義したModelを利用してみます。

部署に誰が所属しているか?を表示

Route::get('hasmany',function(){

    //id=1の部署を取得
    $dept = App\Dept::find(1);

    //所属メンバーを取得
    $employees = $dept->employees;

    //部署名表示
    echo "部署名: ".$dept->dept_name."メンバー<br>";

    //メンバー表示
    foreach($employees as $employee)
    {
        echo $employee->name."<br>";
    }

    //人数表示
    echo $employees->count()."名<br>";

    return;

});

要は$deptしか取得していないのに、その下にぶら下がる社員を$dept->employeesで取得できるってことです。便利。

社員一覧に部署名を表示

Route::get('/belongsto',function(){

    //社員一覧取得
    $employees = App\Employee::all();

    //ループして表示
    foreach($employees as $employee)
    {
        //データにnullの値があるためif文入れる(本来はいらない)
        if(!empty($employee->dept->dept_name)) 
            //部署名取得
            echo $employee->dept->dept_name." ".$employee->name."<br>";
    }

    return;

});

社員を取得して、$employee->dept->dept_nameで所属部署を得ることができます。

EloquentとQueryBuilderでのJOIN句

1箇所とかだけでリレーションシップを利用したいような場合は、いちいちModelをいじるのもめんどいものです。
そんな場合は、EloquentやQueryBuilderのクエリ?文の中で、JOINも使えます。

Route::get('join',function(){

    //eloquent
    $employees = App\Employee::select()
                    ->join('depts','depts.dept_id','=','employees.dept_id')
                    ->get();

    //表示
    foreach($employees as $employee)
    {
        echo $employee->dept_name." ".$employee->name."<br>";
        //eloquentは、echo $employee['dept_name']でも動く。
    }

    echo "---------------------------------<br>";

    //query builder
    $employees = \DB::table('employees')
                    ->join('depts','employees.dept_id','=','depts.dept_id')
                    ->get();


    foreach($employees as $employee)
    {
        //表示
        echo $employee->dept_name." ".$employee->name."<br>";
    }

    return;     
});

2つのテーブルをJOINさせるとき、id名がかぶる場合があります。そういう時は、->get();内で、->get(["a.id as aid","b.id as bid"]);などと指定すればよいようです。

普通にjoinするとinner joinのようですが、->leftJoin()とすることもできるようです。

その他

EloquentはLaravel以外でも使えます。こちらの情報も合わせてどうぞ。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
323
Help us understand the problem. What are the problem?