Laravel3、小さなクラスで読み込み削除
タグ: Laravel3
この記事は次の記事の内容の続きとして書いています。
- 参照:例外と小さなクラス達
また上記の記事は、更に以下の記事の内容を元に書いています。
さて、前回の記事ではりんごリポジトリー経由でりんごテーブルへレコードを追加しました。例外を使用し、クラスを単機能のクラス・メソッドに分割する方法を紹介しました。
今回は前回の続きです。最初にレコードの読み込み機能を追加しながら、考えて行きましょう。後半はレコード削除です。
ルートの追加
routes.phpに以下の行を追加してください。
// りんご一件表示 Route::get('apple/show/(:num)', array('as'=>'show-apple', 'uses' =>'apple@show'));
コントローラー
コントローラーにりんご表示のためのコードを加えます。
<?php use ProjectH\Exceptions\ExecutionErrorExceptions\IdNotFoundException; use ProjectH\Exceptions\ExecutionErrorExceptions\ValidationFaildException; class Apple_Controller extends Base_Controller { public $restful = true; private $appleRepo; public function __construct() { $this->appleRepo = IoC::resolve( 'AppleRepo' ); } function get_create() { return view( 'apple.create' ); } function post_create() { try { $this->appleRepo->create( Input::only( array ( 'name', 'color' ) ) ); } catch ( ValidationFaildException $e ) { return Redirect::back() ->with_input() ->with_errors( $e->validator ); } return Redirect::home(); } function get_show( $id ) { try { $apple = $this->appleRepo->read( $id ); } catch ( IdNotFoundException $e ) { return Redirect::error( '404' ); } return view( 'apple.read' ) ->with( 'apple', $apple ); } }
get_showメソッドです。レコードのIDを受け取ります。
新しい例外を追加しました。その名が示す通り、IDが見つからないよ例外です。IDはURIで指定された数字ですが、存在するID以外の数字を指定される可能性があります。その場合は、この例外が上がってきますので、コントローラーで捕捉し、404エラーにします。
AppleRepoクラスはpost_createメソッド中でIoCクラスによりインスタンスを取得していたものを、コンストラクターで取得するように変更しました。Appleコントローラーに制御が渡ってくる場合は大抵applesテーブルに対する読み書きが発生することでしょう。ですから、各メソッドで取得のための同じコードをばらまくよりは、一箇所で取得しておいたほうが好ましいですよね。
例外
AppleRepoで投げ、Appleコントローラーで捕捉される新しいIdNotFoundException例外を作成しましょう。
<?php namespace ProjectH\Exceptions\ExecutionErrorExceptions; class IdNotFoundException extends ExecutionErrorException {}
リポジトリクラス
続いてリポジトリクラスにも読み込み処理を追加しましょう。
<?php namespace ProjectH\Repositories; use IoC; use ProjectH\Exceptions\ExecutionErrorExceptions\IdNotFoundException; use ProjectH\Exceptions\ProgramErrorExceptions\ValidatorClassNotFoundException; class AppleRepo { private $apple; public function __construct() { $this->apple = IoC::resolve( 'Apple' ); } public function create( $data ) { if ( IoC::registered( 'AppleValidator' ) ) { $appleValidator = IoC::resolve( 'AppleValidator' ); $appleValidator->validate_create( $data ); } else { throw new ValidatorClassNotFoundException( 'Appleモデルのバリデタークラスが存在していません。' ); } $apple = $this->apple->create( $data ); $apple->save(); } public function read( $id ) { $apple = $this->apple->find( $id ); if ($apple === null) { throw new IdNotFoundException('IDが見つかりません。'); } return $apple; } }
ご覧のとおり、readメソッドを追加しました。Eloquentモデルでfindメソッドで読みこみます。引数で渡したレコードのIDが存在しない場合、nullが返されます。返された値がnullなら例外を投げます。簡単ですね。
ここでもコントローラーで行ったのと同様に、リポジトリクラス内のメソッドではApple Eloquentモデルクラスは必ず使用されるでしょうから、コンストラクターでインスタンスを取得しています。
それに伴い、AppleクラスのIoCコンテナを使ったインスタンスの取得時に、引数を渡す必要が無くなりました。ioc.phpの該当部分を変更しましょう。
if ( !IoC::registered( 'Apple' ) ) { IoC::register( 'Apple', 'Apple' ); }
さて、もう一度リポジトリーのコードに戻りましょう。
Appleクラスをコンストラクターで取得しているのならば、ついでにAppleバリデタークラスも生成してしまえば良いのにと思いませんか。これは色々な考え方ができます。
まずはコンストラクターで生成してしまうという手です。一般的な依存性の注入ではLaravelにおけるIoCコンテナのような便利なクラスの存在を前提にしていません。ですから、依存性注入の「コンストラクターでの注入」パターンに似せるため、コンストラクターでインスタンスを獲得するという方法です。(Laravel4ではこれをもっとスマートに実現できます。)
ただし、明らかにreadメソッドではバリデーションクラスは必要ありません。読み込みだけであれば、入力値のチェックは不必要です。ですからリポクラスのreadメソッドだけを使用する場合、無駄なインスタンスの獲得を行なっていることになります。
それを防ぐには、一クラス一機能という主義になりましょう。createメソッドと、readメソッドは別のクラスに分割します。これもよくある主義ですね。いくつかのプログラムパターンでも見かけます。
でも今回はこのまま、クラス分けをせず、コンストラクターで取得するクラスはメソッド共通のものだけにするという考えで行きます。もちろん、皆さんは自分の考え方に従って、クラスを分割したり、生成したりしてください。
今のところバリデーションを行なっているのは新規追加時だけですが、更新の場合もバリデーションが必要になります。新規と更新のバリデーションでクラスを分けるという考え方もできますし、同じクラスを使用しメソッドを分けるという手法も取れます。
今回は、同じクラスでメソッドを分ける方法を採用します。するとレコード追加時と更新時は毎回バリデーションクラスを生成することになります。レコード追加が同じリクエスト中に何度か起きるコードを書くこともありますし、レコードを何度か更新するかも知れません。今回そのようなコードを書く可能性は低いですが、バリデーションクラスは毎回生成しなくても、一つのインスタンスを使い回しができそうです。ならば自分のコードの中で対応するよりは、IoCコンテナの機能を使用しましょう。
ioc.phpのAppleValidatorクラスの登録を以下のように変えることで、インスタンスが一つだけしか生成されないようになります。
IoC::singleton( 'AppleValidator', '\\ProjectH\\Services\\Validators\\AppleValidator' );
ビュー
最後にビューです。簡単なビューです。
<p>ID:{{ $apple->id }}</p> <p>りんご名:{{ e($apple->name) }}</p> <p>りんご色:{{ e($apple->color) }}</p>
これでURIapple/show/1
にブラウザからアクセスすれば、最初のりんごテーブルのレコードの内容が表示されます。
レコード削除
続いてレコード削除を実装しましょう。今回は確認ページを表示してまで本当に削除するのか尋ねません。単純に、URIで指定されたら、即削除しましょう。
まずはルートの定義です。routes.phpですね。
// りんご削除 Route::get('apple/delete/(:num)', array('as'=>'delete-apple', 'uses' =>'apple@delete'));
りんごコントローラーに削除のアクションを追加しましょう。
function get_delete( $id ) { try { $this->appleRepo->delete( $id ); } catch ( IdNotFoundException $e ) { return Redirect::error('404'); } return Redirect::home(); }
表示とほとんど同じです。
最後にリポジトリーに削除メソッドを追加です。
public function delete( $id ) { $apple = $this->apple->find( $id ); if ( $apple === null ) { throw new IdNotFoundException( 'IDが見つかりません。' ); } $apple->delete(); }
指定されたIDから1レコードを取得し、取得できなかった場合は例外を出す部分は、readメソッドと待っとく同じです。これはひと工夫しましょう。
public function read( $id ) { $apple = $this->find( $id ); return $apple; } public function delete( $id ) { $apple = $this->find( $id ); $apple->delete(); } private function find( $id ) { $apple = $this->apple->find( $id ); if ( $apple === null ) { throw new IdNotFoundException( 'IDが見つかりません。' ); } return $apple; }
例外を使っているため、readとdeleteメソッドの中でいちいちfindの返り値をチェックするなど面倒なことは行なっていません。例外をtry{}catch{}で補足せず、そのままスルーしています。ですからIDが見つからない場合、findメソッドから制御が返ってくることはありません。逆に言えば制御が戻ってきたときは、IDが見つかっているので、戻り値はApple Eloquentモデルとして利用可能であると保証されています。
例外も慣れてしまえば簡単で便利でしょう。
次の記事では、更新処理を行いましょう。今回の応用です。