LaravelのEloquentを使って複合主キーを使用したテーブルを操作する方法

## 概要
LaravelのEloquentを使用して複合主キーを使用したテーブルを操作する方法です。多対多の中間テーブルで使用することが多いと思います。
ユーザ同士のフォロー機能を例にすると下記のようなテーブル。

|Field  |Type  |Null | Key | 
|---|---|---|---|---|---|
|following_user_id  |int(10) |NO  | PRI |
|followed_user_id  |int(10) |NO  | PRI |
|created_at  |timestamp |NO  |  |
|updated_at  |timestamp |NO  |  | 

ここではフォロー/フォロワーを実装するために、usersとfollowersの2テーブルを使用しています。

## テーブル生成マイグレーション
各テーブルを作成するための、マイグレーションファイル。

```php:usersテーブル
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function($table) {
            $table->increments('id');
            $table->string('nickname', 100)->nullable;
            $table->string('introduction', 500)->nullable();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('users');
    }

}
```
```php:followersテーブル
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateFollowersTable extends Migration {

    public function up()
    {
        Schema::create('followers', function(Blueprint $table) {
            $table->integer('following_user_id')->unsigned();
            $table->integer('followed_user_id')->unsigned();
            $table->timestamps();
            
            // 外部キー設定（必要だったら）
            $table->foreign('following_user_id')->references('id')->on('users');
            $table->foreign('followed_user_id')->references('id')->on('users');
            
            // プライマリキー設定
            $table->unique(['following_user_id', 'followed_user_id']);
        });
    }

    public function down()
    {
        Schema::drop('followers');
    }
}
```

## Eloquentクラス
Followerクラスではプライマリキーを指定して、incrementを無効化しています。

```php:Follower.php
<?php

class Follower extends Eloquent
{
    // テーブル名
    protected $table = 'followers';

    // プライマリキー設定
    protected $primaryKey = ['following_user_id', 'followed_user_id'];
    // increment無効化
    public $incrementing = false;

    protected $fillable = ['following_user_id', 'followed_user_id'];
}
```

```php:User.php
class User extends Eloquent
{
    // テーブル名
    protected $table = 'users';
    protected $guarded = array('id');

    // フォローしているユーザ一覧を取得する
    public function followUsers()
    {
        return $this->belongsToMany('User', 'followers', 'following_user_id', 'followed_user_id')
            ->withTimestamps();
    }
    // フォローされているユーザ一覧を取得する
    public function followedUsers()
    {
        return $this->belongsToMany('User', 'followers', 'followed_user_id', 'following_user_id')
            ->withTimstamps();
    }
}
```

## できること/できないこと
上記のテーブルとFollowerクラスでできることとできないこと。

* データ作成はできる

```
$follower = Follower::create(['following_user_id' => 6, 'followed_user_id' => 10]);
```


* first/allによるデータ取得はできる

```
$follower = Follower::first();
$followers = Follower::all();
```

* saveによる保存はできない（※1）

```
$follower = Follower::first();
$follower->user_id = 1;
$follower->save();
```

* 関連モデルからattach/detachはできる

```
$user = User::find(1);
$user->followUsers()->detach(2);
$user->followUsers()->attach(2);
```

※1 基本的に中間テーブルとして使う場合が多いので、直接操作することはあまりないと思うのですが、どうしても複合主キーを使用したEloquentクラスからsaveを呼び出したい場合は、下記参考リンクのようにoverrideすることにより実装できる。

## 参考

http://laravel.com/docs/4.2/eloquent#working-with-pivot-tables
https://github.com/laravel/framework/issues/5517