Laravel5.xのリレーション


MVC系フレームワークのリレーションって使わないな・・・というところですが、食わず嫌いもなんなので、ひとまずまとめておきます。とは言え、私が使うのはせいぜいhasManyとbelongsToだけなので、とりあえずその２つ。

##想定例

いくつかの企業があり、その企業が、それぞれお店を経営していて、さらにそのお店に導入されているPCを管理するようなイメージ。３階建て構造。

利用するテーブルは、下記の２つ。

* companiesテーブル
* shopsテーブル
* terminalsテーブル

##データとモデルの準備

Migrateしてもいいけど、今回は素のSQLで。
私はMySQLを利用しています。

こんな感じ。

```sql
drop table if exists companies;
create table companies(
	id int primary key auto_increment,
	name varchar(32)
);

insert into companies(name) values('ConpanyA');
insert into companies(name) values('ConpanyB');


drop table if exists shops;
create table shops(
	id int primary key auto_increment,
	name varchar(32),
	company_id int
);

insert into shops(name,company_id) values('A Tokyo',1);
insert into shops(name,company_id) values('A Osaka',1);
insert into shops(name,company_id) values('B Fukuoka',2);



drop table if exists terminals;
create table terminals(
	id int primary key auto_increment,
	name varchar(32),
	shop_id int
);

insert into terminals(name,shop_id) values('TermA',1);
insert into terminals(name,shop_id) values('TermB',2);
insert into terminals(name,shop_id) values('TermC',3);
insert into terminals(name,shop_id) values('TermD',1);
insert into terminals(name,shop_id) values('TermE',2);
```

各モデルは、

####Company.php

メソッドの命名規則とかは特にないようですが、hasManyの場合はshopsのようにsをつけ、blongsToの場合はshopのようにs無しとしました。

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Company extends Model
{
	public $timestamps = false;
	
    public function shops()
    {
    	return $this->hasMany('App\Shop');
    }
}
```

####Shop.php

3階建てなので、中間？層のshopsは、hasMeny()もbelongsToもあります。

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Shop extends Model
{
	public $timestamps = false;
	
    public function company()
    {
    	return $this->belongsTo('App\Company');
    }

    public function terminals()
    {
    	return $this->hasMany('App\Terminal');
    }
}
```

####Terminal.php

belongsTo()を定義。

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Terminal extends Model
{
	public $timestamps = false;
	
    public function shop()
    {
    	return $this->belongsTo('App\Shop');
    }
}
```

eloquentでは、外部キー等はルールに従えば明示しなくても大丈夫です。
つまり、各テーブルの主キーがidとなっており、各参照idが、company_idやshop_idとかになっていればいいです。
そうでない場合も、hasMany(),belongsToの第2引数、第3引数に指定してやればOKです。

##利用例

利用シーンとしては、

* 参照系
* 書込系

の2パターンがあるかなと。

###参照系

この例では、Companyを取得し、暗黙にShopsもTerminalsも取得できているという例と、Terminalを取得すれば、そのパラメーターとして、ShopやCompanyの値も取得できるという例です。

```php
    public function index()
    {
        //会社のみ取得で全情報を取得
        $companies = Company::all();

        //下位（端末情報）から上位情報取得
        $terminal = Terminal::find(1);
        $shop_name = $terminal->shop->name;
        $company_name = $terminal->shop->company->name;

        //return
        return view('index')
            ->with('companies',$companies)
            ->with('shop_name',$shop_name)
            ->with('company_name',$company_name);
    }
```

一応、表示するblade。Companyはループで全階層を表示しています。
※個人的には、ループしなくても、各リソースにアクセスする方法がないか知りたい。

```html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h3>上位から下位を参照</h3>
@foreach($companies as $company)
	{{$company->name}}<br>
	@foreach($company->shops as $shop)
		---{{$shop->name}}<br>
		@foreach($shop->terminals as $terminal)
			---------{{$terminal->name}}<br>
		@endforeach
	@endforeach
@endforeach
<h3>下位から上位の参照</h3>
<p>terminal id = 1の上位情報</p>
{{$shop_name}}<br>
{{$company_name}}<br>
</body>
</html>
```

###更新系

特定の従属先を指定して、下位の情報を保存するような場合。
これには、

* 上位から、下位を紐付けるパターン
* 下位から、上位を紐付けるパターン

があるのかなと。

* 上位から下位の場合は、->save()
* 下位から上位の場合は、->associate()（その後、->save())

を利用するようです。

```php
    public function add()
    {
        //上位から紐付け

        //紐付け情報生成
        $shop = Shop::create();
        $shop->name = "foo";

        //紐付け
        $company = Company::find(2);
        $company->shops()->save($shop);

        //下位から紐付け
		
		 //端末情報生成
        $terminal = Terminal::create();
        $terminal->name = "TermH";
		　
		 //店情報取得
        $shop = Shop::find(1);
        //端末から店を紐付け
        $terminal->shop()->associate($shop);
        //保存
        $terminal->save();

        return redirect()->to('/');
    }
```

とりあえずは以上。