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:もう少しスマートな方法はないものか。