Rのつく財団入り口

最近の本や技術系の話などを書いてます。元はTRPG系サイトの入り口でした。

【感想】PHPフレームワーク Laravel入門

皆さんこんばんは。年末年始は時間もある方はゆっくりネットやTVを見たりしてリフレッシュできたでしょうか。
 気になる異性と日々を共有しちゃう可能性も考慮に入れると、スマホの日記アプリのUIに西暦表示は大事ですね!(入れ替わっちゃう某映画を唐突に語る)

PHPフレームワーク Laravel入門』

 最近の本の感想をば。PHP言語のフレームワークは幾つかありますが、2017年の状況だと世界的にも一番注目を浴びているのがLaravel、国内だと採用実績の多いCakePHPが3も出て根強く、あとはSymfonyなどなどという感じでなのしょうか。

gray-code.com www.webprofessional.jp techacademy.jp itpropartners.com

 これから学ぶなら旬になりつつあるLaravelがアツそうですが、まだあまり本がない中で出たLaravel5.4対応の初心者ターゲットの入門書がこちら。2017/9発行、351ページ、電子書籍はまだなくて青い表紙の本です。

PHPフレームワーク Laravel入門

PHPフレームワーク Laravel入門

兄弟的な本で赤い表紙でCakePHP入門もあります。

PHPフレームワーク CakePHP 3入門

PHPフレームワーク CakePHP 3入門

Chapter1 Laravelを準備する

 そもそもフレームワークとはなんぞや、という基本中の基本から、Laravelや他のフレームワークのこと、パッケージ管理ツールのComposerと、Composerを使ったLaravelのインストール方法が丁寧めに書いてあります。

Chapter2 ルーティングとコントローラ

 以下、配列の宣言と紛らわしいですが日本語の説明を[]内に入れます。
 Laravelでは [アプリケーションルート]/routes/web.phpに書いていく形式です。要は、

<?php

Route::get('[URL/{変数名は最後に?}]'), [無名関数のクロージャで直接書くか、'Controllerクラス@アクションメソッド名'] );
Route::post('[URL/{変数名は最後に?}]'), [無名関数のクロージャで直接書くか、'Controllerクラス@アクションメソッド名'] );

と設定してコマンド実行で

php artisan make:controller XyzController

でApp\Http\controllersにXyzControllerクラスの.phpファイルが自動生成され、アクションメソッドを実装していく。
あるいは、

<?php

Route::get('[アドレス]'), '[コントローラクラス名]');

のような設定にすると「シングルアクションコントローラ」という種類になり、アクションメソッドは__invokeの一種類のみになる。

 HTTPリクエストとHTTPレスポンスは、Illuimnate\Http\Request, Responseクラスにラップされており、アクションメソッドの引数から取れて中のいろんなものを取り出せる。という方式になっています。
 普通のアプリならコントローラクラス必須ですが、簡単な確認には直接web.phpに書く方式も取れるのが柔軟ですね。

Chapter3 ビューとテンプレート

コントローラクラスとテンプレートの連携

 アクションメソッドの最後で

<?php

view('[テンプレートの在り処のフォルダ名.ファイル名]');

とすると、対応するHTMLテンプレートの中身がレスポンスとして返される。ただしviewメソッドの戻り値はHTMLの文字列でなくResponseインスタンス
 そしてコントローラクラス内から変数を渡したい時は、

<?php

$data = [
  'value1' => 'Controllerから渡す値1',
  'value2' => 'Controllerから渡す値2',
];

return view('hello.index', $data);

という方式。変数 $request からも取得可能。という、多くのPHPフレームワークとだいたい同じような方式です。
 ちなみにPHP7がもう必須であることからか、本書のコード例ではすべて配列の宣言には往年の array()でなく []方式を使っています。他の言語から来た人にはこちらの方が分かりやすいですね。

Bladeテンプレート

 Laravelが内部で採用しているHTMLテンプレートエンジン、Bladeの話。MicrosoftASP.NET MVCが採用しているテンプレートエンジンは名前がrazorだったし、何か鋭い名前にするのが流行りなんでしょうか。

{{ 変数 }}
{!! エスケープしないで出したい値!!}

という感じでテンプレート内に書けたり、@ifとか@foreachなど制御構文が使えます。一覧表示の繰り返し処理で$loopという特別な変数に1要素分の情報がいろいろ入っていたり、@actionや@yield、@componentなどを一見アノテーションぽく書いてテンプレート同士も継承関係が持てたり、共通のフッタが書けたりします。
 この辺はPHPのHTMLテンプレートエンジン各種が持っている機能とだいたい同じで、例えば僕も知っているSmartyと大差ない感じです。

ビューコンポーザ

 Laravel独特の機能。コントローラクラスの処理の後、ビューテンプレートの描画の前にちょっとしたロジックを入れたい時がメイン用途のようです。

 入れたい処理を書くのが「ビューコンポーザ」クラスで、artisanコマンドなし、配置場所も自由。普通のPHPクラス。

<?php

function compose(View $view)
{
  // このメソッドを実装すればよい。
}

登録用のクラスが「サービスプロバイダ」。artisanコマンドでServiceProviderを継承したXyzProviderをつくり、boot()メソッドの中に

<?php

View::composer('アドレス', '[登録したビューコンポーザクラスのフル名前空間指定]');

 そして設定ファイルの [アプリケーションルート]/config/app.php連想配列指定部分に、キー 'providers' のところに登録用のサービスプロバイダクラスをフル名前空間で指定。

とするとControllerクラスでは何もせずとも、ビューコンポーザクラス内の処理が自動的に動きます。動的型付け言語のPHPなのにcomposeメソッドの引数はViewという型を指定しているのか?というあたりが気になりますが、フレームワーク側でうまく処理しているようです。

Chapter4 リクエスト・レスポンスを補完する

ミドルウェア

 ミドルウェアというとふつうWebアプリケーションサーバとかDBとかその辺のハードウェア込みの仕組みを連想しますが、Laravelではフレームワークが持っているソフトウェア的な機能です。

リクエスト→対応するアクションメソッド実行→レスポンス

 のアクションの前後に処理を挟み込みたい時に使えます。

artisanコマンドで App\Http\Middleware\XyzMidlerwareクラスが作成。

<?php

function handle($request, Closure $next)
{
  // このメソッドを実装。
  // $request->merge([連想配列]) すると、画面から渡ってきた値が
  // 入っているリクエストの中にデータを追加できる。
  // $response = $next($request); でレスポンスの操作もできる。
  
  return $next($request); // アクション前に処理差し込み
  return $response; // アクション後に処理差し込み
}

[アプリケーションルート]/routes/web.php の各種設定でURLルーティング設定をしているところに、->middleware([ミドルウェアクラス名]::class) のように追加すると、そのアクションメソッド実行の前/後にミドルウェアを実行してくれます。

 1つ1つのアクション個別に設定するのでなく必ず実行したいときは、「グローバルミドルウェア」として登録する方法があります。

  • [アプリケーションルート]\Http\Kernel.php$middleware 変数の配列の中に、ミドルウェアクラスのフル名前空間指定を追加。
  • web.phpのルーティング設定には追加不要。

 ほか、グループに分けて登録し、ルーティングの web.php にグループ名を指定するとグループに属するミドルウェアが全実行されるやり方もあります。

 設定用のスクリプトファイルの名前がKernelなのが面白いですね。
ぱっと思いつくミドルウェアの用途は全アクション共通のロギングや分析用のログ、認証や画面ごとの権限判定などでしょうか。このへんも柔軟に作れるようになっています。

バリデーション

 本格的なWebアプリケーションを作ると何かと問題になるバリデーション。Laravelでは、RubyScalaミックスイン機能のようにクラス内に継承関係なしで処理を埋め込めるPHPの新機能トレイト(Traits)を使い、共通の親Controllerクラスにトレイトでバリデーション処理が埋め込まれています。

 個別の画面に対応したコントローラクラスのアクションメソッド内で

<?php

$validate_rule = [
  '[画面側のname属性値]' => '[定義済みのバリデーションのルール]',
  '[画面側のname属性値]' => '[定義済みのバリデーションのルール]',
];
$this->validate($request, $validate_rule);

とすると実行してくれます。
バリデーションルールに値が必要なときは、'numeric|between0, 100' のようにルールのところに | 区切りで引数的に指定します。

 画面側では $errors という変数にオブジェクトでエラーの集合が入っているので、->all() とか ->first() とか取り出す用の便利なメソッドが揃っています。

HTMLテンプレート側のテキストボックスなどは、value属性値に {{old('[name属性値]')} のように書くと、バリデーションでエラーになった際も見た目上は値が消えずに残ってくれます。このへん地味に必要ですね。

 きちんとクラス設計を見直していくとよく出てくる、バリデーションをコントローラクラスから追い出したい場合はどうするか。
 LaravelではRequest を継承した FormRequet クラスという仕組みを使う方法があります。

  • artisanコマンドでクラス作成。authorize()rules()メソッドを実装する。
  • このauthorize()メソッドはパスによってtrueを返せば許可、falseを返すとフォーム処理が行われず例外発生に行く。
  • rules()メソッドは、戻り値でコントローラクラスに書く時と同じように、以下を書けばよい。バリデーションの実行は特に書かなくても自動的にやってくれる。
<?php

return  [
  '[画面側のname属性値]' => '[定義済みのバリデーションのルール]',
  '[画面側のname属性値]' => '[定義済みのバリデーションのルール]',
];
  • そしてコントローラクラスのアクションメソッドでは引数の$requestを、作ったフォームリクエスト型に変更。戻り値のreturn文に正常時のメッセージを追加。

 また、バリデーションメッセージを日本語化したりカスタマイズしたい時は、フレームワーク側のFormRequest#messeages()メソッドをオーバーライドし、戻り値で

<?php

'[定義済みのバリデーションのルール]' => '[日本語で返したいメッセージ]'

のように上書きすることで可能です。
※ここで本の中だとメッセージの例に「年齢は0~150の間で入力下さい」とありますが、ふつうに考えて0と150の値は何らかの方法で外出ししますね。こういう間違いが時々混じっています。

 それ以外にバリデーションをカスタマイズしたい場合は、Laravel側が用意しているValidatorクラスを使う方法があります。

<?php

$rules = [
  '[name属性値]'=>'[適用したいルール]',
  '[name属性値]'=>'[適用したいルール]',
];
$messages = [
  '[適用したいルール]' => '[返したいメッセージ]',
  '[適用したいルール]' => '[返したいメッセージ]',
];
$validator = Validator::make('チェックしたい値の配列', $rules, $messages);
if ($validator->fails()) {
  // エラーがあるときの処理の分岐
}

という風にコントローラクラス内に書くと細かく指定できます。チェックしたい値の配列は全部にしたり、GETで来た時のパラメータも指定できます。

<?php

return redirect('パス')->withErrors($validator)->withInput();

でフォームの値を持ったまま別画面にリダイレクトさせることもできます。

<?php

$validator->sometimes([name属性値], [適用したいルール], function($input){ [クロージャ内に条件]});

 のようにsometimes()メソッドを使うと、クロージャ内に書いた条件が偽の時だけバリデーションが適用されるようにもできます。

 バリデーションの独自ルールについては、

  • Validatorを継承してXyzValidatorを作り、function validatexyz($attribute, $value, $parameters)を実装。validateの後の文字が自動的にルール名になる。
  • プロバイダクラスのboot()メソッドに追加。
  • そしてコントローラクラスかFormRequestでバリデーションのルールを指定する際にこの独自ルールを書くと、そのルールでバリデーションしてくれる。

 という風に自作もできるようになっています。ほか、CSRF対策やクッキーの読み書き、リダイレクトの仕方などなど。

 実際の経験から言うと、複雑な業務システムでよく実現方法で問題になるのが以下のようなパターンです。

  • ある画面のどのボタンを押したか、権限など現在のユーザの情報によってバリデーションするしないが変わる動的変更
  • 画面内でラジオボタン等でこれを押してる時だけこのバリデーションする/しない、などの動的変更
  • 複数の項目の値が影響する相関チェック
  • フレームワークが用意している以外のルール。このテキストボックスの値はDBのこことチェックするなどのアプリ固有のルール、さらにこれとこれの合計かチェックするなど、機能ごと、画面ごとのルール

 Laravelの場合はバリデーションの実装方法を複数柔軟に持つことで複雑なチェックに対応していますね。それぞれの実装方法が微妙に違って覚えにくいのはありますが、大抵のことには対応できそうです。

 余談ですが僕も仕事で簡単なフレームワークを作ったことがあるのですが、そこでは

  • コントローラクラスのアクションメソッドに来るところで、画面側の各要素の入力値は対応したデータの入れ物クラスに入っているので、そこから値を取得
  • 引数に値の文字列などを取り、チェック結果をtrue/falseで返すバリデート用ユーティリティを用意
  • バリデーションが必要な画面要素は逐一値をこのバリデート用ユーティリティに渡してチェック、引っかかったらエラーメッセージを貯めて~の繰り返し部分を、コード量を減らせるようにある程度自動化...

という、テンプレ―ト側は設定不要、アクションメソッド内に必ずバリデーションの分をPHPコードで書かないといけないがどんな条件や独自チェックが来ても対応可能、という実直な方法を採っていました。
 このへん、やはりOSSフレームワークはもっと凝った高度なやり方で実現していますね。

Chapter5 データベースの利用

 実務でよく出てくるDB周り。本書では接続するRDB例にSQLiteを使っていますが、設定例はMySQL,PostgreSQL, SQLServerも載っているので適宜読み替えて進みます。Oracleは例にないですがOracle Clientのdllを用意して設定すれば繋がるようです。
 基本的にLaravelでのDBアクセスは3種類あって、

①DBクラスを使ってSQLを書く方法。プレースホルダ可能、selectの結果はstdClassの配列。

<?php

$param = ['id' => [セットする値] ];
$items = DB::select('select * from tableName where id = :id', $param);

②クエリビルダを使う方法。DBクラスにテーブル名を指定し、メソッドチェーンでActiveRecordパターンぽく書ける。条件の複数指定、order byなども可能。insert/updateの場合は [カラム名] => [値] の連想配列を渡す。

<?php

$item = DB::table('[テーブル名]')->where('id', [セットする値] )->first();

③ Eloquent ORMを使う方法。次章参照。

と複数用意されています。他、Railsなどでもお馴染みのマイグレーションの機能など。

Chapter6 Eloquent ORM

 一見フランス語か何かかと思いそうなEloquet、英語の形容詞で「雄弁な」、「能弁な」、「表情豊かな」などの意味です。Laravel独自のORM、Eloquetの章。
 ネットの情報だとカタカナ表記は「エロクワント」「エロクヮント」をよく見ますが本書では「エロクアント」で表記されています。(「ワ」「ヮ」の方が実際に近いかな?)
 中身はかのRubyRailsや、他のPHPフレームワーク群、PythonDjangoなどと似た、標準的なActiveRecordパターンのORM。

php artisan make:model Person

のコマンド実行でフレームワーク側のModelクラスを継承したPersonモデルクラスが自動生成。(ここでPersonsテーブルが自動生成されるわけではない)
そしてコントローラクラス内などでこのPersonクラスを使い、

<?php

$items = Person:all(); // 既存レコードのselect
$items = Person::where('[カラム名]', [セットする値])->first(); 

$person = new Person();
$person->prop1 = [セットする値];
$person->fill([連想配列で一気にセット]);
$person->save(); //新規レコードのinsert

$person = Person::find([キー項目で検索]);
// 更新するカラムの値をセット
$person->save(); //レコードのupdate
Person::find([キー項目で検索])->delete(); // レコードのdelete

という塩梅。テーブルが複数ある場合のhasOne/hasManyや副問い合わせなど複雑なSQL文の実現などなどもありますが、他のORMと特に目新しい違いはない感じです。

Chapter7 RESTfulサービス/セッション/ぺジネーション/認証/テスト

RESTfulサービス

WebAPI通信の実装サポートもあります。コントローラクラスの作成の操作で

php artisan make::controller XyzController --resource

と引数をつけると最初からREST用のコントローラになり、web.phpに追加するURLルーティングも専用の1文で済むというもの。これは便利ですね。

セッション

 ここは普通のセッション管理ユーティリティがあるというもの。

ページネーション

クエリビルダ/Eloquent ORMでもやり方は同じで、

<?php

$items = DB::table('[テーブル名]')->.simplePaginate('[1ページの表示件数]');
$items = Person::orderby('age', 'asc')->simplePaginate('[1ページの表示件数]');
$items = Person::orderby('age', 'asc')->paginate('[1ページの表示件数]'); // リンクを出す場合

という風にできます。2テーブル以上を結合した複雑なSQLの検索結果のページネーションはやはり対応していないようです。
 凝った画面だと出てきがちな「前へ」「次へ」のリンク群のカスタマイズ方法も記述があります。

認証

 Laravel側で用意している機能による認証の実装方法。

テスト

 Laravelは最初からphpunitを内蔵しているので、これを使ったユニットテストの入門。テストメソッドはごく基本的なことしか解説していないので、高度なテスト技法は他の本やWebの資料をという流れです。

気になったところ

  • 全体的に初心者向け、Laravelの基本的な使い方に終始しているので、Laravelを巡る背景や文化、他のフレームワークとの違いなどの話はほぼまったく出てきません。
  • staticなメソッドの連続で大抵の機能を呼び出せるファサードの話(GoFデザインパターンのFacadeパターンとはまた違うのがミソ)などもLaravelの特色だと思うのですが、この話も触れていません。このへんの高度な話は『Laravelエキスパート養成読本』を読もうということでしょうか。
  • 準備の説明の画像キャプチャはWindowsですが、テストプロジェクトをデスクトップ直下に作っていたり、フォルダ構成を見せてるところがWindowsエクスプローラの大アイコン表示になっていたり、初心者でない人からするとちょっとイラッとしそうなところがあります。
  • サンプルコードは豊富なのですが、間違いがけっこう多いです。$person = new Person の後の()が抜けてるとか、名前空間指定のuse文が抜けてる/パスが間違ってる、設定用のPHPコードがちょっと違ってる...などなど。PHP経験者や他言語の開発経験者の方なら「たぶんこれ間違いだな」でなんとなく察して進めるのですが、しっかり写経して学びたい初心者さんだと躓くかも。
  • phpunitのサンプルコードでも先頭に不要と思われる$this->assertTrue(true);が何回も残っていたり、ほかの所でもこの行を2回引用しているのはいらんのでは?と思う時があったり、内容の精査がいまいち足りてない印象です。

 作者の掌田津耶乃さんは『Ruby on Rails 5超入門』など様々な言語やフレームワークの入門書を幅広く執筆されていますが、ネットの書評を見るとど~も全般的に誤植が多いようですね。兄弟本ぽい『PHPフレームワーク CakePHP 3入門』も誤字の多さをかなり指摘されています。この本も間違いが多めな時に当たってしまったようです。

まとめ:数少ないLaravel5.4で動くアプリを作れる入門本

 というように間違いが多いのが難点ですが、何せ日本語の新しい書籍がほとんどないLaravelの貴重な入門書。説明自体は丁寧めに書いてあるので、PHP歴の浅い方、フレームワークを活用した開発の初心者の方には、実際にコードを書きながらDBに繋げて動くアプリケーションを作り、学習していく上で助けになるでしょう。

 上述のように背景の説明などはほとんどないので、既にLaravelを使っている方、他のPHPフレームワークの開発経験のある方には、ターゲットの違いからいまいち物足りないところがあるかと思います。『Laravelエキスパート養成読本』か、英語書籍、しばらくは今まで通りネットの情報で頑張るしかないところでしょうか。
 僕もまだ仕事レベルでLaravelを使うまでには至っていないのですが、参考にはなりましたが物足りませんでした。うーん『Rails5アプリケーションプログラミング』や『Angularアプリケーションプログラミング』クラスの網羅性のあるしっかりしたLaravelの本が出てくれないかなあ。

おまけ:ララベル関連本

Laravelエキスパート養成読本[モダンな開発を実現するPHPフレームワーク!] (Software Design plus)

Laravelエキスパート養成読本[モダンな開発を実現するPHPフレームワーク!] (Software Design plus)

 2015年の本でコード例もLaravel4でちょっと古いですが、まずLaravelの周辺情報を広く浅く得るのに最適な本。僕も最初に読みました。

Laravel リファレンス[Ver.5.1 LTS 対応] Web職人好みの新世代PHPフレームワーク

Laravel リファレンス[Ver.5.1 LTS 対応] Web職人好みの新世代PHPフレームワーク

 Laravel5.1対応ですが、この本どうも評価がいまいちであまり見ませんね。

 プログラミングスクールの講師による、Cloud9上の仮想環境を使った学習本。電子書籍でしか手に入りませんが、入門にはよいようです。