Zend FrameworkでのモデルとZend_Formの王道的使い方(の予告)

Zend_FormはZend Frameworkを特徴付けるコンポーネントの一つです。ZFではモデル実装部分についてはあまり多くが語られず、基本自由というスタンスなんですが、zf-mvcというMLとZF開発者のBenjamin EberleiさんとMatthew Weier O'Phinneyさんのブログで具体的な話が出ていて面白いのでまとめることにしました。勝手に王道宣言をしていますが、要するに紹介ネタ(の予告) です。

今のところ上記ブログ3つでだいたいでFAです。
いずれ、別の方法論も生まれてくると思います。個人的には、もう一段階進めるつもりですが、それはオレオレ色が強くなるので別エントリにします。
あと一応、モデル作成とフォーム作成の基本部分は、quickstartが充実してきたのでそちらを見てもらった方がいいかもしれません。

MLが熱い

昨日の朝方(日本時間)、fw-mvcのMLに興味深い投稿がありました。

:Ed Lazor

$model->display();

っていう実装を考えてるんだけど、OOP的にこれはまずいの?

という感じの投稿です。このスレがよく伸びていていました。(現在21レス)

よくある反論 SRP( Single Responsibility Principle)

Edが提示したコードに対して、想定内な反論は、SRPを犯すというもの。モデルに表示機能を付けるなよという。
MVCの初歩的な説明ではモデル、ビュー、コントローラーを分離します。コントローラーでモデルをビューに投入してビューが出力を行うという一連の流れが基本になります。ZFのマニュアルにもそのパターンの解説があります。Edの提示したコードはそれに真っ向反するわけですね。
Edからは、$model->display();っていうコードの他、Viewをインジェクションした形のサンプルも出てきて、あぁ、DIね、いやそれはDIじゃないみたいないろいろと参考になる話が流れました。

それってZend_Form的だよね

視点を変えてみるとEdが提示した内容はZend_Formの実装そのものでした。Edが提示するモデル構想は、UIモデル層の実装です(たぶん)。セマンテックなUIを動的に構築するには、MVC分離の橋渡しをするmediator的クラスが必要でそれをUIモデルと考えていいかと思います。
本来の流れでいえば、UIモデル用のコンポーネントをプロポーザルするところですが、いかんせん、Zend_Formがその機能の90%を網羅してしまっています。そもそも、formはUIモデルの1要件に過ぎないんですが、formに特化しすぎているところが難点です。また、名前をZend_Formにしているために、Form用のコンポーネントになんでモデル?なんでコントロール?みたいな入らぬ混乱を招いているようにも思えます。

Zend Frameworkの残念なところ

ZFでちょっと残念な部分は、クラス設計が具体的すぎて、抽象的な振る舞い部分でインターフェースを共有していない部分と思っています。この点ではyii Frameworkの方が優れていると思います。Zend_Form_Elementはちょいと抽象化すれば、フォームエレメントだけじゃなく、モデルのフィールドとしても使える。Zend_Form_Decoratorも一般化しておけば、ビューでも使いまわしできる。が、それをしない・・・。なんでそうしないのか、個人的にとても興味がありました。
一つには、Zend Frameworkコンポーネント開発が個々のコンポーネントに対するプロポーザルベースで、綿密に計画されてテストされたという"前提"で進められているので、コンポーネントを跨いだ概念の共有が行われにくいという性質があるかもしれません。コンポーネント間の依存を減らす目的もあるでしょう。しかし、設計的には無駄があるように思います。たとえば、Zend_FormのバリデーションはZend_Filter_Inputの焼き直しですが、Zend_Filter_Inputを内包せずに、独自実装にしてあります。これも粒度の問題でしょうか。コンポーネント間の依存を少なくするためにコピペコードを使うのが正解とは思えないのですが。

とりあえず言っとけ

そんな気持ちもあったので、MLにその辺の話をぶつけてみました。
ZFの場合、MLであれissue trackerであれ、とりあえず言いたいことは言っとけみたいなオープンな雰囲気があっていいです。実際にそれでコンポーネントに変更が加わらなくても、言ったことを契機にして、他のコンポーネントで我慢するか、自作コンポーネントを作るかという自分の方針が決定しやすくなるだけでなく、他の開発者がどう実装しているかという紹介を貰えることがあるので、ありがたいです。

話の成果

まぁ、いろいろやり取りした現段階の成果として、Edのリードがうまかったのか、モデルからフォームの自動育成に至るベストプラクティスが見えてきた気がします。
ZFのMVCって何ぞ?というのが流れを追って確認できる良スレだったなぁと。

SRP?おいしいの?それ。

ところで、Zend_FormがSRPに反すると言う話については、ZFのメイン開発者のMatthewがブログで、

As for assigning the model to the view, this is actually classic MVC. I agree with you that it can open the door for malicious actions on the parts of those working with the views, but that's a case for discipline, IMO. That said, it's also none too difficult to add functionality to the model that makes it immutable, and I will likely cover that situation in a later post.

http://weierophinney.net/matthew/archives/200-Using-Zend_Form-in-Your-Models.html

という具合に、古典的なMVCではきれいに分離したかもしれないが、意外とそうでもないよ。という話を提示しています。
MLでのMatthewの発言から拾うと

That said, I agree with you -- your models themselves shouldn't be
renderable, though I will sometimes recommend that your service layer
objects (which consume your domain models) have that capability.

データアクセスレイヤーに表示機能を持たせるのはどうかと思うけど、サービスレイヤーだったら機能性が向上すると思うぞ

Zend Frameworkを使う場合、Matthewの考え方についていけるかどうか、彼が他のコントリビュータとどんな会話をしているかが今後の行く先を見極めるポイントになると思います。
それでも、古典的なMVCを離れて、サービスレイヤーのモデルにmediator的役割を持たせるクラスでハンドリングするときは、mediator自身が具体的な仕事をやらないように注意するか、肥大化したときは、やっぱり思い切って分解するということは考えた方がいいと思います。
個人的には、サービスレイヤーよりも、もう一枚上のレイヤーでUIモデル層を作って、そこにmediatorを配置すべきなんじゃないか?という気はしています。って、結局は粒度の問題に落ちて行くわけですけど。


タイトルの内容については、また後日。