Home > PHP
PHP Archive
「レイヤードアーキテクチャを意識したPHPアプリケーションの構築」を発表しました
2015/06/27 に開催された PHPカンファレンス福岡2015 にて、「レイヤードアーキテクチャを意識したPHPアプリケーションの構築」という発表をしてきました。
MVC フレームワーク(CakePHP / Laravel)で構築したアプリケーションをレイヤードを意識して改善したという内容です。参加いただいた皆さんの顔ぶれを見ると歴戦の勇者みたいな方ばかりでしたが、和やかな雰囲気でセッションを進めることができました。ご参加ありがとうございました。
発表資料
発表資料は以下です。
MVC にサービスレイヤを追加して、それぞれの役割を意識して作る。レイヤ間の依存を明確にする。サービス(ドメイン)を中心に考える。よく言われていることなのですが、実際に実践する中で、ハマりがちなことや実際に実践してきた中で感じたことを紹介しました。もちろん、これで ok ということはないので、今後取り組んでいきたい内容も取り上げています。
MVC フレームワークを使っていて、ある程度複雑なアプリケーションを作っている人なら、ぶつかる部分だと思うので、参考になれば嬉しいです。
あなたのアーキテクチャの話が聴きたい
発表の最後に、このメッセージを伝えました。
PHPのイベントでは、色々な種類の発表があるのですが、意外と今回のようなアーキテクチャや設計の話は少ないように思います(実際、PHPメンターズ 以外ではあまり見ない気がします)。
誰もが、アプリケーションを実装する際は、なんらかの設計をしているわけで、そこでは数多ある方法から取捨選択しているわけです。その形が、枯れた方法であったり、最近キャッチーな内容ではないかもしれません。いや、むしろその方がほとんどだと思います。
ただ、こうした話こそ、現場で戦っている人にしかできないことで、それが聞けるのが、勉強会やカンファレンスのようなイベントの醍醐味でもあります。現場で実践した内容は(そのまま取り込むかは別にして)多いに参考になります。
設計には、これという正解があるわけで無く、それぞれのプロジェクトのコンテキストを理解していないと伝わらない部分が多いです。また、変なことを言うと、批判されるのでは、という思いもあるかもしれません。
これまで、私も、わざわざ話すほどでも無いなあと思い、こうした内容の発表は LT 以外ではしてきませんでした。ですが、今回、実際にやってみると多くのフィードバックを得ることができました。
「参考になりました!」という方もいれば、「内容には同意しないけど、違う意見が聴けて面白かった。」という方もいました。そして、多くの方から「自分は、こう考える、こうしてる。」といった考えを聞くことができました。情報は発信する人に集まる、と良く言われていますが、まさにそのとおりになりました。
今回の内容は、以前書いたエントリや Laravel エンジニア養成読本に書いたものがベースになっているのですが、こうしたアウトプットの事前準備において、普段やり過ごしていたポイントに気付くなど多くの発見がありました。
アーキテクチャや設計に関する話は、フレームワークや言語すらを超えて、共通の話が多く、色々な人で共有できます。もっと、PHP コミュニティでも、こうしたセッションが増えるといいですね!
さいごに
PHP カンファレンス福岡は、初回にも関わらず、盛況で、とても良いイベントでした。セッションも、スピーカーの方の経験や考えを話すものが多く、そこでしか聞けないものばかりで、とても有意義なものでした。
最後の委員長お二人の挨拶でホロッとさせられたのも、生な感じで良かったです:)
スタッフの皆さん、関係各位の皆さん、本当に良いイベントをありがとうございました。
- コメント (Close): 0
- トラックバック (Close): 0
「認証機能に学ぶ Laravel 5 アプリケーション」を発表しました #phpkansai
第21回関西PHP勉強会にて、「認証機能に学ぶ Laravel 5 アプリケーション」というタイトルで発表しました。
Laravel 5 アプリケーションの作り方を知る題材に
Laravel 5には、認証周りの機能がデフォルトプロジェクトに含まれています。そのまま利用できて便利というのはもちろんなのですが、はじめから存在する Laravel アプリケーションのサンプル実装という見方もできるので、これを題材に、アプリケーションの実装で活用できるノウハウをお話しました。
発表資料は、以下です。
勉強会では、これから Laravel を触るという方が多かったので、少しイメージしにくい部分があったかもしれません。今後、Laravel を使っていく中で、ヒントとなることもあると思うので、いつか思い出して頂けると嬉しいです。
これから Laravel を触るなら
ちょうど良い本が今日発売したので、よろしくお願いしますm(_ _)m
Laravel 4がベースですが、こちらで雰囲気掴んで貰えれば、Laravel 5の習得もスムーズですね。
- コメント (Close): 0
- トラックバック (Close): 0
一冊まるごと Laravel な「Laravelエキスパート養成読本」が出ます
「Laravelエキスパート養成読本」が、2015/04/21に出版されます。この本の執筆に参加しました。
昨今、話題のフレームワーク Laravel ですが、ようやく日本語での書籍が登場します。これまで、和訳された電子書籍の本はあったのですが、紙の書籍としては、国内初となります。
目次
目次は、下記のようになっています。バージョンは、Laravel 4 をベースにしており、新機能紹介として、Laravel 5 の内容を加えています。
Chapter1. Laravel をはじめよう Chapter2. MVC モデルが基礎からわかる Chapter3. IoC コンテナ、ファサード、サービスプロバイダ、Eloquent Chapter4. Laravel 5 新機能紹介! Special. PHP フレームワーク最新事情 Chapter5. 実践!REST API アプリケーション
Laravel 公式のドキュメントは、丁寧に書かれており、知りたい機能の解説が分かりやすく書かれています。
ただ、リファレンスとしては分かりやすいドキュメントなのですが、これから Laravel を始める人が網羅的に各機能を知っていくには、やや足りない部分があります。
本書では、Laravel とは、から始まり、Laravel を使って、Webアプリケーションを作る流れを見ていきます。それをベースに、より深いフレームワークの機能や最新版である Laravel 5 の新機能を学んでいきます。さらに、Laravel 以外のフレームワークを見て、Laravel の立ち位置を知ります。最後に、応用編として、REST API アプリケーションを題材に、現場での活用方法を知るという流れです。
この本で、Laravel でアプリケーションを作るという概念を理解していけば、より深い部分は、公式ドキュメントやフレームワークのソースから学んでいけるでしょう。
実践!REST API アプリケーション
私は、「Chapter5. 実践!REST API アプリケーション」を担当しました。
5-1. Laravel で作る REST API アプリケーション 5-2. ルーティング 5-3. コントローラ 5-4. サービス、モデル 5-5. テスト
Laravel の基本的な内容は、他の章で解説されているので、機能の紹介というよりは、実際のアプリケーション開発では、どのように機能を利用していくかという視点で書いています。私自身、Laravel 4で、AngularJS やスマートフォンアプリと連携する REST API を開発した経験を元に書いているので、現場で活用してもらえると嬉しいです。
以前、書いた自分流 Laravel 4 アプリケーションアーキテクチャをベースにしており、独自ディレクトリ構成や、レイヤードアーキテクチャを紹介しています。PHPUnit を使ったファンクショナルテストにも触れており、Laravel でテストを書く際は参考になると思います。
本章では、サンプルアプリケーションを元に解説しており、アプリケーションのコードは、Github 上で公開しています。
https://github.com/shin1x1/gihyo-laravel-book-reservation
Vagrantfile と Ansible によるプロビジョンファイルを同梱しているので、Virtualbox と Vagrant があれば、サンプルアプリケーションが動かすことができます。
予約お待ちしています!
すでに、Amazon でも予約受付が開始されています。
これから、Laravel を触ってみようかなという方はもちろんのこと、現在 Laravel を利用している方にも参考になる内容だと思いますので、ぜひ手に取って頂ければ嬉しいです!
Laravelエキスパート養成読本[モダンなPHP開発を実現するノウハウ満載!] (Software Design plus)
なお、共著者である @hirokws さんの呼びかけで、本書の印税の一部を Laravel プロジェクトへ寄付するつもりです。ありがとう、Laravel。
- コメント (Close): 0
- トラックバック (Close): 0
「Azure Websites で作るスケーラブルな PHP アプリケーション」を GoAzure 2015 で発表してきました
Azure コミュニティの一大イベント GoAzure 2015 にて、「Azure Websites で作るスケーラブルな PHP アプリケーション」という発表を行ってきました。
セッションでもお話したのですが、テーマは、もう一つあり、それは「LAMP(LAPP) ユーザが使う Azure Websites」でした。
Azure は、マイクロソフトのサービスなので、どうしても Windows に最適化されているイメージがあったのですが、普段 Mac で開発して、Linux にデプロイして、運用している人間でも、興味深いサービスであることを伝えようと考えました。
発表資料
発表資料は、こちらです。
発表冒頭で、Azure Websites を使っている方に挙手頂いたのですが、6-7 割の方から手が上がりました。Azure のイベントなので、当然といえば、そうなのですが、利用されているのが多いのだなと実感しました。
PHP で PaaS を使う
以前から、PHP を公式にサポートする PaaS はいくつかあったのですが、海外のサービスが多く、ドキュメントも英語版のみで、日本ではあまり使われていない印象でした。
PaaS の雄である Heroku が、2014 年に正式に PHP に対応したことで、今後、PHP でも PaaS を使うことが広がっていくでしょう。(Heroku も公式サイトは、英語ですが、日本語のブログ記事や書籍があります。)Heroku は、PaaS として非常によく出来ているのですが、残念ながら、日本国内にサーバがありません(2015/01/19現在)。
その点、Azure Websites は、日本国内(東日本と西日本)にサーバがあり、日本語のドキュメントもあるので、PHP で PaaS を使うなら、まずは試してみる価値はあります。
Azure Websites + PHP + New relic が利用できない
発表では、入れ忘れたのですが、困ったことを一つ。
Azure Websites は、Windows Server なので、Windows 版 PHP 用の DLL が無い拡張は利用することができません。主要な拡張は問題無いと思うのですが、私が触った範囲では、一つだけこの問題に当たりました。
それが、New Relic の PHP 拡張です。
Azure 自体は、New Relic と連携ができ、.NET 環境では利用できるようなのですが、New Relic の PHP 拡張は、Windows 版が無いため、利用することができませんでした。
New Relic for PHP の FAQ でも現在は対応されていないことが明示されています。New Relic は便利なので、ぜひ対応してもらいたいところです。
https://docs.newrelic.com/docs/agents/php-agent/getting-started/new-relic-php-faqs#windows
アーキテクチャを明示して欲しい
発表資料には入れていますが、一つの Websites 内にある各インスタンスでは、ストレージ(D ドライブ)が共有されているようです。
検証した結果では、おそらくそうであろうということは分かっていましたし、そうであるという情報も見聞きすることができました。
ただ、最後の裏取りとして、公式のドキュメントで、このことが明示されている箇所を探したのですが、残念ながら、見つけることができませんでした。
PaaS だから、裏側を知らなくても良いだろう、という考え方なのかもしれませんが、アプリケーションを作る上でも、構成を組むにしても重要な部分なので、はっきりと公式サイトで明示して欲しいと思いました。
実際、私自身も検証の段階で、このことを知らずに、想定したような結果が出ずに首を傾げるということを経験しました。
せっかく、共有ストレージのおかげで、スケールアウトが容易という特徴があるのですから、ちゃんと明示した方が双方にメリットがあるのではないでしょうか。
追記:2015/01/24
こちらの記事で共有ストレージであることが記載されているようです。https://msdn.microsoft.com/ja-jp/magazine/dn786914.aspx
さいごに
冒頭でも触れましたが、Azure は、以前は「Windows Azure」という名称だったため、どうしても「Windows ユーザのためのもの」という印象がありました。ただ、今回 Azure Websites を検証してみたところ、あくまでマイクロソフト社が運営しているというだけで、クラウドサービスの一つであるという認識に変わりました。
OSX や Linux 上でも動くコマンドラインツール(npm でインストール)があり、Azure Websites では、Git でデプロイができます。デプロイ時に任意のスクリプト(Bash)を実行することができるので、Composer やフレームワークでのマイグレーションなども自動化できます。
上手くやれば、わりと活用できるシーンはあると思うので、今後も使ってみたいと思います。
無料枠もあるので、まだの方は、ぜひ一度試してみてください。
- コメント (Close): 0
- トラックバック (Close): 0
2014 年に発表したセッションと資料まとめ
2014 年も残すは、あと 1 週間になりました。今年も様々なイベントで登壇しましたので、発表したセッションと資料をまとめてみます。
写真提供:久岡写真事務所
登壇イベント
2014/04/04 「わかってるフレームワーク Laravel」Laravel勉強会福岡
「わかってるフレームワーク Laravel」とうタイトルで発表しました。
Laravel で、とあるプロジェクトの開発が終わった後だったので、Laravel への良さを主観たっぷりでお話しました。
翌日の Laravel Meetup Tokyo と合わせて、一人 Laravel Japan ツアーでした:D
2014/04/05 「知っておくべき Auth オートログイン」 Laravel Meetup Tokyo vol.3
Laravel 4.1.25 以前にあった Auth フィルタ利用時のオートログインの問題点についてお話しました。
いくつかの前提条件は必要ですが、影響度が大きいので、「これはやばい」というのが伝わっていました。
なお、この問題は、4.1.26 にて修正されたので、現在は問題ありません。
Laravel ユーザなら知っておくべきAuthオートログインのこと
2014/04/12 「Vagrant ユーザのための Docker 入門」 第3回コンテナ型仮想化の情報交換会@大阪
Docker が盛り上がってきた時だったので、Vagrant ユーザを対象に Docker 入門をお話しました。
セッション途中でも活発に質問が飛び込んできたり、デモでコンテナ起動の速さに驚きの声があがったりで、盛り上がったのを覚えています。
「VagrantユーザのためのDocker入門」を発表してきました
2014/04/19 「最近なんだか、はてブがおかしい」俺聞け8 in Tokyo
資料未公開
当初は「Webエンジンアとしてご飯を食べていく」という内容を考えていたのですが、事前に、主催の @msng さんから「ブログに関する発表が多くて嬉しい」というコメントがあったので、ブロブに寄せようと内容を変えました:)
会場には、著名なブロガーの方が多く、みなさん同じようなことを感じているようで、発表後の情報交換が捗りました。
2014/04/24 「Vagrant 体験入門」DevLove関西
これから Vagrant を学ぶ方向けに、Vagrant 概要についての発表と体験ハンズオンを行いました。
Vagrant ハンズオンでは、みんなで同一拠点から一斉にプロビジョンを行うので、遅延やエラーが起こったり、環境の違い(Vagrant + VirtualBox が起動するまでの)でトラブルが発生したりで、毎回発見があります。
ハンズオンの資料は Qiita に公開しています。
Vagrant体験入門ハンズオン手順 – 2014/04/24 DevLove関西
2014/06/19 「Heroku で作るスケーラブルな PHP アプリケーション」第16回関西PHP勉強会
Heroku で PHP が正式サポートされたので、スケーラブルな構成をどう組むかという内容でお話しました。
その後、いくつかのプロジェクトで Heroku + PHP を使っているのですが、やはり PaaS として良くできていますね。まだ掴みきれていない部分もあるので、引き続き追いかけていきます。
Heroku で作るスケーラブルな PHP アプリケーション
2014/06/28 「PHPコードではなく PHPコードの「書き方」を知る」PHPカンファレンス関西2014
PHPカンファレンス関西の初心者向けセッションということで、FizzBuzz を題材に、関数化、クラス化、そして自動テストを書くという流れをお話しました。
「PHPコードではなくPHPコードの「書き方」を知る」を発表してきました
2014/07/05 「開発現場で活用する Vagrant」夏の JAWS-UG 三都物語 2014
関西での JAWS-UG イベント、三都物語でのセッションでした。JAWS-UG では久しぶりの発表になりました。
Vagrant を実際に使う例として、デモで実演しながらセッションを進めました。
会場がフランクな雰囲気で、話しやすかったのが印象的でした。あと Yo を使って、バーチャル拍手というかうなずきを送ってもらったりもしましたね。
2014/08/23 「Vagrant 心得 5 ヶ条」DevLove 甲子園 2014 西日本大会
資料未公開
DevLove 甲子園にて、Vagrant についてお話しました。ここでは、Vagrant を活用する上で、気をつけておくと良いことを 5 つにまとめました。
内容は、いずれ blog に書きたいと思います。
2014/10/11 「Ansible ではじめるサーバ作業の自動化」PHPカンファレンス 2014
PHP カンファレンスで、Ansible についてお話しました。発表を聴いた方からは、好意的なフィードバックが多く、「Chef を使っていましたが、Ansible 使ってみます!」という意見もあり、良かったです。
準備している時は、話したいことをどう上手くおさめるかを苦心したので、普段スピーカーをしているコミュニティの仲間から「うまくまとまっていましたね」と言ってもらえ、やはり見ている人には分かるんだなあと感じたりもしました。
このセッションを見た方から、別のイベントでの登壇についてお誘いがあったり、次につながったセッションでもありました。
「Ansibleではじめるサーバ作業の自動化」を発表してきました
2014/12/19 「ビルドサーバで使う Docker」DevLove 関西
年内最後の発表は、やはり Docker でした。今年は、ほんと Docker が躍進した一年でしたね。
Jenkins サーバでの活用例から、導入のポイントや設定方法などをお話しました。
「Jenkinsサーバで使う Docker」を発表してきました
ロクナナワークショップ 「Laravel で学ぶモダン PHP 開発講座」
11 月から、ロクナナワークショップさんにて「Laravel で学ぶモダン PHP 開発講座」という講座を行っています。
みっちり 1 日で、Vagrant や Composer の使い方、Laravel を使った REST API の実装、そして、その自動テストを書くという内容です。
すでに 2 回開催しているのですが、参加された方は熱心に課題に取り組まれていて、講師としても勉強になることが多いです。まとまった時間でじっくり課題をこなしていくので、これから学んでいこうという方にはおすすめです。
次回は 2015/02/17 に開催予定ですので、もし興味がある方は、ご参加下さい。
さいごに
今年を振り返ってみると、自分で名乗り出るよりも、お声がけ頂いて、登壇するという機会が増えました。お声がけ頂けるのは嬉しく、スケジュールやタイミングが許せば、できるだけお引き受けしたいです。
人前で話すというのは、準備も大変ですし、いつも緊張しますが、毎回色々な発見があり、また、終わった後は何ともいえない解放感というか充実感を感じることができます。聴いて頂いた方からのフィードバックも嬉しいものです。
今年も、セッションに参加して頂いた方、イベントにお声がけ頂いた方、本当にありがとうございました。
2015 年は、年始の 1/16 に GoAzure 2015 で登壇する予定です。Websites でスケーラブルな PHP アプリケーションを作るという内容ですので、ご参加お待ちしています!
- コメント (Close): 0
- トラックバック (Close): 0
Docker Hub 公式 PHP イメージで、複数バージョンの PHP コンテナを起動する
Docker Hub の 公式 PHP イメージで、PHP 5.4, 5.5, 5.6 のコンテナを起動します。
PHP Advent Calendar 2014 の 12 日目です。昨日は、PHPで簡単に華麗にDIとAOPをキメる でした。
Docker Hub には、公式でミドルウェアや言語のイメージがで公開されています。PHP もその一つで、現在は、5.4, 5.5, 5.6 が公開されています。また、バージョン毎に複数のタグが存在し、cli / fpm / apache(mod_php) といた構成違いのものが存在します。
https://registry.hub.docker.com/_/php/
ここでは、5.x-apache
タグのイメージを使って、複数バージョンのコンテナを起動して、ブラウザから、同一コンテンツを異なる PHP バージョンで確認できるようにします。
boot2docker のインストールと起動
Docker の実行環境として、boot2docker を使いました。
pkg ファイルが公開されているので、これをダウンロードして、インストールすると良いです。
http://docs.docker.com/installation/mac/
インストールできたら、boot2docker を起動しておきます。
$ boot2docker init $ boot2docker up
boot2docker の VM が起動したら、IP アドレスを確認しておきます。
$ boot2docker ip The VM's Host only interface IP address is: 192.168.59.xxx
公式 PHP イメージでコンテナを起動
Docker Hub 公式の PHP イメージを使って、5.4, 5.5, 5.6 の Docker コンテナを起動します。5.4-apache
/ 5.5-apache
/ 5.6-apache
のイメージは、Apache + mod_php の構成になっており、手軽に Web アプリケーションを実行できます。
実行するコンテンツは、OSX 上にあるファイルを利用します。boot2docker では、1.3 から shared_folder で、OSX の /Users
のディレクトリを、VM から /Users
として参照することができます。これにより、VM の docker コンテナからシームレスに OSX のディレクトリをマウントすることができます。
コンテンツは、単純に PHP バージョンを出力するだけです。
$ cat /Users/path/to/index.php <?php echo 'PHP ' . phpversion();
バージョン毎にポートを分けて、以下のようにコンテナを実行します。初回は、各イメージをダウンロードするので時間がかかります。次回以降は、即座に起動します。
$ docker run -v /Users/path/to:/var/www/html -p 8084:80 -d php:5.4-apache $ docker run -v /Users/path/to:/var/www/html -p 8085:80 -d php:5.5-apache $ docker run -v /Users/path/to:/var/www/html -p 8086:80 -d php:5.6-apache
ブラウザから確認
では、ブラウザから起動した Docker コンテナにアクセスします。ポートを変えて、アクセスすると、それぞれの PHP バージョンが出力されます。
- http://boot2docker-vm:8054/
PHP 5.4.35
- http://boot2docker-vm:8055/
PHP 5.5.19
- http://boot2docker-vm:8056/
PHP 5.6.3
さいごに
Docker Hub 公式の PHP イメージを使って、複数バージョンの PHP を起動しました。
このイメージは、使ってみると分かるのですが、拡張は必要最低限となっており(mbstring も pdo_mysql / pdo_pgsql もありません)、実際のアプリケーションを動かすには、拡張を追加して、ビルドする必要があります。
もちろん、こうした状況は考慮されており、イメージには docker-php-ext-install
という拡張を追加するコマンドが用意されています。
実用する場合は、公式イメージをそのまま使うのではなく、これをベースに Dockerfile を書き、独自に拡張を追加したイメージを使うという方法になりますね。
明日の PHP Advent Calendar 2014は、ngyuki さんです。お楽しみに!
- コメント (Close): 0
- トラックバック (Close): 0
自分流 Laravel 4 アプリケーションアーキテクチャ
Laravel Advent Calendar 2014 の 9 日目です。
今年の Advent Calendar では、Laravel 5 リリース目前ということで、Laravel 5 の話題が多いのですが、それは他の方にお任せして、ここでは、Laravel 4 でのアプリケーション実装について書いてみます。
Laravel は自由度の高いフレームワークですので、アプリケーションも自由な構成にすることができます。ただ、この「自由さ」が故に、どういう構成が良いのかというのが悩ましい点でもあります。
このエントリでは、私が実際に構築したプロダクトをベースに構成例をご紹介します。Laravel アプリケーションを構築する上での参考になれば嬉しいです。
1. ディレクトリ構成
まずは、大枠のディレクトリ構成から。アプリケーションや開発環境用の Vagrantfile やプロビジョンファイルなどは、Git リポジトリで管理しています。
ディレクトリ構成としては以下になります。
Dockerfile
は、CI サーバで Docker を使って、自動テストを行っているので、そのためです。
Vagrantfile
は、開発環境用の VM を管理するためのものです。アプリケーションディレクトリにルートに Vagrantfile を置いておくと、アプリケーション内のどのディレクトリからでも vagrant
コマンドできるので便利です。
provision/
は、開発環境、上記 CI サーバでの実行環境、ステージング環境用のプロビジョンファイルが入っています。プロビジョンには、Ansible を使っています。本番環境は、構成管理はこちらでは行わないので、デプロイ用の playbook が格納されています。
src/
が、Laravel アプリケーションのコードが入っているディレクトリになります。以下では、この src/ について見ていきます。
. ├── Dockerfile ├── README.md ├── Vagrantfile ├── docker/ ├── provision/ └── src/
1-1. src/ ディレクトリ
src/
の構成は以下です。
このアプリケーションは、Laravel で REST API を実装して、AngularJS で実装したクライアント側と連携して動作するアーキテクチャになっています。また、クライアント側のコンポーネントの管理に、bower を使っており、そのためのファイルやディレクトリがあります。
Laravel 自体は、ベーシックな構成ですが、アプリケーション独自のコンポーネントを格納するために、package
というディレクトリを作っています。これについては、後述します。
. ├── CONTRIBUTING.md ├── Gruntfile.js ├── _ide_helper.php ├── app/ ├── artisan ├── bootstrap/ ├── bower.json ├── bower_components/ ├── composer.json ├── composer.lock ├── karma.conf.js ├── package/ ├── package.json ├── phpunit.xml ├── public/ ├── readme.md ├── server.php ├── spec/ └── vendor/
2. Laravel アプリケーション の役割
Laravel アプリケーションでは、主に以下の役割を担っています。
- HTML の生成、出力
- JavaScript(AngularJS アプリケーション) / CSS などの生成、出力
- REST API の提供(AngularJS アプリケーションと連携)
初期のビューは、Laravel で生成、出力して、あとは、AngularJS + REST API で連携して動作する SPA(Single Page Application) の形です。このアプリケーションでは、特定ユーザのみが利用するものなので、認証を行い、認可されたユーザのみがアプリケーションを利用できるように、ビューの出力も Laravel で行っています。
ほとんどの機能は、REST API にて提供されるので、以降はこの部分について書いていきます。
3. 処理フロー
REST API への HTTP リクエストは、概ね、以下のフローで処理されます。
- Routing – リクエストされた URI から、処理すべきハンドラを決定します。通常は、コントローラのメソッド、簡単な処理ならネイティヴクロージャに処理を委ねます。
- Controller – 処理の起点となります。バリデーションを行い、正常値であれば、サービスクラスのメソッドに値を渡して、実行します。
- Service – 実処理を行う部分です。ビジネスロジックを実行し、Model(Eloquent)を使って、データベースへの操作などを行います。
- Model(Eloquent) – Eloquent を継承したクラスです。データベースへのアクセスを担うとともに、エンティティ固有の処理なども行います。
各フェーズ(レイヤ)では自身の責務に注力すればよく、上位のフェーズを関知する必要はありません。(必要な情報は受け渡されます。)
HTTP レスポンスボディは、JSON で返すので、blade などのビューテンプレートは、ここでは利用しません。
4. アプリケーションクラスの構成(namespace, autolaod)
ルーティング以外のクラスは、app/
以下ではなく、package/
以下に配置しています。また、namespace で、独立したパッケージとして分離しています。
アプリケーションをフレームワークの構成から独立したパッケージとすることで、フレームワークの一部としてアプリケーションを構成するのではなく、アプリケーションの一部としてフレームワークを利用することをイメージしています。
package/ └── Foo/ ├── DomainA │ ├── Command/ <--- CLI Command │ ├── Controller/ <--- Controller │ ├── Model/ <--- Model(Eloquent) │ ├── Service/ <--- Serivice │ ├── Test/ <--- Test │ └── Validation/ <--- Validation ├── DomainB │ ├── Command/ │ ├── Controller/ │ ├── Model/ │ ├── Service/ │ ├── Test/ │ └── Validation/ OneByOne/ └── Laravel ├── Controller/ ├── Exception/ ├── Service/ └── Test/
namespace のトップレベルには、アプリケーション名(Foo)を、次に、ドメインパッケージ名(DomainA / DomainB)が続きます。アプリケーション内でも業務などのドメイン毎にパッケージを分けています。さらに、それぞれのドメインごとに、Controller や Model など役割に応じた namespace を割り当てています。
例えば、Foo システムの DomainA の Controller にある SampleController であれば、Foo\DomainA\Controller\SampleController
となります。
ここでは、Foo システムの他に、汎用コンポーネントを集積した OnebyOne システムも同梱しています。
アプリケーションクラスは、composer.json
にて、PSR-4 を使って、オートロードで読み込めるようにしています。Laravel におけるアプリケーションは、オートローダで読めれば、どこに配置しても良いので、こうした柔軟な構成が可能です。
- composer.json
{ "autoload": { "psr-4": { "Foo\\": "package/Foo" "OneByOne\\": "package/OneByOne" } }, }
5. Routing
ルーティングでは、URI と処理を行うコントローラのメソッドを Route::verbs()
で連結するだけです。
一部、単純にデータベースから値を読み込んで、JSON として返す程度のものは、ネイティヴクロージャで実装しています。
認証が必要な API については、認証用のフィルタを実装しておいて、それを、Route::group()
で指定します。Route::group()
のネイティヴクロージャ内に認証が必要な API の定義を記載します。
Route::when()
では、POST
, PUT
, DELETE
リクエスト場合は、csrf フィルタを適用するようにしています。
Route::pattern()
では、/foo/{id}
の {id}
を受け入れるパターンを定義していまます。こうしておけば、各ルーティングで、正規表現を定義する必要がなく、便利です。
- app/config/routes.php
<?php use Foo\DomainA\HogeController; $apiPrefix = '/api/v1'; // csrf filter Route::when('*', 'csrf', ['POST', 'PUT', 'DELETE']); // patterns Route::pattern('id', '[0-9]+'); Route::group( ['before' => 'auth'], function () use ($apiPrefix) { $controller = HogeController::class; Route::get($apiPrefix . '/foo', $controller . '@read'); Route::post($apiPrefix . '/foo', $controller . '@create'); Route::put($apiPrefix . '/foo/{id}', $controller . '@update'); Route::delete($apiPrefix . '/foo/{id}', $controller . '@delete'); } );
6. Controller
コントローラでは、入力値のバリデーション、サービスクラスの実行、そして、HTTP レスポンスを構築します。
下記は、典型的なコントローラの例です。
コントローラクラスでは、フレームワークの Contoller クラスを継承しています。あと、API 共通処理(JSON レスポンス等)を ApiControllerTrait
というトレイトに実装しているので、これを use
しています。
- package/Foo/DomainA/Controller/SampleController.php
<?php namespace Foo\DomainA\Controller; use Foo\DomainA\Service\SampleService; use Foo\DomainA\Validation\SampleValidatorBuilder; use OneByOne\Laravel\Controller\ApiControllerTrait; class SampleController extends Controller { use ApiControllerTrait /** * @var SampleService */ protected $service; /** * @param SampleService $serivice */ public function __contruct(SampleService $service) { $this->service = $service; } public function create() { // validation $validator = (new SampleValidatorBuilder())->create(Input::all()); if ($validator->fails()) { return $this->validationError($validator->messages()); } $this->service->create(Input::all()); return $this->created(); } }
下記が、ApiControllerTrait の一部です。ここでは、JSON レスポンスを返すメソッドをまとめています。
- package/OneByOne/Laravel/Controller/ApiControllerTrait.php
<?php namespace OneByOne\Laravel\Controller; use Response; use Symfony\Component\HttpFoundation\Response as Res; trait ApiControllerTrait { /** * ok * * @param mixed $message * @return Illuminate\Http\JsonResponse */ public function ok($message = '') { return Response::json($message); } /** * validationError * * @param mixed $messages * @return Illuminate\Http\JsonResponse */ public function validationError($messages = []) { return $this->badRequest('validation', $messages); } /** * BadRequest * * @param string $error * @param array $messages * @return Illuminate\Http\JsonResponse */ public function badRequest($error = null, $messages = []) { $response = [ 'error' => $error, 'messages' => $messages, ]; return Response::json($response, Res::HTTP_BAD_REQUEST); } (snip) }
6-1. サービスクラスのインジェクト
SampleController に戻ります。コンストラクタでは、処理を担うサービスクラスを引数に取ります。コントローラは、フレームワークによって、インスタンス化されるので、Laravel の IoC コンテナにより、SampleService クラスのインスタンスが自動でインジェクトされます。
もちろん、テストなどの際は、自らインスタンス化して、モックオブジェクトを差し込むことも可能です。
6-2. バリデーション
ルーティングによりリクエスト URI に合致したメソッドが実行されます。
ここでは create
メソッドが実行されるとします。コントローラのメソッドでは、まず、入力値のバリデーションを行います。
SampleValidatorBuilder クラスにて、バリデーションクラスのインスタンスを生成します。
SampleValidatorBuilder の実装例は下記です。バリデーションクラスの生成(ルールの設定)をクラス化しておくことで、コントローラからバリデーションルールの設定を逃がすことができ、同じルールを異なる箇所で共有できます。当然、テストからも、バリデーションクラスが生成できるので、バリデーションのテストが書くのが容易です。
このバリデーションビルダのインターフェースとテスト用のトレイとをまとめて、packagist に公開しています。
shin1x1/laravel-validator-builder
- package/Foo/DomainA/Validation/SampleValidatorBuilder.php
<?php namespace Foo\DomainA\Validation; use Shin1x1\ValidatorBuilder\ValidatorBuilder; use Validator; /** * Class SampleValidatorBuilder * @package Foo\DomainA\Validation */ class SampleValidatorBuilder implements ValidatorBuilder { /** * @param array $inputs * @return IlluminateValidationValidator */ public function create(array $inputs) { return Validator::make( $inputs, [ 'name' => 'required', 'email' => 'required|email', ] ); } }
6-3. サービスクラスの実行
入力値が妥当なものであれば、サービスクラスのメソッドを実行します。
サービスクラスのインスタンスは、コントローラのコンストラクタでセットされているので、これを利用します。
処理が正常であれば、created
メソッド(ApiControllerTrait に実装)を呼び出して、201 Created
を返します。
現在は、コンストラクタインジェクションでサービスクラスをインジェクトしているので、1コントローラ=1サービスの形が多いです。(App::make() などで、メソッド内で、サービスクラスのインスタンスを取得する場合もあります。)
6-4. Laravel 5 でのメソッドインジェクション
今後の話ですが、Laravel 5 では、コントローラのメソッドについても、IoC コンテナによる DI が可能になります。これにより、各アクションメソッド毎にサービスやバリデーションビルダがインジェクトできるので、より使いやすくなります。
7. Service
サービスクラスでは、ビジネスロジックなどのドメイン固有の処理を実行します。フレームワークのクラスは継承せず、単なる POPO として実装しています。
ユースケースに対応して実装することが多いので、名称から内容が想起できるもにしています。例えば、予約を扱うアプリケーションなら、ReservationService をクラス名とし、各メソッド名は、予約業務のユースケースである「予約する(book)」「予約を更新する(update)」「予約を取り消す(cancel)」としています。
下記では、Bar クラスのインスタンスを作って、入力値をデータベースに保存しています。
入力値のバリデーションは、コントローラで終わっているので、このクラスで処理する場合は、引数は正当な値であるとみなします。ただ、値の整合性(一意制約などのアプリケーション制約)など、事前条件のチェックはここで行います。もし事前条件に違反している場合は例外を投げて処理を中断します。
- package/Foo/DomainA/Service/SampleService.php
<?php namespace Foo\DomainA\Service; use Foo\DomainA\Model\Bar; class SampleService { /** * @param array $inputs */ public function create(array $inputs) { $bar = new Bar(); $bar->name = array_get($inputs, 'name'); $bar->email = array_get($inputs, 'email'); $bar->save(); } }
8. Model(Eloquent)
モデルは、フレームワークの Eloquent クラスを継承しており(正確には、Eloqent を継承した AppModel クラスを継承)、通常の Laravel アプリケーションで利用するものと同じです。
エンティティ固有の処理などは、こちらに実装します。
- package/Foo/DomainA/Model/Bar.php
<?php namespace Foo\DomainA\Model; use OneByOne\Laravel\Model; class Bar extends AppModel { }
さいごに
Laravel 4 で構築したアプリケーションの構成について見てきました。
考え方の基本は、本文でも書いたとおり、アプリケーションが主で、フレームワークはそれを実現するためのものツールに過ぎないということです。そのため、アプリケーションはフレームワークとは、独立したパッケージとして構成しています。
この考え方をより進めるなら、package/ 以下のクラスについては、フレームワーククラスを利用する箇所はより抽象化していく(例えばリポジトリパターンを利用して、Eloquent と分離するなど)方法もあるのですが、抽象化を進めすぎて、かえって複雑になるのを避けるために、現在の状態に留めています。
構築するアプリケーションにもよるとは思いますが、これまでは、この形がしっくり来ています。(もちろん、今後変わる可能性もあります。)
こうしたアプリケーションを主体にした構成にしておけば、フレームワークのバージョンが変わっても、柔軟に対応できそうです。Laravel 5 では、アプリケーションディレクトリの構成が変わりますが、そもそも別構成にしているので、オートローダで読み込めれば問題ありません。
まだまだ改良していく余地はありますが、こうした柔軟な構成が取れるのも Laravel の面白さですね。
Laravel Advent Calendar 2014、明日は neronplex さんです。お楽しみに!
- コメント (Close): 0
- トラックバック (Close): 0
Composer を倍速にした、たった 1 行のコード
- 2014-12-04 (木)
- PHP
まだ 12 月早々ですが、PHP ユーザに素敵なクリスマスプレゼントが届きました。
いまや使うのが当たり前となった Composer ですが、複雑な依存解決に実行時間がかかるのがネックでした。
これは日本国内だけでなく、海外のユーザも同じで、皆がしょうがないと思いつつも、小さな不満を持ちながら使っていました。
そんな、ある日、わずか 1 行のコードが追加されたことで、実行時間が、わずか半分になるという現象が起こりました。
Composer を倍速にするには?
composer self-update
を実行して、最新版にするだけです。
$ composer self-update
実際の効果
このコードの効果を見てみましょう。composer コマンドの --profile
オプションを使って、実行時間と使用メモリ量を出力します。
$ composer update --dry-run --profile
まずは、比較のために、変更される前の composer で実行します。3 回続けて実行した結果が以下です。
# composer = 1.0.0-alpha8 Memory usage: 199.44MB (peak: 854.47MB), time: 397.92s Memory usage: 199.44MB (peak: 854.68MB), time: 353.29s Memory usage: 200.05MB (peak: 855.5MB), time: 338.84s
続いて、変更後の composer で実行します。
# composer = 1.0-dev (37ec0bde9dd6826591308e7a1ad55cb5e38ef117) Memory usage: 122.17MB (peak: 209.3MB), time: 67.82s Memory usage: 122.17MB (peak: 209.29MB), time: 16.34s Memory usage: 122.17MB (peak: 209.3MB), time: 16.77s
なんと、メモリピークは 1/4 、そして実行時間は、6-20 倍ほど早くなっています!!
Github のコメントなどで見ると、効果のほどは、定義されている依存関係や環境によっても異なり、メモリ使用量の変化は小さい場合もあるようですが、実行時間に関しては概ね半分程度にはなるようです。
たった 1 行のコード
では、どんな魔法を使って、この改善を行ったのでしょうか。
その答えが、下記の commit です。
https://github.com/composer/composer/commit/ac676f47f7bbc619678a29deae097b6b0710b799
変更したコードは、わずか 1 行です。(実際には、空行の追加があるので、2 行)
public function run() { + gc_disable();
これは、PHP 5.3 にて、循環参照によるメモリ・リークを解消するために導入されたガベージコレクションを無効にする関数です。デフォルトでは有効になっているのですが、今回は、これを無効にしたことで、パフォーマンスが大幅に改善されました。
このガベージコレクションについては、PHP マニュアルに記述があります。
http://php.net/manual/ja/features.gc.php
また、今回の事象を踏まえて、さらに詳細な内容がまとまっていますので、こちらもどうぞ。
ircmaxell’s blog: What About Garbage?
PHP ユーザの熱狂
この、あまりの効果に PHP ユーザが熱狂しました。Twitter でも多くのコメントがありますが、最も分かりやすいのが、Github です。
この修正を行った commit のページには、数多くの賞賛コメントが付いており、祭りの様相を呈しています:D
Gif アニメーションが多数貼られており、ブラウザで開き切るのも時間がかかる状況です。もはや、悪ノリ感もありますが、いかに多くの PHP ユーザがこの改善を喜んでいるかが分かりますね。
https://github.com/composer/composer/commit/ac676f47f7bbc619678a29deae097b6b0710b799
さいごに
わずか、1 行のコードで、問題が大幅に改善される。
特にパフォーマンスの問題に関しては、こうした体験をした人は多いのではないでしょうか。その 1 行が生まれるまでには、多くの時間が必要なのです。Github 上でもディスカッションが行われていました。
そして、結果として書いたコードは、たったの 1 行でした。
早くなった composer を動かしつつ、やはり大事なのはコードを書いた量ではなく、問題を解決することなんだな、としみじみ感じた出来事でした。
なお、速くなるのは、依存解決の部分なので、ダウンロードの時間は変わりません。あしからず。
- コメント (Close): 0
- トラックバック (Close): 0
CakePHP2 アプリケーションを Heroku で動かす設定
CakePHP2 アプリケーションを Heroku 上で動かす設定についてです。
以前のエントリにも書きましたが、Heroku で Web アプリケーションを動かす際に重要なのは、Web サーバ自体(Heroku では、Dyno)に、アプリケーションの状態(データ、セッション情報、ログ等)を保持させないということです。
Heroku の Dyno は、デプロイの際や、定常的な再起動により、破棄されるため、記録されたファイルは消えてしまいます。よって、こうしたデータファイルは、アドオンなど外部に記録する必要があります。
Heroku では、アドオンを活用するのがポイントですので、ここでは、主に CakePHP アプリケーションからこうしたアドオンと連携する方法を見ていきます。
Environments Library as a plugin
まず、開発環境と Heroku 環境で設定値を切り替えるために Environments Library as a plugin を使います。
https://github.com/josegonzalez/cakephp-environments
これは、CAKE_ENV
という環境変数の値で、環境に応じて、データベースやキャッシュなどの設定を切り替えるものです。昨今のフレームワークではお馴染みの機能ですね。
ここでは、下記の設定について切り替えを行います。
- ログ
- データベース
- キャッシュ / セッション
このプラグインでは、まず Config/bootstrap/environments.php
を作成します。このファイルで、環境毎の設定ファイルの読み込みを行います。下記では、Heroku 環境用の heroku.php
と、開発環境用の development.php
を読み込んでいます。
- Config/bootstrap/environments.php
<?php CakePlugin::load('Environments'); App::uses('Environment', 'Environments.Lib'); include dirname(__FILE__) . DS . 'environments' . DS . 'heroku.php'; include dirname(__FILE__) . DS . 'environments' . DS . 'development.php'; Environment::start();
次に、Heroku 環境用の Config/bootstrap/environments/heroku.php
です。
環境の設定は、Environment::configure()
で行います。第一引数に環境名(CAKE_ENVで指定するもの)、第二引数は適用条件(true なら、CAKE_ENV に合致しなければデフォルトとして適用)、第三引数に設定値の連想配列(Configure::write() する)、第四引数にコールバックを指定します。
第三引数で設定する方法もあるのですが、より柔軟な設定を行うために、ここでは、第四引数のコールバックを指定します。実際の設定は、以降で順に指定していきます。
- Config/bootstrap/environments/heroku.php
<?php Environment::configure('heroku', false, [ ], function () { // Heroku 用設定 });
開発環境用の Config/bootstrap/environments/development
です。コールバックで、デフォルトの設定を記述しています。
- Config/bootstrap/environments/development.php
<?php Environment::configure('development', true, [ ], function () { // Log settings App::uses('CakeLog', 'Log'); CakeLog::config('debug', array( 'engine' => 'File', 'types' => array('notice', 'info', 'debug'), 'file' => 'debug', )); CakeLog::config('error', array( 'engine' => 'File', 'types' => array('warning', 'error', 'critical', 'alert', 'emergency'), 'file' => 'error', )); // Database settings Configure::write('DATABASE_OPTIONS', [ 'datasource' => 'Database/Postgres', 'persistent' => false, 'host' => 'localhost', 'login' => 'shin', 'password' => '', 'database' => 'app', ]); Configure::write('TEST_DATABASE_OPTIONS', [ 'datasource' => 'Database/Postgres', 'persistent' => false, 'host' => 'localhost', 'login' => 'shin', 'password' => '', 'database' => 'app_test', ]); // Cache settings Cache::config('default', array('engine' => 'File')); });
次に、database.php
での接続情報を、環境ごとに設定できるように下記のようにします。各環境ファイルで Configure::write()
した値を、DATABASE_CONFIG クラスに設定します。
- Config/database.php
class DATABASE_CONFIG { public $default = []; public $test = []; public function __construct() { $this->default = Configure::read('DATABASE_OPTIONS'); $this->test = Configure::read('TEST_DATABASE_OPTIONS'); } }
このプラグインを有効にするために、bootstrap.php
を以下のように変更します。コメント部分は削除しています。
- Config/bootstrap.php
<?php require_once __DIR__ . '/../vendor/autoload.php'; include __DIR__ . '/bootstrap/environments.php'; Configure::write('Dispatcher.filters', array( 'AssetDispatcher', 'CacheDispatcher' ));
Heroku で環境変数 CAKE_ENV をセット
このアプリケーションが、Heroku にデプロイした際に、heroku.php を利用するように、環境変数CAKE_ENV
にheroku
をセットします。これで、デプロイしたアプリケーションは、heroku
設定で動作するようになります。
$ heroku config:set CAKE_ENV=heroku
ログ
Heroku 環境でのログの設定です。ログは、ファイルではなく、標準出力もしくは標準エラー出力に出力します。
こうしておくと、heroku logs
コマンドでログが確認できます。また、Papertail や FlyData のようにログを各サービスへ転送するアドオンを使うと、出力されたログを閲覧したり、S3 などに転送することができます。
ログは、まずは標準出力もしくは標準エラー出力にして、あとそれをどう活用するかは、アドオンに任せるという考え方です。
下記では、アプリケーションログを標準出力へ、エラー出力を標準エラー出力へ出力しています。
- Config/bootstrap/environments/heroku.php
<?php Environment::configure('heroku', false, [ ], function () { // Log settings App::uses('CakeLog', 'Log'); App::uses('ConsoleOutput', 'Console'); CakeLog::config('debug', [ 'engine' => 'ConsoleLog', 'stream' => new ConsoleOutput(), ]); CakeLog::config('error', [ 'engine' => 'ConsoleLog', 'stream' => new ConsoleOutput('php://stderr'), ]); });
あとは、アドオンと追加すれば、出力されたログがアドオンで利用できます。例えば、Papertrailを以下のコマンドで追加して、アプリケーションを動作させると、Papertrail の画面でログが確認できます。
$ heroku addons:add papertrail
データベース
Heroku のデータベースといえば、PostgreSQL なので、Heroku Postgres を使います。(もちろん、MySQL が良ければ、ClearDB なり、RDS なり使えます。)
$ heroku addons:add heroku-postgresql
Heroku 内では接続情報が環境変数で渡されるので、これをパースします。パースされた情報を、heroku.php で、Configure::write() で、セットしていきます。
- Config/bootstrap/environments/heroku.php
<?php Environment::configure('heroku', false, [ ], function () { // Log settings (snip) // Database settings if (empty(getenv('DATABASE_URL'))) { throw new CakeException('no DATABASE_URL environment variable'); } $url = parse_url(getenv('DATABASE_URL')); Configure::write('DATABASE_OPTIONS', [ 'datasource' => 'Database/Postgres', 'persistent' => false, 'host' => $url['host'], 'login' => $url['user'], 'password' => $url['pass'], 'database' => substr($url['path'], 1), ]); });
キャッシュ / セッション
キャッシュ / セッションのデータストアには、Redis を使います。
ここでは、Redis サービスのアドオンとして、Redis To Go を利用します。
$ heroku addons:add redistogo
データベースと同じく、接続情報が環境変数で渡されるので、それをパース(ホスト、ポート、パスワード)して、セットするだけです。セッションハンドラには、Cache を使いたいので、その指定も行います。
これで、キャッシュもセッションも、Redis To Go に保存されます。
- Config/bootstrap/environments/heroku.php
<?php Environment::configure('heroku', false, [ ], function () { // Log settings (snip) // Database settings (snip) // Cache settings if (empty(getenv('REDISTOGO_URL'))) { throw new CakeException('no REDISTOGO_URL environment variable'); } $url = parse_url(getenv('REDISTOGO_URL')); Cache::config('default', [ 'engine' => 'Redis', 'server' => $url['host'], 'port' => $url['port'], 'compress' => false, 'password' => $url['pass'], 'serialize' => 'php' ]); // Session Settings Configure::write('Session', [ 'defaults' => 'cache', ]); });
さいごに
ベーシックな部分だけですが、Heroku で CakePHP アプリケーションを動かす設定を見てきました。
ここで紹介した以外では、メールやデータファイル(画像等)など、他にもアプリケーションでは必要なものがありますが、ほとんどのものは、ここで見てきたようにアドオンを活用することで実現できます。
こうした構成にしておけば、Dyno が再起動したり、インスタンス数を増減しても、アプリケーションデータはアドオンに保持されているので、動作に支障はありません。こうしたスケーラブルナ構成にしておくのが、Heroku のような PaaS を活用する肝ですね。
このエントリは、CakePHP Advent Calendar 2014 の 3 日目でした。まだ少し空き枠があるので、CakePHP な何かを書いてみてはどうでしょうか。
- コメント (Close): 0
- トラックバック (Close): 0
Laravel 4 データベースを使ったテストで Migration と Seeder を使う
Laravel 4 でデータベースを使ったテストを書く際の Tips です。
自動テストでデータベースにアクセステストを書く際に大切なのが、データベーステーブルのデータをテストで想定された状態にしておくということです。テーブルの内容がテストを実行される度に異なると、ある時はテストが通って、ある時は通らないという状態になります。
この「想定された状態」をセットアップするために、フレームワークで用意されている Migration と Seeder を利用しています。
テストケースでマイグレーション実行
開発を進めていると、データベーススキーマを変更する場合があります。マイグレーションファイルを作成して、php artisan migrate
コマンドで適用するのことになります。テスト用データベースについても適用する必要がありますが、php artisan migrate --env=testing
を実行するのは、少し手間なので、テストケースで実行するようにしています。
artisan コマンドは、PHP コードでは、Artisan::call()
で実行できるので、下記のようにすると、テストクラスでマイグレーションを実行することができます。
<?php public function setUp() { parent::setUp(); Artisan::call('migrate'); }
ただ、マイグレーションは、全てのテストケースで毎回実行する必要が無いので、基底クラスである TestCase
に下記のように実装して、初回のみ実行するようにしています。
<?php class TestCase extends IlluminateFoundationTestingTestCase { protected static $databaseSetup = false; /** * */ protected function setUpDatabase() { if (static::$databaseSetup) { return; } Artisan::call('migrate'); static::$databaseSetup = true; } /** * Creates the application. * * @return SymfonyComponentHttpKernelHttpKernelInterface */ public function createApplication() { $unitTesting = true; $testEnvironment = 'testing'; return require __DIR__ . '/../../bootstrap/start.php'; } }
データベースを使うテストクラスでは、下記のように setupDatabase
メソッドを実行して、マイグレーションを実行します。(実際にマイグレーションが実行されるのは初回のみ)
<?php class FooTest extends TestCase { public function setUp() { parent::setUp(); $this->setUpDatabase(); } }
フィクスチャとして Seeder を使う
Laravel には、テーブルのデータを一括登録する仕組みとして Seeder があります。テーブルへの登録処理を書いておくと、php artisan db:seed
コマンドでデータを登録することができます。主にアプリケーションで必要なマスタデータや初期データを投入するのに使われますが、これをテスト時のフィクスチャとして利用します。
Seeder クラスは、app/database/seeder
以下に配置するのが通常なのですが、フレームワークとしては、オートローダーで読み込むことができれば、どこに配置しても構いません。
テスト用の Seeder クラスは、テストケースと密接な関係にあり、テストに応じたレコードを用意する必要があるので、同じ PHP ファイルに定義しています。クラス名は、テストケースクラス名の後ろに Seeder
を付けています。こうすれば、どのテストケースでも、同じファイルにある Seeder クラスを __CLASS__ . 'Seeder'
で参照できます。
<?php class FooTest extends TestCase { public function setUp() { parent::setUp(); $this->setUpDatabase(); $this->seed(__CLASS__ . 'Seeder'); } } class FooTestSeeder extends Seeder { public function run() { Eloquent::unguard(); Item::truncateAll(); Item::create( [ 'id' => 1, 'name' => '商品1', 'price' => 100, ] ); } }
Item::truncateAll()
は、PostgreSQL 環境で、truncate を実行するために独自に実装したメソッドで、下記のような実装になっています。TRUNCATE
文に RESTART IDENTITY CASCADE
を付けることで、テーブルのシーケンス値のリセットと、このテーブルを外部参照しているテーブルを同時に削除することができます。
<?php class AppModel extends Eloquent { /** * for test */ public static function truncateAll() { $table = (new static)->getTable(); $sql = 'TRUNCATE ' . $table . ' RESTART IDENTITY CASCADE'; DB::table($table)->getConnection()->statement($sql); } }
複数のテストケースで共有する場合は、app/database/seeder
に配置して、$this->seeder('TestCommonSeeder')
などで実行します。
さいごに
冒頭に書きましたが、データベースを利用したテストでは、事前に想定した環境を作っておくことが肝になります。
Seeder をフィクスチャとして使うことで、アプリケーションの機能を使うことなく、テストに必要な状況を作り出すことができます。テストの中で、テスト対象以外のアプリケーションコードを実行すると、何をテストするためのものなのかが、ぶれてしまうので、環境構築はフレームワークやライブラリで用意された機能のみでシンプルに実現するのが良いでしょう。
- コメント (Close): 0
- トラックバック (Close): 0
ホーム > PHP
- 検索
- フィード
- メタ情報