Laravel 4 + Sentry 2でデフォルトの外部キー名を変更する

前回の続きです。

前回のエントリでは、Sentryで使用するテーブル「users」「groups」「users_groups」「throttle」を、「my_users」「my_groups」「my_users_groups」「my_throttle」に変更しました。しかし、カラム名は変更しなかったので、「my_users_groups」「my_throttle」テーブルから「my_users」「my_groups」テーブルに対して張られる外部キーの名前がデフォルトの「user_id」「group_id」のままになっており、Eloquentの命名規約(モデル名 + id)とずれてしまっていました。そこで今回は外部キーの名前をそれぞれ「my_user_id」「my_group_id」に変更します。

手順

1. Userモデル、Groupモデルの外部キーの命名規約を変更する

「my_users_groups」テーブルから「my_users」「my_groups」テーブルに対して張られる外部キーの命名規約を変更するのは簡単です。SentryのEloquentでは、UserモデルのgroupsメソッドでGroupモデルに対する多対多のリレーションを、GroupモデルのusersメソッドでUserモデルに対する多対多のリレーションをそれぞれ定義しています。これらのメソッドをオーバーライドすることで外部キーの命名規約を変更できます。

app/models/MyUser.php

<?php

class MyUser extends Cartalyst\Sentry\Users\Eloquent\User {

	protected $table = 'my_users';

	public function groups()
	{
		return $this->belongsToMany(static::$groupModel, static::$userGroupsPivot, 'my_user_id', 'my_group_id');
	}

}

belongsToManyメソッドの第3引数、第4引数に関係するキー名を渡します。

app/models/MyGroup.php

<?php
class MyGroup extends Cartalyst\Sentry\Groups\Eloquent\Group {

	protected $table = 'my_groups';

	public function users()
	{
		return $this->belongsToMany(static::$userModel, static::$userGroupsPivot, 'my_group_id', 'my_user_id');
	}

}

こちらも同様です。

2. Throttleモデルの外部キーの命名規約をオーバーライドする

「my_throttle」テーブルから「my_users」テーブルに対して張られる外部キーの命名規約を変更するのは少し面倒です。vendor/cartalyst/sentry/src/Cartalyst/Sentry/Throttling/Eloquent/Provider.phpのfindByUserメソッド内で「user_id」という文字列がハードコーディングされているためです。
これを回避するために、app/start/global.phpなどに下記を追加し、findByUserメソッドをオーバーライドして、「user_id」を「my_user_id」に置換します*1

app/start/global.php

<?php
...

$app = app();
$app['sentry.throttle'] = $app->share(function ($app)
{
	$model = $app['config']['cartalyst/sentry::throttling.model'];
	return new MyThrottleProvider($app['sentry.user'], $model);
});

use Cartalyst\Sentry\Users\UserInterface;

class MyThrottleProvider extends Cartalyst\Sentry\Throttling\Eloquent\Provider {

	public function findByUser(UserInterface $user, $ipAddress = null)
	{
		$model = $this->createModel();
		$query = $model->where('my_user_id', '=', ($userId = $user->getId()));

		if ($ipAddress)
		{
			$query->where(function($query) use ($ipAddress) {
				$query->where('ip_address', '=', $ipAddress);
				$query->orWhere('ip_address', '=', NULL);
			});
		}

		if ( ! $throttle = $query->first())
		{
			$throttle = $this->createModel();
			$throttle->my_user_id = $userId;
			if ($ipAddress) $throttle->ip_address = $ipAddress;
			$throttle->save();
		}

		return $throttle;
	}

}

あとは「my_users_groups」テーブルのときと同じように、EloquentのリレーションをオーバーライドすればOKです。
ThrottleモデルのuserメソッドでUserモデルに対する1対多のリレーションが定義されているので、MyThrottleモデルでuserメソッドをオーバーライドします。

app/models/MyThrottle.php

<?php

class MyThrottle extends Cartalyst\Sentry\Throttling\Eloquent\Throttle {

	protected $table = 'my_throttle';

	public function user()
	{
		return $this->belongsTo('MyUser', 'my_user_id');
	}

}

belongsToメソッドの第2引数に外部キー名を渡します。

3. マイグレーションを変更する

「my_users_groups」「my_throttle」テーブルのマイグレーションをエディタで開き、カラム名を「user_id」「group_id」から「my_user_id」「my_group_id」に変更します。

app/database/migrations/2013_12_19_091459_create_my_users_groups.php

<?php

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

class CreateMyUsersGroups extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
		Schema::create('my_users_groups', function(Blueprint $table)
		{
			$table->integer('my_user_id')->unsigned();
			$table->integer('my_group_id')->unsigned();

			// We'll need to ensure that MySQL uses the InnoDB engine to
			// support the indexes, other engines aren't affected.
			$table->engine = 'InnoDB';
			$table->primary(array('my_user_id', 'my_group_id'));
		});
	}

	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
		Schema::drop('my_users_groups');
	}

}

app/database/migrations/2013_12_19_091441_create_my_throttle.php

<?php

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

class CreateMyThrottle extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
		Schema::create('my_throttle', function(Blueprint $table)
		{
			$table->increments('id');
			$table->integer('my_user_id')->unsigned();
			$table->string('ip_address')->nullable();
			$table->integer('attempts')->default(0);
			$table->boolean('suspended')->default(0);
			$table->boolean('banned')->default(0);
			$table->timestamp('last_attempt_at')->nullable();
			$table->timestamp('suspended_at')->nullable();
			$table->timestamp('banned_at')->nullable();

			// We'll need to ensure that MySQL uses the InnoDB engine to
			// support the indexes, other engines aren't affected.
			$table->engine = 'InnoDB';
			$table->index('my_user_id');
		});
	}

	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
		Schema::drop('my_throttle');
	}

}

このあと上記のマイグレーションを実行すればOKです。

*1:もう少しスマートな方法はないものか。