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以外でも使えます。こちらの情報も合わせてどうぞ。