Laravel4、UserクラスでCRUD
タグ: Laravel4
Laravel4 ベータ4がリリースされました。
どんなふうに変わっているのかを紹介するため、認証にも使用するUserクラスを使用して、紹介していきます。
なお、現在の開発バージョンはベータ5で、正式版リリースまで後約1から2ヶ月です。5月リリース予定ですからね。正式版では変更される部分もあると思います。予めご了承ください。
なお、タイトルはCRUDですが、今回行うのはCUまでです。残りのRDは応用で、簡単ですからね。皆さん自分で試してください。
usersテーブルの準備
データベース設定ファイルの内容はほぼ変更ありません。Laravel3の時と同様に設定してください。
データベースを設定したら、マイグレーション初期化のため、管理テーブルを作成しましょう。Artisanコマンドラインツールを使用します。
php artisan migrate:install
Artisanも進歩しまして、php artisan
だけ打ち込むと、コマンドを表示してくれます。忘れた時の確認に使用できます。
環境の設定はLaravel3の頃と変わりません。--env=local
でローカル環境、つまりconfig/localフォルダー中の設定ファイルの内容が優先して使用されます。
マイグレーション初期化が終了しましたら、usersテーブルを生成するマイグレーションを書きましょう。usersテーブルの生成はコマンドによる自動化がされていません。私はこれに賛成します。なぜなら、usersテーブルにどの様な情報を入れるか、どの項目を認証に使用するかは、開発者やプロジェクトごとに差がある部分ですから、自動化する意味があまりないと思っています。(ぶっちゃけ、一度作成した他のプロジェクトからコピーしたほうが早いし、分かりやすいですからね。)
まずは、マイグレーションファイルを生成しましょう。
php artisan migrate:install CreateUsersTable
基本的にはLaravel3と変わりません。Laravel4からcomposerに乗っかりPSR-0とかPSR−1とか2とかに準拠する関係上、関数名はローアーキャメル、クラス名はアッパーキャメル記法ですので、それに合わせています。
コマンド実行するとapp/database/migratationsに作成日時と指定したクラス名のファイル名がついたphpファイルが生成されます。それをエディターで開いてください。
内容を以下のように更新し、保存します。
<?php use Illuminate\Database\Migrations\Migration; class CreateUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create( 'users', function($table) { $table->increments( 'id' ); $table->string( 'username', 32 ); $table->string( 'email', 320 ); $table->string( 'password', 64 ); // 最低60文字 $table->integer('rank'); $table->timestamps(); } ); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('users'); } }
テーブル生成はLaravel3と同じです。ドロップでは、dropIfExistsメソッドが追加されました。これは、メソッド名が示す通り、テーブルが存在する場合にドロップを行うものです。マイグレーションの順番を間違えたり、生成でエラーが置きたりした結果、テーブルができていないのにdown()のドロップを行い、エラが発生し、マイグレーションのロールバックが上手く動作しないことがLaravel3ではありました。それを防ぐためこのメソッドが作成された様子です。基本、down()メソッドの中では、このdropIfExistsメソッドを使用しましょう。
マイグレーションファイルを作成したら、マイグレーションを実行しましょう。
php artisan migrate
実際に作成されていることを、ツール類で確認してみましょう。
Userクラスの定義
続いてサンプルデータの生成を行いたいところですが、先にUserクラスを作リましょう。現在のベータ4ではUserクラスのひな形がapp/modelsディレクトリーに用意されています。以下のように多少変更してください。
<?php use Illuminate\Auth\UserInterface; use Illuminate\Auth\Reminders\RemindableInterface; class User extends Eloquent implements UserInterface, RemindableInterface { /** * このモデルで使用されるデータベース名 * * @var string */ protected $table = 'users'; // デフォルト通りのため、必要なし /** * JSON形式への変換時に、対象外にするフィールド * * @var array */ protected $hidden = array('password'); // 以下のプロパティ追加 /** * 複数代入禁止フィールド指定 * * @var array */ protected $guarded = array('id'); /** * ユーザーを一意に特定するIDを取得 * * @return mixed */ public function getAuthIdentifier() { return $this->getKey(); } /** * ユーザーのパスワードを取得 * * @return string */ public function getAuthPassword() { return $this->password; } /** * パスワードリマンダーを送信するメールアドレスの取得 * * @return string */ public function getReminderEmail() { return $this->email; } // 以下のメソッド追加 /** * パスワードセッター * * @param string $value */ public function setPasswordAttribute($value) { $this->attributes['password'] = Hash::make($value); } }
Eloquentモデルです。デフォルトではクラス名複数型の小文字がテーブル名になります。
Laravel3と大きく異なるのはコンストラクタやfillメソッドで、配列を指定することでキーと同じ名前のインスタンスのプロパティーに代入してくれる複数代入の機能です。Laravel3では、デフォルトでは全項目代入可能になっていました。Laravel4ではセキュリティー的に強化するため、全項目代入禁止になっています。
$guardedか$fillableプロパティーを指定し、代入禁止、もしくは代入許可を指定する必要があります。
セッター(ミューテーター)のメソッド名も変更になりました。set属性名Attributeです。属性名は先頭一文字大文字、残りは小文字です。
シーディング
さて、シーディングへ戻りましょう。
データベースの初期データ設定をシーディング(seeding)と言います。シーディングのための機能も追加されました。ベータ期間中も、かなり変更されている部分です。
app/database/seedsディレクトリーにシーディングのためのクラスを定義しましょう。今回はUsersTableSeeder.phpファイルです。
<?php class UsersTableSeeder extends Seeder { public function run() { DB::table( 'users' )->delete(); User::create( array ( 'username' => 'user1', 'password' => 'pass1', 'email' => 'user1@x.com', 'rank' => 100, // 管理者 ) ); User::create( array ( 'username' => 'user2', 'password' => 'pass2', 'email' => 'user2@x.com', 'rank' => 1, ) ); User::create( array ( 'username' => 'user3', 'password' => 'pass3', 'email' => 'user3@x.com', 'rank' => 1, ) ); } }
シーディングに使用するクラスはSeederクラスを拡張し、中にrunメソッドを用意します。
内容はご覧の通りです。先にユーザークラスを作成していますので、パスワードなどもそのまま記述できます。
これを実行するには以下のArtisanコマンドを実行します。
php artisan db:seed UsersTableSeeder
これでUsersテーブルにサンプルデータが生成されました。しかし、いちいちクラス名を指定するのは面倒です。
実はクラス名を指定しない時のデフォルトはapp/database/seedsのDatabaseSeeder.phpで定義されているDatabaseSeederクラスになっています。このrunメソッドから、自分で作成したシーディングクラスを呼び出すようにすることで、まとめてシーディングが実行できるようになっています。
<?php class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Eloquent::unguard(); // 複数代入の保護機能を停止 $this->call('UsersTableSeeder'); // 他のシーディングも同様に続けて書いておく } }
これで、まとめてシーディングを実行したい時はクラスを指定しないでdb:seedを実行し、個別にシーディングしたい場合はクラス名を指定して実行するというように、使い分けができるようになりました。
新規作成
説明が長くなりますので、今回ルートはコントローラではなくrouts.phpに記述します。
Route::get( '/user/add', function() { return View::make( 'user.create' ); } ); Route::post( '/user/add', array ( 'before' => 'csrf', function() { $inputs = Input::only( array ( 'username', 'email', 'password' ) ); $rules = array ( 'username' => array ( 'required', 'min:4', 'max:32', 'unique:users' ), 'password' => array ( 'required', 'min:6', 'max:30' ), 'email' => array ( 'required', 'email', 'max:320', 'unique:users' ), ); $val = Validator::make( $inputs, $rules ); if ( $val->fails() ) { return Redirect::back()->withErrors( $val )->withInput(); } $inputs['rank'] = 1; User::create( $inputs ); return Redirect::back(); } ) );
ビューです。app/views/user/create.blade.phpです。
{{ Form::open() }} <p> {{ Form::label('username', 'ユーザー名') }} {{ Form::text('username', Input::old('username', '')) }} @if ($errors->has('username')) {{ $errors->first('username') }} @endif </p> <p> {{ Form::label('password', 'パスワード') }} {{ Form::password('password') }} @if ($errors->has('password')) {{ $errors->first('password') }} @endif </p> <p> {{ Form::label('email', 'メール') }} {{ Form::text('email', Input::old('email', '')) }} @if ($errors->has('email')) {{ $errors->first('email') }} @endif </p> {{ Form::submit('登録') }} {{ Form::token() }} {{ Form::close() }}
Formクラスが復活したため、Laravel3とたいして違いがありません…
…実は、違わないように書いてみました。このように、ほぼ同じようにかけます。ですから、Laravel3で身につけたスキルが無駄になるわけでありません。
これでは面白くありませんね。Laravel4の機能を活用して、今度は更新処理を書いてみましょう。
更新処理
ルートの部分です。
// Laravel4の新機能 Route::model( 'user', 'User' ); // {user}とUserクラスを結びつける Route::get( '/user/{user}', // 存在しないIDなら、404 function(User $user) { // この時点で$userは存在しているレコード return View::make( 'user.update' )->with( 'user', $user ); } ); Route::post( '/user/{user}', // 存在しないIDなら、404 array ( 'before' => 'csrf', function(User $user) { // この時点で$userは存在しているレコード $inputs = Input::only( array ( 'username', 'email', 'password' ) ); $rules = array ( 'username' => array ( 'required', 'min:4', 'max:32', 'unique:users,username,'.$user->id ), 'password' => array ( 'required', 'min:6', 'max:30' ), 'email' => array ( 'required', 'email', 'max:320', 'unique:users,email,'.$user->id ), ); $val = Validator::make( $inputs, $rules ); if ( $val->fails() ) { return Redirect::back()->withErrors( $val )->withInput(); } $user->fill( $inputs )->save(); return Redirect::back(); } ) );
Laravel4ではルートの可変部分に名前を付けられるようになりました。他のフレームワークでもできるものが多いですよね。更に、その名前にEloquentモデルを結びつけることができます。
後は、ルートをご覧いただくと分かります。{user}部分にIDが来るわけです。そのIDがテーブルに存在しなければ、404エラーになります。Laravel3でまともに動作するCRUDを書けば必ずIDのチェックは必要でした。(Laravel3でなくても、何を使おうと必要です。)Laravel4はIDのチェックを取り込んでしまいました。
ですから、ルートの内側に制御が渡ってきた時点で$userのユーザー情報はテーブル上に存在しており、$userはそれを獲得したものですので、安心して使用できます。
今度はビューをご覧ください。app/views/user/update.blade.phpです。
{{ Form::model($user) }} <p> {{ Form::label('username', 'ユーザー名') }} {{ Form::text('username') }} @if ($errors->has('username')) {{ $errors->first('username') }} @endif </p> <p> {{ Form::label('password', 'パスワード') }} {{ Form::password('password') }} @if ($errors->has('password')) {{ $errors->first('password') }} @endif </p> <p> {{ Form::label('email', 'メール') }} {{ Form::text('email') }} @if ($errors->has('email')) {{ $errors->first('email') }} @endif </p> {{ Form::submit('登録') }} {{ Form::token() }} {{ Form::close() }}
createビューとどこが違うか、分かりますか。
フォームのオープンがopenメソッドの代わりにmodelメソッドを使用しています。これが新機能です。
たぶん、この機能のためにFormクラスを復活させたのだと思われます。
modelメソッドで開くと、フォームの入力要素の生成メソッドで、渡したEloquentモデルのインスタンスの属性名と同じフィールド名の値を表示してくれるのです。
しかも、フォーム入力内容の処理でバリデーションエラーが発生した場合、リダイレクトに対してwithInputメソッドで入力値をフラッシュデーターとしてセッションに保存しますが、セッション中に入力項目と同じキーのアイテムが保存されている場合、優先的にそちらを表示してくれます。
つまり、上記のコードの通り、いちいちフォームに表示する内容を、例えばForm::text('name', Input::old('name', ...)))
なんて形で指定する必要がありません。ご覧の通り、すっきりです。
楽しいでしょう?次は認証を見てみましょう。こちらも便利になっていますよ。