シンプル&パワフルなPHPライブラリ rhacoを使ってみよう!

第6回クールなURLを実現する Urls

前回解説したViewsをつかうと、簡単なアプリケーションの作成ならばメソッド一つで実現できるようになりました。

今回は、ViewsとUrlsを組み合わせて、URLを自在に設計してみます。また、まとめとして今まで開発してきたすべてのソースのリポジトリを作成してみました。

Urlsとは

CakePHPなど、最近のフレームワークでは標準で搭載されている「Cool URI」と呼ばれるようなURI設計を可能にするライブラリです。

正規表現を使って、自由自在にURLの設計ができるようになります。

Urlsをつかう

使い方は非常に簡単です。Urlsクラスのparserメソッドに、URLマッピングの配列を引数として渡すと、UrlsがURLを解析して該当するアクションを実行し、HtmlParserのインスタンスを返却します。

使用例

ここでは、TodoモデルのCRUDを実現する簡単な定義を紹介します。

index.php
<?php
require_once '__init__.php';
Rhaco::import('generic.Urls');
Rhaco::import('model.Todo');

$patterns = array(
    // リスト
    '^$' => array(
        'class' => 'generic.Views',
        'method' => 'read',
        'args' => array(new Todo(), new C(Q::eq(Todo::columnClose(), false))),
        'template' => 'list.html',
    ),
    // generic.Views を使用する場合は、設定を省略できます。
    '^detail/(\d+)$' => array('method' => 'detail', 'args' => array(new Todo(), new C(Q::fact()))),
    '^create$' => array('method' => 'create', 'args' => array(new Todo(), null, Rhaco::url())),
    '^update/(\d+)$' => array('method' => 'update', 'args' => array(new Todo(), null, Rhaco::url())),
    '^delete/(\d+)$' => array('method' => 'delete', 'args' => array(new Todo(), null, Rhaco::url())),
);
$parser = Urls::parser($patterns);
$parser->write();

Urlsで指定できる値は次の通りです。

意味
class実行するメソッドを持つクラス
method実行するメソッド名
argsメソッドに渡す引数
templateテンプレート名
var追加でテンプレートにセットする値

この状態でindex.phpにアクセスすると、PATH_INFOに何もセットされていないので一番最初に定義した⁠^$⁠の部分にマッチし、一覧画面が表示されます。

つぎに、Apacheの場合はmod_rewriteの設定を行います。mod_rewriteに関しては、rhacoのセットアップにて設定を自動で書き出すことができます。

setup/project.xmlの最初の行を次のように書き換えます。

変更前
<project rhacover="1.6.1" version="0.0.1" name="kaeru" ...
変更後
<project rhacover="1.6.1" version="0.0.1" name="kaeru" pathinfo="index.php" ...

変更後、setup.phpにアクセスすると初期処理を実行するボタンが出現します。

画像

このボタンをクリックすると、アプリケーションのルートパス上にmod_rewriteの設定が記述された以下のような.htaccessが出力されます。

.htaccess
RewriteEngine On
RewriteBase /kaeru/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ index.php?%{QUERY_STRING}&pathinfo=$1 [L]

完了ボタンを作成する

作成したTODOを完了するためには、本来であれば専用のクラスとメソッドを作ってアクションを実現することになりますが、ここでは少し工夫してUrls+Viewsの機能だけで完了のアクションを作成してみます。

先に述べたUrlsの指定にupdateの項目が既にあるので、強引ですがテンプレートの変更で対応します。

resources/templates/list.html(変更点のみ)
  <table id="todo" rt:param="object_list" rt:var="object">
    <tbody>
      <tr class="even">
        <td>{$object.factCategory.toString()}</td>
        <td><a href="{$rhaco.url('detail')}/{$object.id}">{$object.subject}</a></td>
        <td>{$f.text2html($object.description)}</td>
        <td>{$object.captionPriority()}</td>
        <form action="{$rhaco.url('update')}/{$object.id}" method="post">
          <td>
            <input type="hidden" name="category" value="{$object.category}" />
            <input type="hidden" name="subject" value="{$object.subject}" />
            <input type="hidden" name="description" value="{$object.description}" />
            <input type="hidden" name="priority" value="{$object.priority}" />
            <input type="hidden" name="close" value="1" />
            <input type="submit" value="完了" />
          </td>
        </form>
      </tr>
    </tbody>

このように、http://localhost/kaeru/update に対して、closeフラグを強制的に1(true)にしてカラムを更新させると、一覧に表示されないようになります。

Criteriaでリレーションを扱う

Q::fact()

参照関係のあるデータを取得するとき、親テーブルのデータも同時に取得できるCriterion指定です。

例えば、Urlsで指定したリスト表示部を次のように書き換えると、テンプレート内で {$object.factCategory.name} のようにして親のオブジェクトを参照できるようになります。

    // リスト
    '^$' => array(
        'class' => 'generic.Views',
        'method' => 'read',
        'args' => array(new Todo(), new C(Q::eq(Todo::columnClose(), false), Q::fact())),
        'template' => 'list.html',
    ),

Q::depend()

参照関係のあるデータを取得するとき、子テーブルのデータも同時に取得できるCriterion指定です。

例えば、Urlsに次のように定義すると、カテゴリオブジェクトのdependTodos変数に、カテゴリを参照しているTodoオブジェクトの配列がセットされます。

Urlsの例
    '^cat/(\d+)$' => array(
        'method' => 'detail',
        'args' => array(new Category(), new C(Q::depend()))
    ),
対応するテンプレートの例(generic/category_detail.html)
<rt:extends href="../list.html" />
<rt:block name="content">
  <h2>{$object.name}</h2>
  <p><a href="{$rhaco.url('create')}">Add new todo</a></p>
  
  <table id="todo" rt:param="{$object.dependTodos}" rt:var="todo">
  
 (以下省略)

まとめ

このように、UrlsとViewsを組み合わせると、URLのマッピング定義とテンプレートだけで簡単なアプリであればすぐに作成できてしまいます。

参考資料

これまで開発してきたアプリケーションのソースコードをgithub上にアップロードしました。多少発展的なコードも含めてみたので、ぜひ挑戦してみてください。

gitクライアントからのアクセスのほか、zipやtgzでのダウンロードも可能です。

おすすめ記事

記事・ニュース一覧