【PHP/CakePHP】phpadvent2010 Day21「ピュアPHPでも便利なCakePHPを使おう」

PHP Advent Calendar jp 2010 21日目担当のMASA-Pです。
CakePHP界隈ではいろいろとやらせていただいていますが、PHPコミュニティの方はなかなか日程等があわない関係もあって不参加状態ですので、もしかしたら初めましての方もいらっしゃるかもしれません。一応こんなものなんかを公開させていただいております。またこんな本とかこんな本なんかを執筆させていただだいておりますので、もし興味がありましたら是非よろしくお願いします。できれば買って!(笑)

さて、今回はPHP Tipsという事なのですが、非常にお恥ずかしい話ですが当方は既にCakePHPがないと生きていけない体になっております(笑)。当方としましてもそういう道連れ的な方を一人でも多く製造したい次第ですので(笑)、布教活動がてら「CakePHPに存在している超便利な機能をライブラリとして使ってしまおう!」ということで記事を書いてみたいと思います。実例としては前日のomoonさんの記事とネタが被っているかもしれませんが、まあ続編と思ってもらっても全然構いませんです(^^;。

さて、CakePHPのフレームワークの機能についてここで多くを語るのはやめるとして、CakePHP内には「便利クラス」なるものがいくつか存在します。
特に一押しなのが、今回取り上げさせていただく「Setクラス」です。このクラスは、配列の操作をより便利にする機能を持っています。PHPは標準でも他の言語と比べて配列を自在に操れる方だとは思いますが、かゆいところに手が届かないものもいくつかあります。しかしこのSetクラスを使うと、その辺が簡単に解決できたりする場合があります。
ちなみにどんな機能を持っているのかといいますと…

  • Set::merge()
    配列を再帰的に結合する
  • Set::filter()
    空の要素を取り除く
  • Set::pushDiff()
    配列の差分を付加する
  • Set::map()
    配列をオブジェクトにマッピングする
  • Set::numeric()
    配列内の要素が全て数字かをチェックする
  • Set::enum()
    指定するキーの値を見つける
  • Set::extract()
    配列から指定キーの値を抽出
  • Set::format()
    フォーマットに従って値を抽出・生成する
  • Set::matches()
    配列内の値が条件にマッチしているかを調べる
  • Set::insert()
    配列内に値を挿入する
  • Set::remove()
    配列から指定要素を削除
  • Set::check()
    指定パスの存在をチェック
  • Set::diff()
    配列の差分を抽出する
  • Set::isEqual()
    2つの配列の内容が一致しているかをチェックする
  • Set::contains()
    配列の値が内包されているかをチェックする
  • Set::countDim()
    配列の次元数を調べる
  • Set::normalize()
    配列内の値を正規化する
  • Set::combine()
    2つ以上のパスから新たな連想配列を生成する
  • Set::reverse()
    オブジェクトを連想配列に戻す
  • Set::flatten()
    連想配列の構造をフラット化する
  • Set::sort()
    パスで示した要素を対象にソートする
  • Set::apply()
    抽出結果をコールバック処理

上記メソッドについての詳細はここでは説明しきれませんので、こちらをご覧いただくか「ポケット詳解 CakePHP辞典」を参照してください。できれば買って!(笑) 今回はこの中のSet::extract()とSet::combine()を動作サンプルとして掲載します。

さて前置きはこのくらいにして、さっそくピュアPHPコード内でCakePHPのコードを使用する方法をご紹介しましょう。

1:CakePHPのコードを配置する

先でも後でも良いのですが、とりあえずCakePHPコードを配置してしまう事にします。
今回は、ドキュメントルート内に利用元のピュアPHPコードを展開し、「includes/」内に利用したいCakePHPコードのファイルのみをを入れます。ドキュメントルートとは別の一にCakeのパッケージを丸ごとインストールして、そこを参照したりしても良いかもしれません。
CakePHPはこちらからアーカイブを入手し、その中の「cake/libs/set.php」をincludesにコピーします。また、Setクラス内では同じくCakePHPライブラリである「Stringクラス」も呼び出していますので、「cake/libs/string.php」もコピーします。その他にもクラスを呼び出しているものがありますがそれは次のステップで。

2:ダミークラスを作成する

CakePHPのコードを使うための、超最低限の機能(?)を有するコードを作成します。
includes内に「cake.php」ファイルを作成します。内容は次のようにします。


<?php
define('DS', DIRECTORY_SEPARATOR);

//CakePHP内での基底クラスのダミー
//
class Object {
}

//CakePHPのAppクラスの代替メソッド
//
class App extends Object {

	//ファイル読み込み(App::import()の代替メソッド)
	//
	function import($type, $class){
		include(dirname(__FILE__).DS.strtolower($class).'.php');
	}
}

Objectクラスは、CakePHPのほとんどのクラスが継承している基底クラスで、ライブラリでも使用している場合があります。ダミークラスを作成して問題を起きにくくしておきます。
App::import()はCakePHP内で自在にincludeするための便利メソッドで、これもライブラリ内で用いられています。SetクラスでもStringクラスを呼び出すために使用しています。本来は$typeでクラスの種類(ControllerとかModelとか)を指定するのですが、今回の実装ではincludesディレクトリに全て突っ込んでしまうため、無視します。$classはupperキャメルケースで表現されていますのでアンダースコア形式に変換する必要がありますが、今回はインチキしてstrtolower()してしまっています。厳密にやるのなら「cake/libs/inflector.php」内のメソッドを用いるとできると思います。

3:目的のピュアPHPコードの作成

最後に目的のコードを記述します。今回は次のようなコードで実験する事にします。


<?php
//Includes
//
require_once('includes/cake.php');
require_once('includes/set.php');

//Example : CakePHPのSetクラスをピュアPHPでも便利に使ってみる!
//

//データ
//
$results = array(
	array(
		'User' => array(
			'id' => '1',
			'loginname' => 'saito',
			'password' => '0123',
		),
		'UserProfile' => array(
			'id' => '101',
			'name' => '斉藤',
			'type' => 'baseball',
		),
	),
	array(
		'User' => array(
			'id' => '5',
			'loginname' => 'honda',
			'password' => '0123',
		),
		'UserProfile' => array(
			'id' => '102',
			'name' => '本田△',
			'type' => 'soccer',
		),
	),
	array(
		'User' => array(
			'id' => '10',
			'loginname' => 'ishikawa',
			'password' => '0123',
		),
		'UserProfile' => array(
			'id' => '103',
			'name' => '石川',
			'type' => 'golf',
		),
	),
);

//処理1:名前だけを抜き出す
//
$arr = Set::extract('/UserProfile/name', $results);
var_dump($arr);
echo"<br />\n";

//処理2:User.id = 5の名前だけを抜き出す
//
$arr = Set::extract('/User[id=5]/../UserProfile/name', $results);
var_dump($arr);
echo"<br />\n";

//処理3:(User.id) => (UserProfile.name)の連想配列を入手する(select文生成などで利用)
//
$arr = Set::combine($results, '{n}.User.id', '{n}.UserProfile.name');
var_dump($arr);

実行結果は次のようになります。

array(3) {   [0]=>   string(6) "斉藤"   [1]=>   string(6) "本田△"   [2]=>   string(6) "石川" }
array(1) {   [0]=>   string(6) "本田△" }
array(3) {   [1]=>   string(6) "斉藤"   [5]=>   string(6) "本田△"   [10]=>   string(6) "石川" }

SetクラスはCakePHPユーザでもあまり使われていないかもしれないのですが、この便利さは魔力を秘めているというのか使い出したら止まりません(^^;。上記2つはデータベーステーブルから特定の値を抜き出したりする場合に威力を発揮すると思います。また、今回触れてはいませんが、Set::merge()やSet::sort()なんかもかなり便利です。array_merge()は1次元目の配列要素に対してのみマージが行われますが、これは多階層の配列に対して再帰的にマージが可能です。またsortについても、特定のパスの値に対してソートをかける事ができるので、例えばとりあえず取ってきた複数レコードのデータをid順に並べ直したりといったことが簡単にできます。
しかしSetクラスは少々CPU%を使う仕組みだと思うので、peclかなんかでモジュールを利用する形で作ったりすると良いのかもしれませんね。いや、できればPHPで標準化されて欲しいものばかり!(笑)

ぱっと見なのですが、Setクラスだけでなく次のクラスライブラリもたぶん同様の方法で使えると思います。

  • Inflectorクラス
    アルファベッド表記の名前を単数形←→複数形変換したりキャメルケース←→アンダースコアに変換したりできるクラス
  • Stringクラス
    UUIDを生成したり、テンプレート文字列に要素を挿入したりできる機能のあるクラス
  • NumberHelperクラス
    通貨など数値関連のフォーマッティングをしてくれるクラス。なおAppHelperクラスを上記のObjectクラスのようにダミー化する必要があります
  • TImeHelperクラス
    時間に関するフォーマッティングをしてくれるクラス。strtotime()等の機能をより便利に拡張します

他のクラスにも有用なものがありますが、ちょっと大がかりな工夫をする必要がありそうなので、興味があったらチャレンジしてみてください(Validationクラスくらいなら比較的簡単にけるかもしれない)。

アマゾンのサーバでエラーが起こっているかもしれません。一度ページを再読み込みしてみてください。

明日は kashioka さんの担当です。よろしくお願いします!
それではMerry Xmas!!


コメントは受け付けていません。