Technote

by sizuhiko

CakeFest2014に参加しました

9/19に茅場町Co-Edoで行われたCakeFest2014報告会でも話した内容をベースに記事にしました。

すでに同日に発表された@yandoさんの記事「マドリードで見たCakePHP3の明るい未来」が秀逸なので、CakePHP3の動向についてはその記事をご覧ください。

では私は何を書くかというと、報告会でも話したCakeFest2014の全体感と、来年10周年を迎えるCakePHPを盛り上げる為にみんなでCakeFestに行こう!という気分になってもらえると良いな、というまとめになっています(ちょっとはCakePHP3についても書くよ)。

CakeFestの歴史

CakeFestは2008年に始まって、今回で8回目になります。 参考URL:lanyrd.com

  • 2008/02 フロリダ・オーランド
  • 2008/12 ブエノスアイレス(参考URLの年は間違っています)
  • 2009/07 ベルリン
  • 2010/09 シカゴ
  • 2011/09 マンチェスター
  • 2012/08 マンチェスター
  • 2013/08 サンフランシスコ
  • 2014/08 マドリッド

初年度を除いて、およそ1年に1回世界中からコミュニティのメンバーが集まるお祭りです。

2回目のブエノスアイレスを除いてアメリカとヨーロッパで開催されており、私が始めて参加した4回目のシカゴからは8月終わりor9月初めに開催されるようになりました。 コアデベロッパーは家族や彼女と来ていたりするので、夏休みも兼ねているのではないか、という緩いイベントだった..だったのです。

緩い理由は例えば2012年(マンチェスター)のスケジュールを見てもらえるとわかりますが、9時に始まって16時ぐらいには終了です。このときは2トラックだったこともあり、セッション数としては少なくはないのですが、泊まりのカンファレンスイベントとしては割とあっさり終了する感じです。このあとはLTやりたい人がいればやるみたいな、日本のキッチリしたイベントの仕切りやテキパキ感というよりは参加者がお茶やビールを片手に交流する時間がたっぷり取られているという感じだったのです。

2014年は違っていた

私もCFP(Call For Paper)にセッションの応募をしたのですが、落選してしまいました。 で、確定したスケジュールを見て、ビックリしたのです。 朝は9時からと例年通りですが、終わる時間が!!なんと21時ですよ。つまり12時間。これはちょっとしたPHP Matsuri状態ではないですか。

今年はシングルトラックでしたが、あんまりBreakタイムがなかったので、各自の判断でカンファレンスルームの外で話したりしていた状況ではありました。

なお私が知る限りのCakeFestの中では、最もイベントとしてのテキパキ感とかはあったように思います。これもコミュニティマネージャであるJames Watts氏の活躍があってこその事。CakeFestの仕切りも3回目という事でだいぶ慣れてきたのかなーとも思います。

CakePHPの中の人たち

そういえば、あまりCakePHPの中の人たちって使っていてもCakeFestに参加しないと知らないのではないかと思い、コアチームのメンバーの顔と名前が一致するように、2枚の写真で紹介します。

photo by Anna Fillina.

左からJames Watts、Jose Gonzalez(@savant)、Larry Masters(@phpnut)(敬称略)

こちらの写真は私がスペインに到着した夜の出来事なのですが、まぁお酒もだいぶ入っているところでの余興みたいな感じだったのでしょう。私はフライトの疲れでビールを1杯飲んで部屋に戻ってしまったので見ていなかったのですが、こんな感じでホテルのバースペースで盛り上がったり、そのあと朝4時まで繁華街へ飲みに行ったりしていたみたいです。 毎年朝方まで飲んでいる参加者がいるのもCakeFestの一部です(その影響か午前中はホテルの部屋から出て来ない人も多数ですがw)。

で、写真の後どうなったか、というとコチラの動画から確認できます。

ここ数年はコアチームへのQ&Aセッションがあります。こちらでもCakePHPの中の人たちを確認することができます。

photo by Anna Fillina.

左からMarc Ypes(@ceeram)、Mark Story、Mark Scherer(@dereuromark)、Larry Masters(@phpnut)、José L. Rodríguez(@josezap)、Jose Gonzalez(@savant)、Renan Gonçalves(@renansaddam)、James Watts(敬称略)

CakePHP1.3の終了と2系の今後

Q&Aセッションはカンファレンス1日目のお昼直後に実施されたのですが、午前中にMark Story氏からCakePHP3の話と、1.3のサポート終了の話があったので、その2つについて質問が集中していました。1.3のサポート終了に関しては、何とちょうど先週顧客にCakePHP1.3を使った案件の提案をしたばかりなのに、どうしたら良い?何とかならないの?!というビックリするようなQを聞きました。先日も1.3のアップデートがあったり、1.3自体非常に長い間使われているのだなぁと思いました。その後、質問をした人になんでこれから1.3使うの?と聞いたら、資産があるから、という事でした。 こちらのAは、ベストな方法はマイグレーションすることだけど、3ではなくCakePHP2にするのが良いという話と、1.3の件は通常のCakePHPとしてはムリだけど、CakeDCとして考えられなくもない。まぁビジネスだね、という回答でした。 とはいえ、たぶんCakeDCも1.3の面倒を見続けるのは現実解ではないので、マイグレーションのサポートをする事で解決をするのではないかなーと思われます。

日本でもまだCakePHP1.xを使っているサイトなど残っていると思いますが、3へのジャンプは難しいと思うので、まず2系にしておくことが重要になるでしょう。 2系についてはまだ3年はサポートと今回明言されています。じゃぁまた3年後には3にしないといけないの?!という話はあるかもしれませんが、1から2系へはアップグレードシェルがあるのと、3のアップグレードシェルができたとして、2系からのアップグレードのみ対応であると推測されるので、まずは2系なのかなと思います。これは上記のコアチームのQ&Aでの回答からも妥当な判断と思います。

CakePHP3で気になっていたこと

CakePHP3のもくもく会で、何度もCakePHP3を試しているのですが、昨年ビヘイビアって無くなるって言ってなかったか?と思い、ORM担当の大きいJoseこと@jose_zapに確認しました。答えとしては、いろいろ考えたけど、コールバックの関係もあって互換性を考え残すことにした。もちろんTraitにしてEventマネージャを使うアプローチは素晴らしいし、できればそちらを使ってほしいけど、コアとしてはビヘイビアも残すよ、ということでした。

恒例のリアルケーキ

今となっては恒例となったリアルケーキ。実はCakeFestで始めてケーキが登場したのは、2010年のシカゴだったと記憶しています。 キッカケは日本で開催されたCakePHPカンファレンス東京で、当時のCakePHP開発マネージャだった@gwoo氏を招いてCakePHP型のケーキを出したのがとてもウケが良く、それが本家でも採用されたと。CakeFestには日本コミュニティのDNAが含まれています!

この直後、コアチームとして新しく入った@dereuromarkがファーストバイトならぬカップケーキ投げの手荒い歓迎を受けたり、サプライズイベントがあったり、楽しくケーキを食べながら交流を深めて解散となりました。

CakeFest2014で感じたこと

CakeFestに参加すると、良く知った常連さんと、始めての人がいて、なるべく始めて会った人と話すようにいつも心がけています。

各国でのCakePHPの普及状況や、コミュニティの活動などの話を聞くのですが、ドイツから参加してきていた人の話によると、CakePHP(PHP)のイベントをやりたいけど、なかなか人が集まらなくて苦労している。日本ではどうなの?という話から、日本の状況を説明すると、とても日本のコミュニティが羨ましいという感想をもらいました。これはドイツ以外の参加者からも同様の反応をもらったのですが、まぁ日本というか東京など都市部には人が密集していてやりやすい状況なのかな、とも思います。そういった環境面やコミュニティ活動を積極的にやってくれる人がいるので都市部のエンジニアは特に恵まれた状況だと思います。もっと勉強会やイベントに出て交流しましょう!

今年は(も)シングルトラックだったのでほぼ全てのセッションを見る事ができたのですが、CakePHPと関連するセッションは、コアチームに集中していて、他のセッションはPHP全般だったり、JavaScriptだったり、HTML(CSS)だったりとCakePHPに関する話題は例年以上に少なかったような気がします。 これはCakePHP2のライフサイクルがだいぶ長くなっていて新しい話題が少なめなのと、まだ3は早すぎるという状況もあったのではないかと思います。 来年はコアチーム以外からも3の話題がもっと出てくるのではないかと思います。

CakeFest2014のjoind.inにスライドのリンクがあるので、ぜひCakePHP3のセッションについては見て欲しいのと、公式のPodCastが配信されているので、そちらも聞くとCakePHP3について理解が深まると思います。

CakePHPに関する話題以外で、ぜひスライドをチェックして欲しいのは

  • Writing code that lasts presented by Rafael Dohms
  • Why You Can’t Test presented by Chris Hartjes

どちらもPHP界では有名人で、同じ内容を各国のカンファレンスで講演しているので、どこかで見ているかもしれませんが、一度はチェックしてみてください。

LTもやったし、BDDプラグインの人として声をかけられたり、交流できた

また初日のLTでData Generator for Testing CakePHP Application(いつものテストデータの作り方の英語バージョンです)をやりまして、反響もたくさんもらいました。 CakeFestのスピーカーの中には、特にCakeと関係ないけどPHPのセッションとして応募してくる人もいるので、他のフレームワークには対応しないの?とか、Fakerと連動するにはどうするの?などなど。 CakeFestではLTは当日募集で、手を挙げればもれなく喋れるので、海外カンファレンスでのプレゼン登竜門としては最適と思います。

それと自分の自己紹介スライドのアイコンから、BDDプラグインの人でしょ?と声をかけられたりもして、Breakタイム少なかった割によく話したなーという印象でした。

また例年以上?に日本自体に関心がある人がいて、日本語喋れるノルウェーからの参加者(漫画大好き)だったり、日本食について聞いてくる地元スペインの人だったり、3次会でカラオケ行こうと誘われたり、フクシマの話をしたり、PHP以外の話でも交流ができました。

「海外のカンファレンスとか行ったことないし、大変そう」という方がほとんだと思います。私もお世辞にも英語得意でない(割と適当)けど、何とか交流できています。CakeFestであれば、ここ数年@yandoさんや私が参加していますし、行きたいと宣言すれば一緒に行く人が見つかるかもしれません。

もし会社勤めのエンジニアだと休みが取れない、経費出ない、など難しい事情があるかもしれないのですが、ぜひ上司と交渉して来れるようになると良いなと思っています。参加して得られることは文章では書けないぐらい大きいです。

コミュニティマネージャからのメッセージ

最後にコミュニティマネージャであるJames Watts氏から、キーノートに関連して日本のコミュニティへメッセージをいただきましたので、動画をシェアします。 では来年のCakeFestで会いましょう!

最初のトラブルは、テラスの石畳と砂利の間に足を取られて転びそうになったのです…

CakePHP3(Beta1)のDebugKitではSQLiteが必要になります

CakePHP 3.0.0 もくもく会(勉強会) #7 に参加してCakePHP3ベータ1を早速試しました。

ベータ1はアルファ版からディレクトリの変更も多く、CakePHP本体をcomposer updateしてもダメなので、再度 composer create-project でテンプレートから生成しなおします。

最新のcakephp/appテンプレートにはDebugKitプラグインが依存関係に入っています。

    "require": {
        "php": ">=5.4.19",
        "cakephp/cakephp": "3.0.*-dev",
        "mobiledetect/mobiledetectlib": "2.*",
        "cakephp/debug_kit": "3.0.*-dev"
    },

require-devではないのですね、と思ったり。。

以下のコマンドでCakePHP3アプリケーションのプロジェクトスケルトンを作ります。

composer create-project -s dev cakephp/app dev-cake3 app

ブラウザでアクセスすると、以下のような画面が表示されます。

なんかDebugKitがSQLiteを参照しているみたいなんですよ。

そこで、DebugKitの設定を見てみます

app/plugins/DebugKit/config/bootstrap.php

if (!ConnectionManager::config('debug_kit')) {
    ConnectionManager::config('debug_kit', [
        'className' => 'Cake\Database\Connection',
        'driver' => 'Cake\Database\Driver\Sqlite',
        'database' => TMP . 'debug_kit.sqlite',
        'encoding' => 'utf8',
        'cacheMetadata' => true,
        'quoteIdentifiers' => false,
    ]);
}

そうですか、そうですか…

githubのCakePHP3用ブランチもチェックしてみました。

Requirements

The 3.0 branch has the following requirements:

CakePHP 3.0.0 or greater. PHP 5.4.19 or greater. SQLite or another database driver that CakePHP can talk to. By default DebugKit will use SQLite, if you need to use a different database see the Database Configuration section below.

そうですか、そうですか….

でもなんか別のDB接続も可能みたいに書いてあるよ

Database Configuration

By default DebugKit will store panel data into a SQLite database in your application’s tmp directory. If you cannot install pdosqlite, you can configure DebugKit to use a different database by defining a debugkit connection in your config/app.php file.

ふむふむ

config/app.php のDatasourceに以下のようなdebug_kit接続を追加してみます。

        'debug_kit', [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Mysql',
            'persistent' => false,
            'host' => 'localhost',
            'login' => 'hoge',
            'password' => 'fuga',
            'database' => 'database_name',
            'prefix' => false,
            'encoding' => 'utf8',
            'timezone' => 'UTC',
            'cacheMetadata' => true,
            'quoteIdentifiers' => false,
        ],

ブラウザをリロード

ぎゃー、DebugKitの該当箇所のコードを見てみると、そんなに変じゃないように見えますが、

        $configs = ConnectionManager::configured();
        foreach ($configs as $name) {
            $connection = ConnectionManager::get($name);

このときの$configをwatchしてみると

array (size=5)
  0 => string 'default' (length=7)
  1 => string 'test' (length=4)
  2 => int 0
  3 => int 1
  4 => string 'debug_kit' (length=9)

上記のようになっています。2と3誰よ….

ということで、まだベータ1だからねー。はははーという事で、現時点はSQLiteのextension入れて試しましょう。

例えば、friendsofcakeのvagrant-chef使っているなら、cookbooks/php/recipes/install.rbに追加します。

  "php5-mysql",
  "php5-sqlite",

mysqlの下あたりに追加します。vagrant destroy/upを実行して環境を作り直したら、もう一度ブラウザをリロードします。

おなじみの青いバーの画面が表示されました。画面の右下に出ているCakePHPアイコンがDebugKitです。 アイコンをクリックすると以下のようにデバッグ情報を参照可能になります。

DebugKitも使えるし、どんどんCakePHP3のアプリケーション開発がやりやすくなっていると思います。 CakePHP3.0.0 もくもく会で情報交換しながら、楽しく新機能を試してみませんか?

CakePHPで学ぶ継続的インテグレーションが発売されます

先日の記事にも書きましたが、

春先ぐらいからBlogの更新が滞っていましたが、その要因となったアレが大分落ち着いてきたので、たまっていたネタを順番に書き出します。

アレが出ます。

ババーン ww とでかい画像を付けましたよ。

CakePHPで学ぶ継続的インテグレーション

共著者の

でも既に紹介されていますので、そちらを一読いただくと理解が深まります。

私は主に以下のあたりを担当しました。

  • Chapter 5 開発工程(1)
    • 5-1 開発の進め方
    • 5-2 ユーザーストーリーの定義
    • 5-3 機能実装
  • Chapter 6 開発工程(2)
    • 6-1 ステップの定義
    • 6-2 継続的インテグレーションの定義

上記以外でも、おおよそCakePHPに関連するところを書いたと思っていただければ。

これは何の本

タイトルの通り、と言いたいところですが、わからないという人のために補足すると

  • 継続的インテグレーションの入門書です
  • CakePHPでのアプリケーションの作り方を解説した本ではありません
  • サンプルアプリケーションの構築、自動テストの作成方法の解説にCakePHPを使っています

タイトルや見出しから内容が想像しずらいと思うので、私が書いた部分の継続的インテグレーションに関する技術的キーワードを列挙すると、以下のとおりです。

上記以外にアプリケーション構築用にBoostCake、CakeDC Usersプラグインも使っています。 自分が作ったプラグインであるBDDとFabricateを採用していますが、これは現在支援を行っている現場でも活躍しているので手前味噌というだけでなく、自動テストを継続していく上で役立つと思います。

私の記述したところの、ざっくりした解説は以下のとおりです。

  • ComposerとMigrationsプラグインを使うと、誰でも簡単に同じ環境を構築することができるようになります。
  • Environmentsプラグインを使うと、環境毎の設定切り替えや、振る舞いの制御を容易にできるようになります。
  • BDDプラグインを使うとCakePHPからBehatとの連携が容易になり、受入テストの自動化に取り組みやすくなります。
  • Fabricateプラグインを使うとFixtureを使ったテストデータの呪縛から解放されます。

これらは過去から現在に至るまで、私が様々なプロジェクトで経験したことを背景にしています。 もちろんプロジェクトごとに様々な背景があると思いますが、一度実践していただければ効果を体感していただけるのではないかと思っています。

また、PHP界隈ではまだBDDやATDDに関して、解説する本がなかったり、実践的な解説がなかったのですが、本書がこれらを導入するきっかけになると嬉しいなと思っています。

CakePHP以外ではどうなの

サンプルアプリケーションの構築にはCakePHPを利用していますが、上記の技術要素に関しては他のPHPフレームワークでも同様のものがあると思います。 もし、CakePHP以外のフレームワークに精通していれば、Behatを直接使ったり、データジェネレータにFakerの機能を使ったり、各フレームワークのマイグレーションや環境切り替えプラグインを使ったりできるのではないかと思います。 皆様の現場の状況に合わせて読み替えて、継続的インテグレーションを導入する参考にしていただければ幸いです。

いつ発売なの?

amazonでは2014/09/19(金)に発売です。

書店などでは多少前後することがあると思いますが、ぜひ一度手に取ってみてください。

また9/19は、茅場町Co-EdoにてCakeFest2014報告会を実施します。本会には共著者である@kaz_29さんも参加予定なので、著者サインを集めるチャンスです! 先月実施されたCakeFestではどんな事があったのか、CakePHP3の動向などを話しますので、ぜひこちらにも参加ください。

お知らせと感謝(追記)

CakePHP3の話題もあるなか、まだまだ現場ではCakePHP2を利用するケースもあると思います。 本書をきっかけにCakePHPをより学んでみたいなと思っていただけたら、私も共著者で参加したCakePHP2実践入門(amazonでチェック)を参考文献として手に取ってみてください。

こちらはちょうど2年ほど前の2012/09/29に発刊となったのですが、このたび 第3刷 の増刷が決まりました。

長い間CakePHP2の参考書として手に取っていただいているようで、本当に嬉しいです。ありがとうございます。

CakePHP2のプラグインをTravis.ciで継続的インテグレーションする

春先ぐらいからBlogの更新が滞っていましたが、その要因となったアレが大分落ち着いてきたので、たまっていたネタを順番に書き出します。

CakePHP2のプラグインを作成/公開していて、継続的インテグレーションってどうするの?と思っていました。 もちろんユニットテスト書いてあるし、素晴らしい協力者の方がPull Requestを送ってくれれば、developブランチに取り込んでテストしたりします。

Githubを徘徊していると、よく見る

これを表示したいと思ったのです。

ただCakePHP2のプラグインは当然それだけではユニットテストを実行できません。CakePHPのアプリケーションがあって、テスト用データベースとその設定があって…、ぬーん。 早い話、テスト用のアプリケーション作って、そこからテスト実行すれば良いの?ということをずっと思っていました。

CakePHPの有名プラグインはどうしているのだろう?

と思い、最初にチェックしたのはCakeDC/searchプラグイン。 大抵のアプリケーションで使う、もっともメジャーなプラグインではないかと思います。 Searchプラグインにも、あのビルド成功やダウンロード数、バージョン番号の画像が表示されています。

.travis.yml ファイルがあるので、内容を確認してみます。 すると、

before_script:
  - git clone https://github.com/burzum/travis.git --depth 1 ../travis
  - ../travis/before_script.sh
  - if [ "$PHPCS" != 1 ]; then
            echo "
                require_once APP . DS . 'vendor' . DS . 'phpunit' . DS . 'phpunit' . DS . 'PHPUnit' . DS . 'Autoload.php';
            " >> ../cakephp/app/Config/bootstrap.php;
    fi

https://github.com/burzum/travis.gitをcloneしてきて、何かやっているようです。

CakePHPプラグインを簡単にTravis.ciで継続的インテグレーションできる

早速、ページにアクセスしてみると、どうもそれはあのFriendsOfCakeからForkされたもののようです。 FriendsOfCake元リポジトリを確認します。

Easy travis setup for CakePHP plugins This repository helps easy travis integration for CakePHP plugins, primarily focused on FriendsOfCake projects, but can be used within any plugin when satisfying the requirements.

おぉ、正に私の求めていたもの。

Fabricateに導入してみた

最近作った中で、最も思い入れのあるCakePHPプラグインFabricateを継続的インテグレーションするため、上記のFriendsOfCake/travisを導入してみます。

Quick Installに書いてるとおり、以下の手順で進めます。

cd ~/develop/fabricate

git clone https://github.com/FriendsOfCake/travis.git

export PLUGIN_NAME="Fabricate"

travis/setup.sh

rm -rf travis

私がやったときはこの手順ではなかったのですが、最近変わったみたいです… まぁ細かい事は気にせずですね。

実行すると、以下のファイルが自動生成されます。

  • .travis.yml : Travis.ciで継続的インテグレーションを実行するための設定ファイル
  • Test/Case/AllFabricateTest.php : プラグインのユニットテストをすべて実行するテストスイート。すでにこの命名規則で生成されていれば作られません。

以前のバージョンでは

  • .editorconfig
  • .semver
  • .travis.yml
  • .AllPluginNameTest.php
  • composer.json
  • CONTRIBUTING.markdown
  • LICENSE.txt
  • README.markdown

が生成されていましたが、継続的インテグレーションには必要がないファイルも混在していたので、整理されたものと思います。

後はTravis.ciにサインアップして、リポジトリを追加するだけです。

実は、これが最初のビルド。PLUGIN_NAMEをexportしていなかったので、正しくテストの実行が動いていませんでした。PLUGIN_NAMEはちゃんとexportするようにした方が良いです。 .travis.ymlのPLUGIN_NAMEFabricateに設定して実行した結果が以下の画面です。

現在のtravis.gitではPHP5.4以上のようなので、問題ないのですが、当時(と言っても数ヶ月前)はPHP5.3もテスト対象になっていたので、ショートArrayシンタックスを使っている私のコードはテストが失敗していました。.travis,ymlのPHP部分を以下のように変更して

php:
  - 5.4
  - 5.5

pushすると、ビルド結果は以下のようになります。

PHPCS以外は成功しています。

PHPCSではCakePHPのコード標準がチェックされます。 CakePHPの標準コードチェックだと厳しい部分だったり、パラメータを変更してチェックさせたくないディレクトリがあったりすると思うので、PHPCS_ARGSでphpcsを実行するときのパラメータを上書きできるようになっています。

matrix:
  include:
    - php: 5.4
      env:
        - COVERALLS=1
    - php: 5.4
      env:
        - PHPCS=1
        - PHPCS_ARGS="-p -s --extensions=php --standard=ruleset.xml --ignore='*/Test/*,*/Vendor/*' ."

拡張子phpのファイルを対象とし、TestやVendorのディレクトリを対象外として実行します。 ruleset.xmlは以下のようにしました。

<ruleset name="Custom Standard">
    <rule ref="CakePHP">
        <exclude name="CakePHP.NamingConventions.ValidVariableName.PrivateNoUnderscore" />
        <exclude name="CakePHP.NamingConventions.ValidFunctionName.PrivateNoUnderscore" />
        <exclude name="CakePHP.NamingConventions.ValidVariableName.ProtectedNoUnderscore" />
        <exclude name="CakePHP.NamingConventions.ValidFunctionName.ProtectedNoUnderscore" />
        <exclude name="CakePHP.NamingConventions.ValidFunctionName.ScopeNotCamelCaps" />
        <exclude name="CakePHP.NamingConventions.ValidVariableName.MemberVarNotCamelCaps" />
        <exclude name="CakePHP.WhiteSpace.TabAndSpace" />
    </rule>
</ruleset>

CakePHPのコード標準ではprivateやprotectedの変数、関数の先頭にアンダースコアが求められるので、それを無効にしたのと、キャメルケースではない変数や関数名を使いたい箇所があったので、それを無効にしています。 最後のCakePHP.WhiteSpace.TabAndSpaceでは、以下のように=のインデント位置を合わせたコードがエラーになってしまうので、無効にしています(わかりづらいですが、=の位置が揃っています)。

public function __construct($name) {
    $this->name  = $name;
    $this->items = [];
}

どうなっているのか?

プラグインのリポジトリには.travis.ymlが追加されるぐらいです。 この設定内で、FriendsOfCakeのtravisリポジトリがcloneされ、以下に記述されている手順が実行されます。

before_script:
  - git clone -b master https://github.com/FriendsOfCake/travis.git --depth 1 ../travis
  - ../travis/before_script.sh

script:
  - ../travis/script.sh

after_success:
  - ../travis/after_success.sh

実行されるパターンは.travis.ymlに記述されているphpenv-matrixの組み合わせと、matrixのパターンになります。

  • PHP5.4, DB=mysql CAKE_VERSION=2.4
  • PHP5,4, DB=mysql CAKE_VERSION=2.5
  • PHP5.5, DB=mysql CAKE_VERSION=2.4
  • PHP5,5, DB=mysql CAKE_VERSION=2.5
  • PHP5.4, COVERALLS=1
  • PHP5.4, PHPCS=1

この6パターン毎に、before_scriptscriptafter_successが呼び出されます。

事前準備

before_script.shは実行準備のためのスクリプトで、PHPCSが1にセットされている場合は、以下のとおりpearチャンネルからCakePHPのコード標準とphpcsが依存関係としてインストールされます。

if [ "$PHPCS" = '1' ]; then
    pear channel-discover pear.cakephp.org
    pear install --alldeps cakephp/CakePHP_CodeSniffer
    phpenv rehash
    exit 0
fi

続いて、CakePHP本体が1つ親のディレクトリにインストールされます。Travis.ciではプラグインのリポジトリが最初にcloneされるのですが、結果として以下のようなディレクトリ構成となります。

  • $HOME/sizuhiko/Fabricate : プラグイン本体
  • $HOME/sizuhiko/travis : FriendsOfCakeのtravis.gitクローン先
  • $HOME/sizuhiko/cakephp : CakePHP本体
  • $HOME/sizuhiko/cakephp/app/Plugin/$PLUGIN_NAME : プラグイン本体がコピーされる先

上記ディレクトリ構成ができた後で、composer で依存関係を解決します。COVERALLS用のカバレッジレポートも生成されるようにファイルが準備されます。

実行

script.shがPHPCSが1の場合はphpcsを、それ以外の場合はユニットテストを実行します。

実行が成功したら

after_success.shがCOVERALLSのカバレッジレポートを生成します。

そして現在

最初のスクリーンショットがFabricateの現在の状態です。すべて成功しています。

最後にREADMEにバッジを表示させる方法を紹介します。

Travis.ciのバッジを表示する

Travis.ciの画面にアクセスして

build:failingのように表示されている画像をクリックします。

ブランチを選択して、表示形式からマークダウンを選択したら、表示内容をREADME.mdに貼り付けます。

カバレッジのバッジを表示する

カバレッジはCOVERALLSというサービスを使って表示します。

サイトにアクセスして、サインアップはGithubのアカウントでできるの簡単です。

サインアップ後に、上部のメニュー表示からREPOSを選択して、

ADD REPOをクリックしてリポジトリを追加します。 その後、追加したリポジトリのページを表示します。

今度は表示される画像ではなく、GET BADGE URLというボタンをクリックします。このあたりのUIは統一されると嬉しいですね…

いろいろな記述形式のコードが表示されるのでマークダウンのコードを選択してREADME.mdに貼り付けます。

ダウンロード数、バージョンを表示する

ここからはCIとは関係ないのですが、よく見るバッジであるダウンロード数とバージョン番号も表示してみたいと思います。

Badge Poserというサイトにアクセスします。

ダウンロード数やバージョン番号は、Packagistで表示されている情報を画像に変換するので、リポジトリがPackagistに登録されていることが条件となります。 Show the markdown for your Badgesにダウンロード数を表示したいPackagistのパッケージ名を入力します。

表示形式からマークダウンを選択して、コードをREADME.mdに貼り付けます。

最後に

CakePHPのプラグインを継続的インテグレーションする方法は、実はとても簡単でした。 これを機に、まだプラグインを継続的インテグレーションしていないそこのあなたも、Travis.ciとCOVERALLSを使って継続的インテグレーションをしてみましょう。

CakePHP3 もくもく会#5 に参加して Model の新機能を試してきた

2013/6/16(月) に コワーキングスペース茅場町 Co-Edo(コエド) で行われた「CakePHP3 もくもく会 #5」に参加しました。

前回のイベントではBakeすることに成功したので、今回は大きく変更となったモデル周辺についてテストコードから把握してみる目標をたてました。

環境構築する(前回との差分)

3回目に参加した記事では

環境構築でも利用した Friends Of Cake のリポジトリにはCakePHPのアプリケーションスケルトンを作成する app-template というリポジトリがあります。 すでにCakePHP3用のスケルトンも(cake3ブランチとして)用意されているので、GitHubのREADMEに書いてあるとおりのコマンドでCakePHPのアプリケーション環境を構築します。

composer -sdev create-project friendsofcake/app-template app dev-cake3

のように記述したのですが、その後devが上がったところでこちらのスケルトンは最新追従していなかったので、CakePHPの公式スケルトンを使っています。

composer create-project -s dev cakephp/app dev-cake3

CakePHP3におけるモデル

CakePHP3では従来モデル(Model)と呼んでいたクラスはなくなり、テーブル(Table)とエンティティ(Entity)という2つのクラスに別れます。従来のモデルがテーブルに変更になりfindなどの責務を持っています。find結果は従来arrayで返却されてきたのですが、CakePHP3からはエンティティと呼ばれるオブジェクトで返却されます。 CakePHP1から2にかけてbasukeさんが作られたCakeEntityというプラグインがあります。このプラグインはCakePHP3の新しいモデルのベースになったものでもあり、利用したことがあればそれほど違和感なく移行できるのではないかと思います。

Bakeされた結果を見てみる

前回bakeコマンドで生成したモデル層のファイルを見てみると

App/Model/Table/PostsTable.php

<?php
namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Posts Model
 */
class PostsTable extends Table {

/**
 * Initialize method
 *
 * @param array $config The configuration for the Table.
 * @return void
 */
    public function initialize(array $config) {
        $this->table('posts');
        $this->displayField('title');
        $this->primaryKey(['id']);
        $this->addBehavior('Timestamp');

    }

/**
 * Default validation rules.
 *
 * @param \Cake\Validation\Validator $validator
 * @return \Cake\Validation\Validator
 */
    public function validationDefault(Validator $validator) {
        $validator
            ->add('id', 'valid', ['rule' => 'numeric'])
            ->allowEmpty('id', 'create')
            ->allowEmpty('title')
            ->allowEmpty('body');

        return $validator;
    }

}

App/Model/Entity/Post.php

<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;

/**
 * Post Entity.
 */
class Post extends Entity {

/**
 * Fields that can be mass assigned using newEntity() or patchEntity().
 *
 * @var array
 */
    protected $_accessible = [
        'title' => true,
        'body' => true,
    ];

}

のようになっています。

PostsTable::initialize()は従来publicプロパティとして定義していたテーブル名などの情報です。 validationDefault()は$validatesで定義していたバリデーション定義です。

Postクラスはエンティティで$accesibleに利用可能なフィールド名を列挙します。これはRailsでattraccessorを定義するとプロパティにアクセス可能になるのと同様の効果を持つようになると推測されますが、現時点では何も実装はされていないようです。

コアのテストコード見てみる

<?php
    public function testRewind() {
        $query = $this->table->find('all');
        $results = $query->all();
        $first = $second = [];
        foreach ($results as $result) {
            $first[] = $result;
        }
        foreach ($results as $result) {
            $second[] = $result;
        }
    }

とてもザックリしたところを切り抜きましたが、まぁそういうことです。 これを2系のコードで似せて書くと

<?php
    public function testRewind() {
        $results = $this->Model->find('all');
        $first = $second = [];
        foreach ($results as $result) {
            $first[] = $result;
        }
        foreach ($results as $result) {
            $second[] = $result;
        }
    }

えっ!行数3の方が増えてないか?!とかいうツッコミはいらないですよ!! なんか聞いている程変わってないですよね。

ではもう少し複雑な例を

<?php
    public function testFindAllNoFieldsAndNoHydration() {
        $table = new Table([
            'table' => 'users',
            'connection' => $this->connection,
        ]);
        $results = $table
            ->find('all')
            ->where(['id IN' => [1, 2]])
            ->order('id')
            ->hydrate(false)
            ->toArray();
        $expected = [
            [
                'id' => 1,
                'username' => 'mariano',
                'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO',
                'created' => new Carbon('2007-03-17 01:16:23'),
                'updated' => new Carbon('2007-03-17 01:18:31'),
            ],
            [
                'id' => 2,
                'username' => 'nate',
                'password' => '$2a$10$u05j8FjsvLBNdfhBhc21LOuVMpzpabVXQ9OpC2wO3pSO0q6t7HHMO',
                'created' => new Carbon('2008-03-17 01:18:23'),
                'updated' => new Carbon('2008-03-17 01:20:31'),
            ],
        ];
        $this->assertEquals($expected, $results);
    }

どうもテーブルには findやwhere,orderなどのメソッドがあり、それをチェインさせてクエリを組み立てるようです。これも他のActiveRecord系の記述とほぼ変わらない形式になります。 hydrate(false)を指定するとエンティティでなく従来の配列形式で値を戻すことができるようになっています。 メソッドチェインの最後のtoArray()はエンティティを配列形式で取得するという意味ではありません。CakePHP3からはfindの戻りがイテレータになります(配列ではないので注意が必要)。なのでイテレータからすべて配列形式で取得するという意味になります。

ではいくつかそれ以外の例を見てみましょう。

<?php
        $query = $table->find('all')
            ->select(['id', 'username'])
            ->where(['created >=' => new Carbon('2010-01-22 00:00')])
            ->hydrate(false)
            ->order('id');

たとえば取得するカラムを絞り込むためにはselect()を利用するようです。従来はfieldsで指定していたカラム名の配列です。

<?php
        $query = $table->find('list', ['fields' => ['id', 'username']])
            ->hydrate(false)
            ->order('id');

とはいえ、従来っぽい書き方もできるようです。

<?php
        $results = $table->find('all')
            ->find('threaded')
            ->select(['id', 'parent_id', 'name'])

!!!! findがチェインの中で2回呼ばれています。

これがCakePHP3のモデルだ!

findが2回とはどういうことなのか、ソースを調べてみました。 まずTable::find()とは何者なのか

<?php
    public function find($type = 'all', $options = []) {
        $query = $this->query();
        $query->select();
        return $this->callFinder($type, $query, $options);
    }

どうもクエリオブジェクトを取得して、それにselect()をかけて….. callFinder って何ですか?を返すみたいです。

<?php
    public function callFinder($type, Query $query, array $options = []) {
        $query->applyOptions($options);
        $options = $query->getOptions();
        $finder = 'find' . ucfirst($type);          // ココ
        if (method_exists($this, $finder)) {    // とココに注目
            return $this->{$finder}($query, $options);
        }

        if ($this->_behaviors && $this->_behaviors->hasFinder($type)) {
            return $this->_behaviors->callFinder($type, [$query, $options]);
        }

        throw new \BadMethodCallException(
            sprintf('Unknown finder method "%s"', $type)
        );
    }

find(‘all’) と呼ぶとfindAllメソッドがあるかどうか(ビヘイビアも含めて)調べて、なければ例外になる。 ということはTableにfindAllとかあるの?

<?php
    public function findAll(Query $query, array $options) {
        return $query;
    }

あるよ!

そういうことか、これがCakePHP3のモデルか。

  • 条件やら何やらメソッドチェインである
  • 旧来の配列形式に変換できる
  • findXxxで独自の条件などをメソッドチェインに組み込める(今ココ)

独自の条件をメソッドチェインに組み込む

まずPostsテーブルにfindOrderIdDescというIDの降順で検索するメソッドを作成します。

<?php
    public function findOrderIdDesc(Query $query, array $options) {
        return $query->order(['id'=>'desc']);
    }

でそれを使うときは

<?php
    $this->Posts = TableRegistry::get('Posts', $config); 
    $posts = $this->Posts->find('all')->find('orderIdDesc')->all();

みたいに書けるわけです。 もちろんいくつチェインしても良いです。 例えば様々なテーブルで利用できるファインダ(findXxxメソッド)をtraitで共通化するもの面白そうです。

このように新しいモデルはかなり期待の持てる作りにもなっています。 CakePHP3もAlphaになりました。 Alphaを使ってみての感想などはまた書きたいと思います