Laravel4、テーブル操作の戻り値
タグ: Laravel4
今回のLaravel4のドキュメントは、不親切な部分があります。Laravel3の知識を前提にしているのかどうかわかりませんが、初めてLaravelを使用する人は、基本的な部分が抜けてしまいがちです。
今回は、そうした中から、テーブル操作の戻り値について、多少解説します。
Eloquentの戻り値
EloquentはORMです。テーブル上の1レコードをPHPのオブジェクト一つに対応させ、オブジェクトの操作や指定により、テーブル操作をやってしまおうというものです。
クエリーの結果を全部受け取るall()、クエリーの結果が複数の場合はget()、最初の一件だけを受け取るfirst()、レコードのID番号を指定し、一件受け取るfind()が基本となります。
find()とfirst()では、結果のレコード件数は0か1件です。0件の場合を例外で処理したい場合は、Laravel4で追加された、findOrFail()、firstOrFail()を使用します。
結果が一件の場合は、該当するレコードの値に$return->フィールド名で、アクセスできます。例えばAppleクラスはapplesテーブルに対するマッパークラスですが、テーブルのフィールドにnameという項目があれば、以下のようにアクセスできます。
- $apple = Apple::find( 7 );
- echo $apple->name;
- // もしくは
- echo $apple['name'];
この場合、IDが7のレコードが存在するならば、上記のようにアクセスできます。しかし、存在しない場合は、例外が発生します。
get()とall()は、条件により0件以上のレコードが存在します。foreachによりループ処理で処理するのが基本です。
- $apples = Apple::all();
- foreach ($apples as $apple)
- {
- echo $apple->name;
- }
get()、all()、first()、find()の戻り値は、
count()
を使用することで、件数を簡単にチェックできます。
- $apples = Apple::all();
- echo count($apple); // 全レコード件数表示
- $apple = Apple::find( $id );
- // フィールドにアクセスする前に、実用的には通常必ずチェックが必要
- if (count($apple) != 1) App::abort(404, 'ページが見つかりません');
- echo $apple->name;
- // もしくは例外を発生させ、グローバルに処理する
- // try catchしても良い
- $apple = Apple::findORFail( $id ); // レコードに一致しなければ、例外発生
- echo $apple->name;
- // 通常、start/globle.phpの中などで例外処理を書く
- use Illuminate\Database\Eloquent\ModelNotFoundException;
- App::error(function(ModelNotFoundException $e)
- {
- return Response::make('見つかりません', 404);
- });
クエリービルダー
具体的にはDBクラスにより、テーブルにアクセスする方法です。
insert
insert()の戻り値は、挿入の成否を論理値で返されます。
update
update()の戻り値は、影響を受けたレコード件数です。
delete
delete()の戻り値は、影響を受けたレコード件数です。
Eloquent ORM
Eloquentは戻り値がバラエティーに富んでいますが、論理的に考えれば納得できるでしょう。
insert
挿入には2つの方法があります。ORMでは、一件ずつ挿入します。なぜなら、レコードとモデルは対応しているわけで、モデルオブジェクトは一度に一つだけしか、生成できないからです。
create()で挿入する場合、戻り値は新しく生成した、オブジェクトです。
- $apple = Apple::create(array('name'=>'ふじ'));
- echo $apple->id; // 今回作成されたレコードのid
- echo $apple->name; // 'ふじ'
save()で挿入する場合、戻り値は成否フラグです。
- $apple = new Apple(array('name' => 'ふじ'));
- $result = $apple->save();
- if ( ! $result ) return View::make('error');
update
更新処理は、一度レコードを取得し、その内容を書き換え、save()する手順となります。save()を使用するので、先の挿入処理と同様に、成否フラグが返ってきます。
delete
削除はその方法により、戻り値が異なっています。
一番基本となるのは、一度レコードを取得し、削除する方法です。戻り値は成否フラグです。
- $apple = Apple::find( $id );
- $result = $apple->delete();
アクティブレコードと同様に、whereで条件を指定し、該当するレコードを複数削除できます。その場合、影響を受けたレコード数が戻ってきます。
- $affectedRecode = Apple::where('name', '紅玉')->delete();
キー指定による複数レコード削除も可能です。この場合、返り値は戻って来ません。
- Apple::destroy( 1, 2 );
このdesttoy()は削除したいレコードのid値を複数指定できます。テーブルに存在する場合は削除し、存在しない場合は何もしません。存在しなくても、エラーになりません。
検証テストコード
上記を検証するために、記述したテストコードです。
- <?php
- class TabelOperationResultTest extends TestCase
- {
- public function setUp()
- {
- parent::setUp();
- Artisan::call( 'migrate' );
- // Artisan::call( 'migrate:refresh' );
- $this->seed();
- }
- public function test全レコード件数カウント()
- {
- $apple = Apple::all();
- $this->assertSame( count( $apple ), 2 );
- }
- public function testGetによるレコード件数カウント()
- {
- $apple = Apple::get();
- $this->assertSame( count( $apple ), 2 );
- }
- public function testFirstによるレコード件数カウント()
- {
- $apple = Apple::whereNotNull( 'name' )->first();
- $this->assertSame( count( $apple ), 1 );
- }
- public function testFindによるレコード件数カウント()
- {
- $apple = Apple::find( 1 );
- $this->assertSame( count( $apple ), 1 );
- }
- public function testAllによるレコード未取得時件数カウント()
- {
- DB::table( 'apples' )->delete(); // 全件削除
- $apple = Apple::whereNull( 'name' )->get();
- $this->assertSame( count( $apple ), 0 );
- }
- public function testGetによるレコード未取得時件数カウント()
- {
- $apple = Apple::whereNull( 'name' )->get();
- $this->assertSame( count( $apple ), 0 );
- }
- public function testFirstによるレコード未取得時件数カウント()
- {
- $apple = Apple::whereNull( 'name' )->first();
- $this->assertSame( count( $apple ), 0 );
- }
- public function testFindによるレコード未取得時件数カウント()
- {
- $apple = Apple::find( 99999 );
- $this->assertSame( count( $apple ), 0 );
- }
- public function testクエリービルダーのinsertの戻り値が成否フラグ()
- {
- $result = DB::table( 'apples' )
- ->insert(
- array( 'name' => '紅玉', 'weight' => 100 ),
- array( 'name' => 'BigApple', 'weight' => 150 ),
- array( 'name' => '小りんご', 'weight' => 20 )
- );
- $this->assertSame( $result, true );
- }
- public function testEloquentによるレコード追加の戻り値が成否フラグ()
- {
- $apple = new Apple( array('name' => 'ゴールド', 'weight' => 130 ) );
- $ret = $apple->save();
- $this->assertSame( $ret, true );
- }
- public function testクエリービルダーのupdateによる複数レコード更新戻り値が影響行数()
- {
- $affectedRecodeCount = DB::table( 'apples' )
- ->update(
- array('name' => '紅玉', 'weight' => 100)
- );
- $this->assertSame( $affectedRecodeCount, 2 );
- }
- public function testクエリービルダーのupdateによる1レコード更新戻り値が影響行数()
- {
- $affectedRecodeCount = DB::table( 'apples' )
- ->where('name', 'ふじ') // 一件抽出
- ->update(
- array('name' => '紅玉', 'weight' => 100)
- );
- $this->assertSame( $affectedRecodeCount, 1 );
- }
- public function testクエリービルダーのupdateによるレコード更新0件時の戻り値が影響行数()
- {
- $affectedRecodeCount = DB::table( 'apples' )
- ->whereNull('name')
- ->update(
- array('name' => '紅玉', 'weight' => 100)
- );
- $this->assertSame( $affectedRecodeCount, 0 );
- }
- public function testEloquentによるレコード更新の戻り値が成否フラグ()
- {
- $apple = Apple::find( 1 );
- $apple->name = '椎名林檎';
- $result = $apple->save();
- $this->assertSame( $result, true );
- }
- public function testクエリービルダーによる複数行削除の戻り値が影響行数()
- {
- $affectedRecodeCount = DB::table( 'apples' )->delete(); // 全件削除
- $this->assertSame( $affectedRecodeCount, 2 );
- }
- public function testクエリービルダーによる0件削除の戻り値が影響行数()
- {
- $affectedRecodeCount = DB::table( 'apples' )
- ->whereNull( 'name' )
- ->delete();
- $this->assertSame( $affectedRecodeCount, 0 );
- }
- public function testEloquentによる1件削除の戻り値が成否フラグ()
- {
- $apple = Apple::find(1);
- $result = $apple->delete();
- $this->assertSame( $result, true );
- }
- public function testEloquentによる複数削除の戻り値が影響行数()
- {
- $affectedRecodeCount = Apple::whereNotNull('name')->delete();
- $this->assertSame( $affectedRecodeCount, 2 );
- }
- public function testEloquentのキー指定削除による戻り値は無し()
- {
- $affectedRecodeCount = Apple::destroy( 1, 2 );
- $this->assertNull( $affectedRecodeCount );
- }
- public function testEloquentのキー指定削除による削除は存在するキーのみ削除する()
- {
- $affectedRecodeCount = Apple::destroy( 1, 9999 ); // 1件目のみ削除
- $apples = Apple::all();
- $this->assertSame(count($apples), 1);
- $this->assertEquals($apples->first()->name, '女医ゴールド'); // 2件目の名前
- }
- }
データベースはSQLiteでメモリー上に配置しており、毎回自動的に消去されます。これを使用するのは、テストスピードあげるためです。
テスト本にはsetUP()の中で、
Artisan::call( 'migrate:refresh' );
を呼び出すサンプルになっています。最近試したところ、私の環境では、Artisan::call('migrate');
にする必要がありました。以前は確かに、DBはメモリ上にあっても、電源を切るまで、しばらく持続していました。そのため、マイグレーション用のテーブルは残っており、migrate:refreshで、良かったのです。今回は、マイグレーションテーブルも逐一消去されているようなので、毎回マイグレーションを実行するように変更しました。
app/config/testing/database.php
- <?php
- return array(
- 'default' => 'sqlite',
- 'connections' => array(
- 'sqlite' => array(
- 'driver' => 'sqlite',
- 'database' => ':memory:',
- 'prefix' => '',
- )
- )
- );
マイグレーションは一つだけです。名前は適当に。
- <?php
- use Illuminate\Database\Migrations\Migration;
- use Illuminate\Database\Schema\Blueprint;
- class CreateApplesTable extends Migration {
- /**
- * Run the migrations.
- *
- * @return void
- */
- public function up()
- {
- Schema::create('apples', function(Blueprint $table) {
- $table->increments('id');
- $table->string('name');
- $table->integer('weight')->unsigned();
- $table->timestamps();
- });
- }
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
- {
- Schema::drop('apples');
- }
- }
シードクラスはapp/database/seed/ApplesTableSeeder.phpです。これをDatabaseSeederクラスから、呼び出しています。
- <?php
- class ApplesTableSeeder extends Seeder
- {
- public function run()
- {
- //DB::table('apples')->delete();
- $apples = array(
- array( 'name' => 'ふじ', 'weight' => 150 ),
- array( 'name' => '女医ゴールド', 'weight' => 200 )
- );
- DB::table( 'apples' )->insert( $apples );
- }
- }
最後に、Appleモデルです。
- <?php
- class Apple extends Eloquent {
- protected $guarded = array();
- }