フレームワークで語るMVCの話 : PHP Advent Calendar #19

この記事はPHP Advent Calendarの19日目の記事です。

プログラマ10人集まれば、誰かMVCうんちく語るのが常。みんな大好きMVCの話です。僕は今年でPHPプログラマとして10年が経過しました。この節目の年に、これまで触ってきたフレームワークを振り返り、徹底的な個人的主観でMVCについて語っていきたい思います。忘年会シーズンでお疲れの皆様、ご安心ください。コード・ゼロでお届けします。

いろんな言語のいろんなフレームワークを触ってきたつもりですが、Javaはやってなかったんであまり詳しくないです。主にRails以降のフレームワークを見ていきます。

Railsの功績

PHPプログラマとしてRailsの登場で何にびっくりしたかというと、次の三つです。

ActiveRecordは魔法のように見えましたが、いずれ出てもおかしくないなぁと思ってたので、ショックでしたが想定内でした。でもRouterはほんとにびっくりでした。mod_rewritePHP版ぐらいは作ってみたのですが、これほどきちんと、リクエストとコントローラを柔軟につなげる方法があったのかと。目から鱗でした。cliと対話型シェルは言うに及ばず、もう単純にうらやましいと。

まあ、Railsの良さは、もうみんなご存知の通りです。功績に関しては、あえてこれ以上言うことは無いので、次に進みましょう。

Railsの罪

罪というほど悪いことした訳ではないと思いますがw、Railsのせいでこうなっちゃったというところを指摘しておきたいです。

まずコントローラがむやみに重要視されてしまったこと。特にモデル名の複数形を推奨する構成は、1モデル⇔1コントローラという、本来関係ない関係性がものすごく大事なものと誤解する原因になっているように思います。ちょっとアプリを組んでいけば、モデルを必要としないコントローラもあれば、逆もあります。たまに同じ名前のものの必要がでてくるというぐらいの関係。基本、無関係なもの同士なのに、初心者の人には、必ずペアで作るものと思っている人も見かけます。モデルの無いコントローラでもいいんです。

コントローラが重要視されることとは関係ないはずですが、やたらコントローラにロジックを突っ込みがちなのも初心者に見受けられる特徴です。コントローラを太らすことは、一番やっちゃいけないこと。これの理由も、はじめにコントローラありきで始まってしまうことに起因してるのでは。置き場所に困ったコードをコントローラにおいてしまうということだと思います。

それから、モデル=ActiveRecordという誤解も広げたのもRailsです。MVCのモデルは、データベースアクセスで得られるエンティティの管理だけではありません。ビジネスロジックと呼ばれる、いわゆる処理などもモデルに属するものです。それらは、データベースのテーブル単位よりはるかに大きな枠組みで、複数のエンティティを扱う必要があるのが普通です。モデル=ActiveRecordという考えにとらわれているとどっかで破綻します。ロジックをもっとモデルの一部として認識してもらいたいなと思ってます。

Ruby on Railsという名前に込められた思い

ちょっと話題それますが、この名前、素晴らしいネーミングだと思います。

最初はなんでRubyが先にきているのか?Rubyの上でRailsというフレームワークが動いているんじゃないの?Rubyに失敬なと憤慨してました。でも、たぶん勝手に想像すると、Rubyの自由さを賞賛した上で、それをレールにはめるとこんなに生産性が上がるんだぞということを意味しているんだな、と思い直しました。

Rails作っていた人たち、みんな思ってたと思うんですよ。Rubyすげーよ、みんな使おうよ!...と盛り上がっても、経験者はみんな書き方もバラバラで好き勝手書くし、逆に初心者にはとっかかりもわからない。こうすればいいじゃん!って言うレールを敷いてあげれば、みんなついてくるんじゃない?Ruby使ってくれるんじゃない?って言うことだったんじゃないかなーと。

ほんとのところはどうかわかりませんが、Rubyリスペクトなネーミングであることは間違いないところが素敵です。

CakePHP

このRailsに素直に影響を受けたPHPプログラマが作ったのがCakePHPです。表向きの構造はよく似ています。残念ながら、RubyPHPの言語としての違いがあるため、Railsの柔軟性までは再現できていませんが、よくここまでがんばっているな、という感じです。

CakePHPの特徴は何と言ってもコミュニティの強さでしょう。多くの人がCakeを使ってアプリを作っているというのは心強いことです。情報もたくさん見つかることも良いことです。プラグインもたくさん開発されてます。

欠点としては、Railsの構造をそのまま持ってきてしまっているので、同じ問題がそのまま移植されているように思います。つまり、分厚いコントローラの実装ということと、モデル=データベースアクセスという件です。

前者の分厚いコントローラに関しては、2.0になってリクエスト、リスポンスオブジェクトの導入により、だいぶ改善された気配も見えますが、まだまだ雰囲気的にはコントローラの実装を単に分解しただけ、読みやすくするためのリファクタリングレベルだと思います。いろんな部分が一枚岩、モノリシックなデザインとなっているため、自由に一部分を拡張していく、もしくは変更していくようには出来ていません。そのため、プラグインで改善できることにも限界があるという状況に見えます。

後者のモデルに関してはさらに状況は悪く、O/Rマッパというにはちょっと突っ込みどころ満載の実装です。一番納得できない仕様はオブジェクトではなく配列で結果が返ってくるということ。PHPRubyの表現力の違いをさっ引いてもそりゃねーだろという思いです。この仕様は2.0になっても変わっておりません。

幸い、拙作のCakeEntityを入れることで状況はだいぶ改善されます。同じ突っ込みをしたことがある同士の方、ぜひ導入をご一考いただければ。

https://github.com/kanshin/CakeEntity

また、他にもビューの実装にも疑問が残ります。PHP言語をそのまま使っています。この風潮、同時期の他のPHPフレームワークも同じように採用している主張なのですが、僕は個人的にはPHP言語がテンプレート言語と言い切るのには無理があると思いってます。むしろ、ロジックを記述する言語とビューを記述する言語では、ムードの面で多少違いがあることが大事だと思います。PHPがHTML記述言語として使えるんだからそのままビューとして使おう、という現状から無理矢理導きだしたように見えるこの仕様には違和感を感じます。

これについては、PHPで作られた優れたテンプレート・エンジンを利用することで対処できます。拙作のCakeSmartyは、導入が簡単なようにCakeで便利に使えるSmartyプラグインが同封されてます。興味がある方はお使いいただければ幸いです。

https://github.com/kanshin/CakeEntity

ただし、残念ながらしばらく2.0への対応予定はありません。あしからず。

CakePHPの問題点

上に上げた実装上の突っ込みどころ以外で、Cakeの最大の弱点と言えるのは、実は強みの裏返しで、ユーザー数の多さではないかと思います。実際に仕事でウェブアプリを書いている人が多く居るため、互換性の維持ということが錦の御旗として掲げられているように見えます。そのため、ちょっとした改善にも多くの時間がかかってしまうというのが現状のようです。

2.0になってモジュラリティは確実にあがってます。次へ向けた動きには期待したいところです。

Django

次に来るのはDjangoPythonです。なかなか無骨な渋いフレームワークです。同世代のフレームワークがばんばんバージョンを上げているなか、未だに最新版が1.3という安定性(そもそも1.0までも長かった)。自分的には、その無骨さから「漢(おとこ)」のフレームワークと思ってます。

さて特徴。Rails以後であることは変わりないと思いますが、なんとコントローラが無いです。無いってことは無いんですが、他のフレームワークでコントローラに定義されているアクションが、Djangoではview関数という形で実装されてます。viewを返す関数なのでview関数。コントローラ的な区切りは、Pythonの標準のパッケージを使います。

コントローラの層が薄いということは、直感的にMVC的に非常にきれいに組まれていることの証と見てよいかと思います。アプリケーション依存の部分と、リクエスト依存の部分のコードをきちんと意識してかき分けないと行けないので、自然とモデルとしてロジックが分離していく印象です。

またテンプレート・エンジンの出来が素晴らしい。Djangoのテンプレート・エンジンには、特に名前もついていないのですが、他に無い特徴としてテンプレートの継承と言う新しい考え方が実装されてます。これのおかげで、他のフレームワークでは一般的な、「レイアウト」という仕組みは必要なくなっています。ビューの本体であるテンプレートファイルに、自分は何のレイアウトファイルを拡張するのかを記述すれば良いのです。通常、レイアウトの管理はビューを作るコントローラの役割だったりするので、この継承が実装されたことでコントローラーの役割を軽く出来ているんだと思います。ちなみに、このDjangoのテンプレート・エンジンを参考に作られたのが、後述するSymfonyのTwigに引き継がれていきます。

Djangoのもう一つの特徴が、全体のモジュラリティの高さです。ミドルウェアと呼ばれる構造で、必要な機能をレイヤーとして積み上げていくことが出来ます。またPythonのデコレータという機能を使って、ビュー(他のフレームワークで言うアクション)に対して宣言的に機能を追加していくことが可能になってます。この辺の柔軟性には非常に感銘を受けました。

モデルに関しては、Railsと同じようにアクティブレコード型となっています。モデル=データソースという考え方です。Railsの爪痕、恐るべしというところでしょうか。

ただし、一点だけ改善されている点があります。それはフォームの扱い。RailsもCakeも、フォームという独立した概念は特になく、モデルを扱うフォームを簡単に扱うことが出来るようなビューのヘルパーが用意されているだけです。

Djangoではフォームは独立したオブジェクトです。もちろんモデルと連携できますが、単体で定義できるというのが大きなポイントです。フォームの処理は、えてしてコントローラに記述されがちです。入力されたデータをデータベースかO/Rマッパが理解できる形に変換する、またはフォームに埋め込める形に戻す、という処理、コントローラに書いてますよね?コントローラに書いたら再利用も出来なければ、機能テスト以上のことも出来ません。そこにロジックがあるなら、リクエスト/リスポンスからは独立させて、モデルレイヤーに分離して再利用可能にすべきです。フォームは、この要求にぴったりのオブジェクトになります。フォームの処理をブラックボックスに押し込めることが出来るのです。テストも簡単に出来ます。ビュー部分にひもづいたUIもウィジェットとして分離できます。

Symfony 2

さて、PHP Advent CalendarなのにDjangoネタを語ってきたのには訳があります。自分的にPHPフレームワークとしては決定版であろう、というSymfony 2に話をつなげたいからでした。

Symfony 2は設計思想も実装も素晴らしいです。徹底的に独立させる思想から、もはやフレームワークとは呼ばれなくてもよいよ的なことを作者が述べています(厳密には、呼びたければそう呼んでくれ的なニュアンスです)。

http://fabien.potencier.org/article/49/what-is-symfony2

では何か。Symfony 2はMVCフレームワークを作る上で必要な機能をライブラリの形で独立して作り上げたコンポーネントの集合体ということです。何言ってんだ?と思われるかもしれませんが、ここ重要です。Symfony 2は、MVCフレームワークという一人歩きしすぎた言葉によって表されてきたこれまでのフレームワークではないと言ってます。ウェブアプリを作るために必要な一通りのミドルウェア的に動作する部分を、それぞれ独立して実装した上で、とりあえずそれらをまとめて配布すれば従来のMVCフレームワーク的に使えるようにしておいたよ。でも、各パーツも好きに使ってね、という主張なんです。気前のいい話です。

細かく見ていくとキャッシュシステムからDOM解析、ルーティング、セキュリティまで、本当に一通りの機能が、それぞれほぼ依存関係なく実装されているのです。これはすごい。ちょっとがんばれば、例えばCakePHPSymfony 2のライブラリを使って書き直すことも出来そうな勢いです。

実際に、多くのプロジェクトがその成果物を使ってすでに開発を行っています。Symphonyの作者自らが設計したマイクロフレームワークであるSilex、CMSDrupalの次のバージョン8、振るまい駆動開発のBehatなど。わくわくしてくます。

中身に関してみてみると、個人的にはDjangoの良さを持っているように感じます。

薄いController層。コントローラクラスはありますが、別にプレーンなPHPクラスでもかまいません。コントローラの役割は、リクエストを受け付けてリスポンスを返す、極論するとそれだけというミニマムな作りも好感が持てます。

ビューは前述したTwigという強力なテンプレート・エンジン。Djangoのテンプレート・エンジンと同等で、継承もちゃんと使えます。

他のフレームワークでモデルと呼ばれるデータレイヤーへのアクセスは、Doctrine 2というライブラリが担当しています。これは、ついにActiveRecordからの呪縛を離れて、設定によってプレーンなオブジェクトの永続化を実現する方式になっています。JavaHibernateに近い感じがします(よく知りませんがw)。設定は、他のSymfony 2のコンポーネントと同様に、YAMLPHPコードのコメント内のアノテーションで記述できます。

フォームも独立しています。Djangoさながら、フォームオブジェクトをクラスから作ることも出来ますし、フォームビルダーでテンポラリなものを作ることまで出来ます。柔軟性ではDjangoに勝ります。

サービスと呼ばれる、汎用的なライブラリ提供機能も用意されています。これは何かしら処理を行うオブジェクトであれば何でも良く、どこからでも比較的簡単に呼び出せる仕組みを提供するとともに、それらの設定をアプリケーション外部から行えるようにする仕組みです。いわゆるDI的なものです。これにより、ビジネスロジックへのアクセスも、クラスへのハードコードを記述すること無く、安全に柔軟に行うことが出来るようになります。

ということで、今のところ自分の一押しはSymfony 2という状況です。

日本Symfony会の人たちが、積極的に日本語リソースを提供してくれていますので、ぜひ一度お試しあれ。

http://docs.symfony.gr.jp/

結局MVCってなんなんだ

Symfony 2の作者、Fabianさんがたどり着いたように、結局ステートレスなウェブでは、MVCはちゃんと機能しないんだと言うことだと思います。「Requestを受け付けてResponseを返す。HTTPの基本に立ち返ろう。難しいHTTPがらみのことは、設定された通りになんとかするから、その間のビジネスロジックの部分は、実装するあんたに任せる!」という原則に回帰した印象です。

MVCのVは、本来MともCとも関係を持ち、スマートに動作するものです。Webに置けるVは、結局一方通行、つまりHTTP的にはプレゼンテーションを提供するだけです。Vと呼ぶべき処理をしてないんじゃないか?そんな気分です。

MVCのMは、ほぼ全てのものです。ロジックというロジック、データというデータは本来Mです。データベースから出し入れするだけのものではないです。

逆にMVCのCは、ただの薄っぺらいグルーコードになるべきです。HTTPのリクエストがステートレスなので、本来の処理の前後でしなくては行けないことが多々あるように見えますが、本質的には、単純な条件分岐を行い、入力チェックを経た後に本来の処理をモデルに依頼する、返ってきた結果をテンプレートに渡すという処理です。Symfonyではアノテーションでそうした定型な処理の一部を記述できるようになってます。コードから設定に機能が移動してます。

昔のJavaXML地獄になるのはまっぴらごめんですが、そろそろ既存のプログラミング言語に依存しないコントローラ記述言語というものが登場しても良いのではないかと夢想したりしてます。ルーターの実装の先にありそうな気がします。

ということで、全てをひっくり返すようですが、最近はウェブにおいてはMVCなんてどうでもいいんじゃないの?という考えになってきてしまってますw。そういう意味でも、今の気持ちにSymfony 2がしっくり来ているんではないかなと思う次第です。

ちなみに、Smalltalk方面には、このウェブの常識を覆すSeasideというフレームワークもあるそうです。いずれ触ってみたいもののひとつです。といいつつ勧められて2年間、まだ勉強もできていませんがw

http://www.ogis-ri.co.jp/otc/hiroba/technical/seaside/seaside1/index.html

さらばPHP

私事ですが、今年いっぱいで今勤めている会社を離れて、来年からは新しいことを始めることになりました。10年間PHPをメインに仕事をしてきましたが、それも今年で一区切りです。さらばPHP、そういう思いもありMVCについて、そしてフレームワークについて自分の思うところをつらつらと書かせていただきました。

さて、Advent Calendar、明日は [twitter:@slumbers99] さんです。お楽しみにー。