Laravelでサクッと開発のコツ

タグ: Laravel3   Laravel4   高速開発  

サクッと開発するという状況はつまり、小さめのサイズのWebアプリを開発するとか、試作(プロトタイプ)をスピーディーに作成するような状況です。うんうんうなりながら、やれテストだ、やれ駆動開発だ、セキュリティ強化だと頭を悩ませる開発とは別物です。

Laravel3は特にこちらのサクッと開発に向いています。Laravel4では全体的に多少煩雑になっていますが、サクッと開発するための機能も追加されています。

他のフレームワークの癖が付いていると、なかなかサクッと開発するためのコツがつかめません。そのため、高速開発を目指す人のため、コツを並べていきたいと思います。

テーブルのカラム名=ビューの項目名=ORMの属性名(使用する場合)

適用:Laravel3、Laravel4

カラム名と項目名、属性名の対応する項目は全部同じ名前にしましょう。テーブルのカラム名がORMであるEloquentのモデルの属性名に必然的になりますので、これは特に設定は必要ありません。

セキュリティのことを考えるなら、多少変化をつけたほうが良いかと思いますが、他の対策をきちんと取っておけば大丈夫でしょう。

入力フィールドはgetを使用せずonlyで取得

適用:Laravel3、Laravel4

Inputクラスは引数無しのget、もしくはallメソッドを使用しても良いのですが、onlyを活用する機会のほうが多くなります。

これらのメソッドはフォームの入力を'属性名'=>‘値’の配列で返してくれます。これが重要で、入力内容のままレコードの追加や更新をしたい場合、テーブルのアクセスメソッドに直接渡せることが多いのです。(もちろんバリデーションが通った場合です。)

一方、項目名を指定してgetで受け取れるのは値だけです。getを使う恩恵は、項目がない場合のデフォルト値を使用できることですが、あまり使い道がありません。

入力を全部受け取った場合、通常その中にはモデルとは関連がないCSRF対策のトークンなどが入り込みます。そこで基本的にonlyメソッドを使用し、受け取る項目を絞りましょう。

$inputs = Input::only( array(取得したい項目名をずらずら) );

$inputsがバリデーションを通ったら、レコードの追加/更新できます。余計な項目が$inputsに入っていなければ、そのまま使用できます。余計なものが入っていたり、足りないと例外が発生します。そのため新しいレコードに追加する項目をさらに絞り込む必要があるのであれば、array_onlyというヘルパー関数を覚えておきましょう。

$data = array_only( $inputs, array(残したい項目名をずらずら) );

Laravel4の場合、様々な便利な機能が増えており、例えばフォームとEloquentモデルを結びつけ、内容を自動的に表示したり、もしくはルートとElquentモデルを結びつけ、指定されたIDのモデルインスタンスを受け取れたりできます。しかし、基本はLaravel3と同じです。

基本、ORMであるEloqunetを使用する

適用:Laravel3、Laravel4

他のフレームワークを使用している人でもEloquentを使用したい人がいるほど簡単で、使いやすくできています。Laravelを使用する理由がこのORMであるという人もたくさんいます。特にLaravel4はコンポーサーに乗り、EloquentをLaravelから切り離し、他のフレームワークで使用する例などが英語のブログには見かけるようになりました。

ORMはテーブルの列とクラスオブジェクトを対応して操作する手法です。簡単に言えばクラスのオブジェクト=モデルでCRUD操作を行います。。

まず、Eloquentモデルは定義が楽です。一応規約は存在しており、テーブル名は英単語の複数形、対応するクラス名は単数形で先頭を大文字にします。

特に複雑なことをするのでなければ、定義はシンプルです。例えばpostsテーブルが存在しており、Eloquentでアクセスしたい場合、application/modelsフォルダーにpost.phpを作成します。

<?php
class Post extends Eloquent {
}

これだけで動作します。

// IDでレコードを取得したければ:
$post = Post::find($id);

// タイトルでレコードを探したいのであれば:
$posts = Post::where('title', '=', $title)->get()
foreach( $posts as $post) {
    echo $post->title;
}

// 一致するものが一件だけだと分かっているのであれば:
$post = Post::where('title', '=', $title)->first();

さらにタイプ量を減らしましょう。最後のコードはFluentクエリービルダーの動的whereを使用し、こうも書けます。

$post = Post::whrere_title($title)->first();

もっと限定するならばwhere_title_and_body('雪', '白い')などの書き方も可能です。余り変態的にするとかえってわかりづらくなりますので、->where()の連鎖でand条件でつなげるか、->or_where()でorでつなげましょう。

OMRはオブジェクトをレコードにマッピングしているという意味です。例えば、新しいレコードの追加はこのモデル=クラスのオブジェクトインスタンスを一つ生成することです。ですからレコードの追加処理の基本はnewで行います。追加する内容が$dataに'カラム名'=>'値'の配列で入っていれば以下の通りオンストラクタに渡すだけです。

$post = new Post($data);
$post->save();

ここでフォームの入力項目名とテーブルの項目名を合わせておく利点がお分かりになったでしょう。一発で代入してくれます。もちろん、複数項目あっても可能です。

ああ、失礼。二行も使ったのでは高速開発はできませんね。では一行で行いましょう。

$post = Post::create($data);

更新処理は更新対象を一度読み込み、その内容を更新する必要がありますので、さすがに一発とは行きません。

$post = Post::find($id); // 取り込む
$post->fill($data); // まとめて代入する
$post->save(); // 保存する

実際は$idで指定したレコードが見つからない場合、$postがnullになるので、それをチェックするのを忘れずに。

まだまだ、関連付けとか便利な機能があります。でも、使えそうな気がしてきたでしょう。ぜひ使ってください。Laravelの高速開発を支えているのがこのEloquentです。基本を抑えるだけで、かなり便利に使用できます。

応用はEloquentを使用しない

ORMはSQLのラッパーとしての性格も持っています。ラッパーである限りどうしても生のSQLより何かしら制限されるものです。

Laravelのリレーションは、JOINを使用しません。関係のあるレコードのIDを"WHERE IN"で連記して引っ張ってきます。その結果、つまりEloquentモデルの配列を取り込みます。

つまり2つのことに注意しておくことが必要です。

  1. 大量のデータを関連付けで引っ張る場合、SQLにはIDが連記されるため、SQL文の長さに制限のあるエンジンを使用している場合、それに引っかかる可能性がある。
  2. 関連付けで引っ張ったレコードは全部メモリ上に乗っかるため、大量/大きなレコードを闇雲に取得すると、PHPなどのメモリ制限に引っかかる可能性がある。

PDOをよく調べていませんが、何かしら行なってくれるかも知れません。よく調べていないのは、初めから上記を意識しているので、無理なコードをEloquentでやろうと努力しないからです。

メモリに引っかからない場合でも、Eloquentを使用する場合、関連付けで引っ張るのがパズルを解くあの楽しみに似ているため、複雑なものでもEloquentでやろうと努力しがちです。私もそうですし、フォーラムにもちょくちょく質問が投げられます。楽しみで組んでいるなら、それも結構。もし、スピード重視なら、あまりElqoeuntにこだわる必要もありません。

テーブル作成時にタイムスタンプを付けておく

Eloquentはデフォルトでタイムスタンプを自動的に更新します。そのフィールドが存在しないと例外が発生します。

基本は付けましょう。テスト、バグ対応の時に役に立ちます。テーブル作成時にcreate_atとupdate_atの2つを作成しておきます。どうしても嫌ならば、モデルのプロパティをfalseにしてください。

public static $timestamps = false;

これは直接開発速度とは関係ありません。しかしハマると時間が取られるので意識しておくべきポイントです。

もしEloquent使用時に、このタイムスタンプ自動更新機能を使用するなら、テーブルに項目を作成しておく、使用しないのならモデルの$timestampsプロパティをfalseにしておくことを覚えておいてください。

フォームと処理を分ける

Laravelではroutes.phpに無名関数を使用し、ルートの内容を定義することも、コントローラークラスを使用することもできます。ルートの数が少ないのでしたら、無名関数だけ使用して構築するのも一手です。これもプロトタイプや小さなサイトを素早く構築する場合の手法として取り入れましょう。

どちらの方法をとっても、フォームとフォームから送られてきた内容の処理は分けましょう。

とりあえず無名関数を使用する方法を採用し、まずフォームを表示しましょう。サクッ。

Route::get('post', function() {
    return view('post');
});

view()ヘルパー関数はView::make()のショートカットです。ここでフォームが表示されることを確認しましょう。

続いてフォームの内容を処理します。サクッ、サクッ。

Route::post('post', function() {
    $rules = array(
        array('title' => 'required|max:30'),
        array('body' => 'required|max:1000'),
    );
    $inputs = Input::only(array('title', 'body'));
    $val = Validator::make($inputs, $rules);
    if ( $val->fails() ) {
        return Redirect::back()
            ->with_input()
            ->with_errors($val);
    }
   // とりあえず今回はレコード追加しておきます。
    Post::create($inputs);
    return Redirect::home();
});

いつも同じパターンで書いているので何も観ずに直接この記事のため入力しました。ですからコード中に間違いがあるかも知れません。でも、このようにさくっとかけるようになります。一つのテーブルの内容のCRUD処理でしたら、ほとんど同じパターンです。

bladeテンプレートを使用する

適用:Laravel3、Laravel4

ビューを書くなら、bladeを書きましょう。簡単です。ファイル名の拡張子を.phpではなく、.blade.phpの二重拡張子にします。

まず、制御構文に関しては<?php ...; ?>と書く代わりに@を先頭につけそのまま制御構文を書きます。

@if ($a === true)
   // 処理
@else
   // 処理
@endif

@foreach( $a as $b => $c)
   // 処理
@endforeach

お察しの通り、正規表現で@ifなら <?php if (...) : ?>に置き換えているだけです。しかも、置き換えた結果はキャッシュされます。細かくパースしているわけでないため、もともと変換スピードは早いのです。

もうひとつ<?php echo ...?>の代わりが{{ ... }}です。これも置き換えられるだけです。このただ置き換えられるという事を理解しておくことが、ブレードが原因で迷う時に解決の手助けになります。ですから心の片隅に置いておくことが重要です。

Laravel3では出力をエスケープするためe()ヘルパーを使用します。他のフレームワークやCMSとおなじですね。Laravel4でもe()は用意されていますが、もっと便利になりましてカギカッコを3重にすると、出力内容がエスケープされるようになりました。

その他、パーシャルビューの取り扱いなどもできますが、とりあえずは上記を覚えておくと、タイプの量がちょっと減らせます。が減らせるとビューのコードが断然見やすくなります。その代わり、大抵のIDEやエディターではPHPの構文でハイライトされなくなるデメリットもあります。

しかしながら、ビューのコードが読みやすくなるため、導入をおすすめします。

フォームに項目の内容を表示する

適用:Laravel3

フォームの入力エリアの典型的なコードは以下の形式です。Bladeを使用しています。

{{ Form::label('title', 'タイトル') }}
{{ Form::text('title', Input::old('title', '')) }}

一番最初のフォーム表示が空フォームの場合でしたらこれでOKですが、更新処理などの場合、通常はテーブルのレコードの内容を表示します。

その場合、ビューに渡してやる方法があります。ビューの表示側ではレコードを取得し、ビューに渡します。

$post = Post::find($id);
return view('post')->with('post', $post);

そしてフォームでは1.前回のフォームの入力内容、2.取得した情報の優先度で表示します。初めてフォームを表示する場合のみ2のパターンで、それ以降は1のパターンになります。それ以降とはバリデーションに引っかかったり、他の原因でエラーが出た場合などですね。フォームへ前回の入力内容を表示し、エラーを示さなくてはなりません。

{{ Form::label('title', 'タイトル') }}
{{ Form::text('title', Input::old('title', $post->title)) }}

この方法では取得した情報をwithを使用しビューに渡しています。また、フォームに入力されたデータとして渡すこともできます。

$post = Post::find($id);
Input::replace( $post->to_array() );
return view('post');

Input::replaceは引数をフォームの入力扱いにしてくれるメソッドです。実際にフォームに直接表示するという意味ではなく、Laravelが受け取ったフォーム入力の値を変更します。ですから、フォームでは通常の入力データを取得するInput::getメソッドを使用し、表示するようにコーディングします。

{{ Form::label('title', 'タイトル') }}
{{ Form::text('title', Input::old('title', Input::get('title'))) }}

お好みでどうぞ。

Laravel4でも基本は同じです。やり方が増え選べるようになったことと、メソッドがキャメル記法になっただけの違いです。

フラッシュデータを理解する

フラッシュデータとは原則、次のリクエストを処理している間のみ存在してる情報のことです。メッセージや入力項目を次のリクエストに持ち越したい場合に使用します。

これにはセッションを使用します。通常セッションに保存したアイテムは自分で消すか、セッションが途切れない限り、持続します。そこで、セッションを保存するときのキーに目印を付け、次のリクエストの最後に目印がついている情報を削除するからくりになっています。(もちろんLaravelにより、自動的に行われます。)

私達開発者から見れば、次のリクエストの間のみ存在している便利な情報というわけです。フラッシュデーターは自分でも書き込むことが可能ですが、大抵Laravelの仕組みに取り込まれているメソッドを使用します。

まず、バリデーションエラーはRedirectに対して->with_errors(バリデターインスタンス)とすると、メッセージがセッションにフラッシュデーターとして書き込まれます。

同じく、今回のフォームの入力値を全部フラッシュデーターとして書き込みたいならばRedirectに対して->with_input()で書き込まれます。

このフラッシュデーターはInput::old()で取得します。

はっきり行ってフラッシュデーターの使用が複雑になっている場合、Laravelについてなにか理解していない可能性が大きいでしょう。私の使用経験と、フォーラムで上る話題による経験則です。ほとんど上記の2つで処理できます。

なお、Laravel4ではwithInput()、withErrors()とメソッド名がキャメル記法になります。

ログインフォームを作成しない

対応:Laravel4

認証を作成するにはどうしてもログインフォームまわりを作成する必要があります。

しかし、プロトタイプの作成目的限定で、Laravel4では基本認証を簡単に使えるようになりました。

ログインユーザーだけに何かを行わせるようなプロトタイプであれば、一分かからずにセットアップできるわけです。

もーすといんぽーたんとしんぐ

公式ドキュメントを最初から最後まで、一度通して読んでください。得てしてベテランほど必要な部分のみしか読まないものですが、肝心なポイントを見落としてしまいがちになります。

新しいフレームワークを学ぶ上で、そのコンセプトをまずそのまま受け取ることは重要です。Laravelの場合、そのコストを払うだけの価値はあります。また簡単に読めますので、時間的にも多くを取られません。