データソース作成の覚え書き

・コンストラク
第一引数でdatabase.phpで設定した値を取得出来る

・connect、close
データ取得時にデータソースと接続したり切断したり。
closeは必要ない種類のデータソースであっても、関数自体は必要

・listSources
テーブルの一覧を配列で返す
Bakeの時のテーブル一覧。この中にモデルの$useTable($table)に一致するものが無いとエラーになる。

・describe
フィールド情報
$this->__cacheDescription($this->fullTableName($model, false), $fields);でファイルキャッシュを作成している

・read
findから呼ばれる。
['モデル名']['カラム名'] => 値の形式の値を返す
$queryに検索パラメータやPaginatorのデータが入っているので、適宜検索処理を実行する

・fullTableName
mySQL側に指定するdbo.形式のテーブル名を返す

・calculate
paginateの時のページ数の算出をするときとかfind(count)の時につかうデータ件数

CakePHPのSearchPluginでbetweenを使ってみたかった。

SearchPluginとの組み合わせでBetweenを使用する場合のコードを書いてみた。
機能を最小限に絞って、idを範囲指定、textをlikeで検索する。

model側のコード
idの内容はarray(id=>"開始|終了"); みたいになってる。

	var $actsAs = array('Search.Searchable');
	
	var $filterArgs = array(
		array('name' => 'id', 'type' => 'expression', 'method' => 'idBetween', 'field' => 'Sample.id BETWEEN ? AND ?'),
		array('name' => 'text', 'type' => 'like'),
	);

	public function idBetween($data){
		$input = $data['id'];
		$inputs = explode('|', $input);

		if($input) {
			$coditions = $inputs;
		}
		return $coditions;
	}

コントローラ側のコード
idはArrayで渡ってくるのでtypeはcheckboxで受ける。

	var $components = array('Search.Prg');
	
	var $presetVars = array(
		array('field' => 'id', 'type' => 'checkbox'),
		array('field' => 'text', 'type' => 'value'),
	);

	function index() {
		$this->Prg->commonProcess();
		$this->paginate['conditions'] = $this->Sample->parseCriteria($this->passedArgs);

		$this->set('samples', $this->paginate());
	}

ビューのコード
view側のidの開始、終了はSample.id.0, Sample.id.1のフィールド名で配列になっている。
フィールド名にid.0だけ指定したらテーブルの情報が付かないのでSample.id.0という形式で指定。本当はid.start, id.endとしたいけどSearchPlugin側で加工した$this->dataの内容から連想配列のkeyが抜けるのであきらめた。

	<h2><?php __('Search box');?></h2>
	<?php echo $this->Form->create('Sample', array('url' => array_merge(array('action' => 'index'), $this->params['pass']))); ?>
	<?php echo $this->Form->input('Sample.id.0', array('type' => 'text', 'label'=>'ID Start')); ?>
	<?php echo $this->Form->input('Sample.id.1', array('type' => 'text', 'label'=>'ID End')); ?>
	<?php echo $this->Form->input('text', array('type' => 'text')); ?>
	<?php echo $this->Form->submit(__('Search',true)); ?>
	<?php echo $this->Form->end(); ?>

サンプルの例は単純なケースなのでわざわざBetween使わなくてもidの部分を以下のように変更したほうがシンプルに実装出来る。

//model
		array('name' => 'start', 'type' => 'value', 'field' => 'Sample.id >'),
		array('name' => 'end', 'type' => 'value', 'field' => 'Sample.id <'),
//controller
		array('field' => 'start', 'type' => 'value'),
		array('field' => 'end', 'type' => 'value'),
// view
	<?php echo $this->Form->input('start', array('type' => 'text', 'label'=>'ID Start')); ?>
	<?php echo $this->Form->input('end', array('type' => 'text', 'label'=>'ID End')); ?>

わざわざBetweenを使用する場面ってなんだろうって思って調べてみたけど、実行速度的なメリットぐらいなのかな。
参考: http://sj6.org/mysql_datetime_index_faster/

betweenでID検索のデモ
http://nanagaiku.mydns.jp/php/searchdemo/

CakePHP inPlaceEditorの更新

CakePHP用にその場編集のプラグインを作って放置してあったのを修正してみた。
Ajax通信でセキュリティーコンポーネントと併用出来る所が特徴と言えば特徴。
セキュリティーコンポーネントはセッションごとに作られるtokenと、PostされるフィールドをハッシュしたSecure値も照合して改変されてないかどうかをチェックする。
inPlaceEditorではセキュリティーコンポーネントが必要とするtokenやsecure値も作成してpostするのでセキュリティーコンポーネントと併用が可能になった。

今回の改修はModel名をformヘルパーなどと合わせて、指定しなければデフォルトのmodelを使用するようにしたこと、ずらずら引数を書かせていたのを配列にしたこと、inPlaceEditorにそのままオプションをスルーできるようにしたこと。JavaScript側も関数をグローバルに持ったりしてたので修正した。
まだ課題もいろいろあるのだけど、ひとまずpush

git://github.com/hidetoshing/interactive_ui.git

CakePHP用にGoogleMapヘルパーを作った

JQueryの勉強でGoogleMapの呼び出しをJQueryプラグインにして、それを簡単に呼び出せるHelperをCakePHPの形式で作成したのでgitHubに公開してみる。
git@github.com:hidetoshing/googlemaps.git

基本の使い方は

<?php echo $this->Html->script('http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js', array('inline' => false));?>
<?php echo $this->Html->script('http://maps.google.com/maps/api/js?sensor=false', array('inline' => false));?>
<?php echo $this->Html->script('/googlemaps/js/jquery.googlemap.js', array('inline' => false));?>

<h1>googlemap sample: map with daraggable marker with geocoding.</h1>

<div id="contents">
	<?php
		echo $this->Googlemap->map( array(
			'height' => '600px', 
			'width' => '600px',
			'latitude' => "30",
			'longitude' => "140"));
		$this->Googlemap->addDraggableMarker(array(
			'lat_field' => '#testLat', 
			'lng_field' => '#testLng', 
			'latitude' => "30", 
			'longitude' => "140",
			'icon' => 'http://maps.google.co.jp/mapfiles/ms/icons/blue-pushpin.png'));
		$this->Googlemap->geocoding(array(
			'address_field' => '#testAddress', 
			'submit_button' => '#geopick_button'));

		echo $this->Googlemap->toScript(array('inline' => false));
	?>

</div>

<?php echo $this->Form->input('address', array('id' => 'testAddress'));?>
<?php echo $this->Form->button(__('Find', true), array('type' => 'button', 'id' => 'geopick_button')); ?>
<?php echo $this->Form->input('lat', array('id' => 'testLat')); ?>
<?php echo $this->Form->input('lng', array('id' => 'testLng')); ?>

<!-- / #contents --></div>

こんな感じで、入力された住所に連動してドラッグ可能なマーカが付いたGoogleMapが表示出来る。
マップオプションは今のところ、latitude, longitude, zoomに対応、マーカはdraggable, iconの変更のカスタマイズが出来る。

Ajax

Ajaxに関してもJQueryなどのライブラリを使用するのが間違いが少なくていいと思う。json, xml等のデータ変換までライブラリ側で行える。

データ形式について

複雑なデータが帰ってくる場合XMLの使用を検討するのもよいらしい。JSON形式は扱いやすい。

関数とカスタムオブジェクト

オブジェクト定義のパターン

// 内部で関数定義
var foo = function(x, y) {
	this.x = x;
	this.y = y;
	this.add = function () {
		return this.x + this.y;
	}
}

// 関数定義は外部
var foo = function(x, y) {
	this.x = x;
	this.y = y;
	this.add = addValues;
}
var addValues = function() {
	return this.x + this.y;
}

// プロトタイプを使用
var foo = function(x, y) {
	this.x = x;
	this.y = y;
}
foo.prototype.add = function() {
	return this.x + this.y;	
}

上二つはfooのインスタンスに限定されるけど、プロトタイプを使用した場合はnew foo()で作成出来る全てのインスタンスに拡張が適用される。という理解。内部で関数を定義した場合はクロージャなども使用できる+プライベートなメンバを定義できるけど、関数までふくめて全てがメモリ上に展開されるからメモリ食い。プロトタイプを使用した場合、プライベートは作れないけどメモリ効率は良い。ということらしい。

使い捨てのオブジェクト定義

var foo = { x: 0, y: 0, add: function() { this.x + this.y; } }

オブジェクトをこんな風にも定義できますよ。という話。とてもシンプルに記述できる

例外処理

オブジェクトからエラーを通知する場合、返値では無くて例外をthrowするとエラー処理を明確にすることが出来る。
ロギングなどの付加的な処理をまとめて記述できるので活用するべき。ただし、オブジェクトを使う側で確実にハンドリングすること。