MochiPHPは、以下の仕組みをサポートする軽量なPHP用Webフレームワークです。
- ページ毎にクラスとテンプレートを書くページ指向
- Form部品はそれぞれクラスとしてコンポーネント化
- プロパティアクセサの自動生成をサポートした永続オブジェクト(ActiveRecord)
サポートするPHPのバージョンは、プロパティアクセサの自動生成を利用する場合は5.3.2以上、 利用しない場合であれば(自前でgetter, setterを書く)、おそらく5.1以上であれば動作するはずです (5.1.6で動作確認済み)。
他のメジャーなPHPフレームワークを使わずにわざわざMochiPHPを使う理由があるとすれば、 以下のようなことが挙げられるかもしれません。
- 必要最小限で簡潔なコード
- 全てのコードを眺めてもそれほど時間はかかりません(一つ一つのメソッドも平均数行程度)。コード量を抑えたまま最低限の機能を確保するというポリシーです。気に食わない部分はすぐに直せますし、拡張するのも容易です。
- ドメインモデル重視型開発のサポート
- モデルに情報やロジックを集約しやすいフレームワークを目指します。
まずApacheが以下の条件を満たしていることを確認します。
- mod_rewrite が有効になっている事
- .htaccess が利用でき、
/webroot/.htaccess
にあるディレクティブの設定が許可されている事
/webroot
以下のファイルを、ドキュメントルート以下の好きな場所にコピーします。
- Linux、Mac OS Xなどの環境では、
internals/app/templates_c
ディレクトリがWebサーバーから読み書きできるようにパーミッションを設定して下さい。
front.php
がドキュメントルート直下にある場合、以下のURLへアクセスします。
http://localhost/hello
以下のようなメッセージが表示されれば準備完了です。
Hello, world!
上記の例でアクセスしたページ/hello
は以下の二つのファイルから構成されています。
/internals/app/pages/hello.php
<?php
require_once('mochi/Page.class.php');
class HelloPage extends Page
{
public $name;
function onRender(Context $context) {
parent::onRender($context);
$name = is_null($this->name) ? 'world' : $this->name;
$this->addModel('message', "Hello, {$name}!");
}
}
?>
/internals/app/templates/hello.tpl
{$message}
ポイントは以下、
/hello
ページに対応するのは、app/pages/hello.php
ファイルに定義されたHelloPage
クラスと、app/templates/hello.tpl
ファイルに定義されたSmartyテンプレート。HelloPage::onRender
はPageクラスに定義済みのメソッドで、テンプレートが出力される直前に呼び出される。ここでは、addModel
というメソッドを使って、テンプレートに渡すデータを定義している(ここではmessage
という名前のデータを定義)。- テンプレート(
hello.tpl
)では、HelloPage
で定義されたデータを参照しながら、ページの見た目を定義する。 - Pageクラスにpublicのプロパティを定義すると、HTTPパラメータを受け取ることができる。上記の例では、
$name
というプロパティが定義されている。試しに、URLを/hello?name=marubinotto
とすると、Hello, marubinotto!
と表示される。
MochiPHPでは、テキストフィールドなどのForm部品をクラスライブラリとして提供しています。 このライブラリを利用することによって、Formにまつわる面倒な詳細 (複雑なHTMLやバリデーション処理など)を書かずに済みます。
以下のようなTwitterっぽいアプリケーションがサンプルプログラムに含まれています(/form
)。
このアプリケーションは/hello
と同様、以下の2つのファイルから構成されています。
以下、順を追って説明します。
まずは、Form
クラスを利用してFormの構成を定義します。
$this->form = new Form('form');
$this->form->addField(new TextArea('content',
array("cols" => 50, "rows" => 3, "required" => true)));
Formに対してTextArea
のような入力フィールドを追加(addField
)していきます。
それぞれの入力フィールドの設定についてもここで行います。例えば、"required" => true
という設定はこの項目について入力が必須であることを表しています。
この設定により、以下のようなバリデーション処理が自動で行われます。
通常、Formの定義は Page::onPrepare
というメソッドで行います。
onPrepareは、ページで行われる主要な処理(パラメータの設定やイベントハンドラなど)
の前に呼び出されます。
以下では、このFormがSubmitされた際に呼び出されるメソッド(イベントハンドラ)を設定しています。
この設定ではページクラスのonSubmit
というメソッドが呼び出されます。
$this->form->setListenerOnValidSubmission($this->listenVia('onSubmit'));
Page::addControl
メソッドを使ってこのFormをページに登録します。
これによって、テンプレートからこのFormを参照できるようになります。
$this->addControl($this->form);
テンプレート側では、以下のようにFormと入力フィールドの配置を記述します。
{$form->startTag()|smarty:nodefaults}
{$form->renderErrors()|smarty:nodefaults}
{$form->fields.content->render()|smarty:nodefaults}
<br/><input type="submit" value=" Send "/>
{$form->endTag()|smarty:nodefaults}
Formライブラリの機能を利用するため、テンプレートでは以下のように スタイルシートやJavaScriptのファイルを指定しておく必要があります。
<link type="text/css" rel="stylesheet" href="{$basePath}/assets/mochi/control.css"/>
<script type="text/javascript" src="{$basePath}/assets/mochi/control.js"></script>
以下は、FormがSubmitされた際に呼び出されるメソッドです。
function onSubmit($source, Context $context) {
// Store the sent data
array_unshift($this->entries, $this->form->getValue('content'));
$context->getSession()->set('entries', $this->entries);
// Redirect After Post
$this->setRedirectToSelf($context);
return false;
}
Formによって送信されたパラメータをgetValue
で取得し、
それを配列に追加してからセッションに登録しています。
ここは通常、データベースなどを利用したトランザクションを行う場所です。
以上の処理が終わった後は、Redirect After Postパターンに従い同じページにリダイレクトさせます。
戻り値が false
になっているのは、リダイレクトするので、
ここでは以降の描画処理などをスキップする、という指定です。
オブジェクトをデータベースに保存するのはとても簡単です。 以下は永続化オブジェクトの定義例です。
class BlogPost extends PersistentObject
{
const TABLE_DEF = "
create table %s (
id integer unsigned not null auto_increment,
title varchar(255) not null,
content text,
register_datetime datetime not null,
update_datetime datetime not null,
primary key(id)
) TYPE = InnoDB;
";
protected $p_title;
protected $p_content;
protected $p_register_datetime;
protected $p_update_datetime;
}
まず、オブジェクトを保存するテーブルについては、
既に構築済みのデータベースを利用する事もできますし、
上の例のようにクラスに TABLE_DEF
としてCREATE文を定義しておけば、
専用のツールやライブラリを利用してテーブルの作成を行う事ができます。
テーブル名はクラス名を元にして決定されます。
BlogPost
の場合、テーブル名は blog_post
となります
(クラス定数 TABLE_NAME
を利用すれば、テーブル名を自分で指定可能)。
注意すべきは、オブジェクトのプロパティの命名規則だけです。
$p_column_name;
という名前のプロパティがある場合、
このプロパティは column_name
という名前のカラムにマッピングされます。
これらのプロパティはprivateかprotectedにする必要があります。
さらに、p_
で始まる名前のプロパティにはアクセサが自動的に提供されます。
上のBlogPost
の例だと、
$instance = new BlogPost();
$instance->title = 'Hello';
$this->assertEquals('Hello', $instance->title);
のような形でプロパティにアクセスできます。
このアクセサは、getTitle
, setTitle
というメソッドを定義する事で読み書きの処理をオーバーライドすることができます。
BlogPost
のような永続オブジェクトには、
対応するリポジトリクラスを定義するのがMochiPHPの流儀です。
class BlogPostRepository extends PersistentObjectRepository
{
function __construct(Database $database) {
parent::__construct($database);
}
function getObjectClassName() {
return "BlogPost";
}
}
リポジトリは、オブジェクトをデータベースから取り出す際の窓口となるものです。 以下はリポジトリを使ってデータベースのCRUDオペレーションを行うテストプログラムです。簡単なCRUDであればSQLを書く必要はありません。
// Create
$instance = $this->repository->newInstance();
$instance->title = 'MochiPHP';
$instance->content = 'MochiPHP is a lightweight framework for PHP.';
$now = $instance->formatTimestamp();
$instance->registerDatetime = $now;
$instance->updateDatetime = $now;
$instance->save();
$id = $instance->id;
$this->assertEquals(1, $this->repository->count());
// Read
$instance = $this->repository->findById($id);
$this->assertEquals('MochiPHP', $instance->title);
$this->assertEquals('MochiPHP is a lightweight framework for PHP.', $instance->content);
// Update
$instance->title = "What is MochiPHP?";
$instance->updateDatetime = $instance->formatTimestamp();
$instance->save();
$instance = $this->repository->findById($id);
$this->assertEquals('What is MochiPHP?', $instance->title);
// Delete
$this->repository->deleteById($id);
$this->assertEquals(0, $this->repository->count());
生まれたばかりのプロジェクトなのでドキュメントがまだ貧弱ですが、順次拡充していく予定です。
最新の状況を知るには以下のブログを追いかけてみて下さい。
ドキュメントと並行して、サンプルアプリケーションの拡充も行っていきます。
/index
にアクセスすると、これらのアプリケーションへのインデックスを見る事ができます。
サンプルアプリケーション Simple Blog (/blog/index
) は、
上記で紹介したフレームワークの機能を一通り網羅した実例になっています。
Form処理やオブジェクトの永続化を利用したCRUDオペレーション、
Ajaxなどのプログラム例が含まれています。