▼ 2008/02/15(金) CakePHPでSQLite3を使う(覚書編)
CakePHP1.2で、SQLite3を動かすには、CakePHP1.2をSQLite3で動かしてみた。&ダウンロードをご覧下さい。
これは自分のための覚書。
array( 'テーブル名.フィールド名' => value )
という形を期待して、結果をfetchしている。
しかし、PHP5のPDOのSQLite3用のデバイスは、テーブル名を省略して、結果を返してくる。テーブル名が上記の形になるのは、複数のテーブルを連結した場合&取得するフィールド名が、重なった場合だけである。
(テーブル名を常につけるオプションは、現段階ではサポートされていない)
CakePHPは、この結果から、 $find で返す配列に
array ( 'テーブル名(正しくはモデル名)' => value )
とセットしている。
これをPDO,SQLite3でも実現するため、SELECT文を発行する時に、テーブル名とカラム名の組み合わせを覚えておき、結果セット取得時に、それと照合する、というやり方にする。
幸い、CakePHPは、ユーザーがテーブル名を省略して、取得フィールドを設定しても、必ず、
という形にするので、それを横取りして、この組み合わせをメンバ変数に覚えておかせることにした。
$spok_map_table,$spok_map_col
そして、フィールド名は次のようにしておく。
ただし、問題となるのは、ユーザーが as によって別名をつけた場合や、max( field ) などとした場合である。
この点は、 as があったら、as 以降を カラム名として扱うこととした。
関数を使った場合は、テーブル名を ゼロ とした。
(たぶん、他のDBOも、この仕様になっているはず)
事前に横取りして、テーブル名とカラム名の組み合わせを覚えておくのは
dbo_source.php の function renderStatement の select文発行のところ。
そこに、 $this->spok_sqlite3_maps($data['fields']) と、return直前に、横取りした。
function spok_sqlite3_maps($fields) は、同じファイルの最後に追加した。
sqlite固有の関数は、すべてPDO用に置き換えた。(たぶん問題ないと思う)
function resultSet は、そもそもその結果を使っていなかったので、すぐにreturnするようにしておいた。
function fetchResult() において、事前マッピングしたものを、ここで利用した。
ダブルクオーテーションを抜くことを忘れずに。
実現するには
これは自分のための覚書。
背景
CakePHPでは、データベースからのfetchでarray( 'テーブル名.フィールド名' => value )
という形を期待して、結果をfetchしている。
しかし、PHP5のPDOのSQLite3用のデバイスは、テーブル名を省略して、結果を返してくる。テーブル名が上記の形になるのは、複数のテーブルを連結した場合&取得するフィールド名が、重なった場合だけである。
(テーブル名を常につけるオプションは、現段階ではサポートされていない)
CakePHPは、この結果から、 $find で返す配列に
array ( 'テーブル名(正しくはモデル名)' => value )
とセットしている。
実現方法
これをPDO,SQLite3でも実現するため、SELECT文を発行する時に、テーブル名とカラム名の組み合わせを覚えておき、結果セット取得時に、それと照合する、というやり方にする。
幸い、CakePHPは、ユーザーがテーブル名を省略して、取得フィールドを設定しても、必ず、
”テーブル名(モデル名)”.”フィールド名”
という形にするので、それを横取りして、この組み合わせをメンバ変数に覚えておかせることにした。
$spok_map_table,$spok_map_col
そして、フィールド名は次のようにしておく。
”テーブル名(モデル名)”.”フィールド名” as ”テーブル名(モデル名).フィールド名”(微妙な違いがわかりますでしょうか)
ただし、問題となるのは、ユーザーが as によって別名をつけた場合や、max( field ) などとした場合である。
この点は、 as があったら、as 以降を カラム名として扱うこととした。
関数を使った場合は、テーブル名を ゼロ とした。
(たぶん、他のDBOも、この仕様になっているはず)
事前の横取りマッピング
事前に横取りして、テーブル名とカラム名の組み合わせを覚えておくのは
dbo_source.php の function renderStatement の select文発行のところ。
そこに、 $this->spok_sqlite3_maps($data['fields']) と、return直前に、横取りした。
function spok_sqlite3_maps($fields) は、同じファイルの最後に追加した。
事後のマッピング利用
dbo_sqliteをベースに改造した。sqlite固有の関数は、すべてPDO用に置き換えた。(たぶん問題ないと思う)
function resultSet は、そもそもその結果を使っていなかったので、すぐにreturnするようにしておいた。
function fetchResult() において、事前マッピングしたものを、ここで利用した。
ダブルクオーテーションを抜くことを忘れずに。
データベースconfig
login,paswordは設定しても無駄である。var $default = array( 'driver' => 'sqlite3_spok', 'connect' => 'PDO', 'host' => '', 'port' => '', 'login' => '', 'password' => '', 'database' => 'sqlite:c:\hogehoge\hogehoge\hoge.sqlite3', 'schema' => '', 'prefix' => '', 'encoding' => '' );
「*」 の問題
SELECT * FROM.....となった場合、事前マッピングが不可能になる。実現するには
- *をすべて個別のフィールドに展開してしまう
- フィールドの個数とテーブル名(モデル名)を対応させておく
- TB-URL http://www.cpa-lab.com/tech/083/tb/
▼ 2008/02/15(金) recursiveの隠しオプション
ここでは、recursiveが、多くの人が思うような動作にするための隠しオプションをご紹介する。recursive はややこしい。かなりの人が混乱しているように思う。
ただし、お断りしておくと、このオプションは実は隠されているのでなく、複雑なテーブル間連携を別途記述するためのオプションを流用する、というだけの話です。
できれば、recursiveを正しく理解するを読んでいただいたほうがいいかと思います。
本当は違うけど多くのCakePHP初学者が思い込むパラメータ
モデルのアソシエーションの記述に以下のように external を付け加えてください。
他のオプションを設定している方は、それは消去せず、externalオプションだけを付け加えてください。
ただし、決して、 finderQuery オプションは設定してはなりません。
(例)
すると、recursive=0 の場合、アソシエーションはとってきません。このオプションを設定しない場合の recursive= -1 と同じになります。
もちろん、recursive= 1 にすれば、第1次アソシエーションを取得します。recursive= 2 にすれば、第2次アソシエーションも取得します(以下同様)。
参考事例
'Virtual' associations Options -googleディスカッション-CakePHP もうひとつの例-googleディスカッション
recursiveを正しく理解するの図と比べるといいかと。

すべては、dbo_source.php 内の function read にある。
なんで、こんなことやってんのやろ?と思ったのがすべての始まり。
現実的には、このような方法をとるのでなく、正しくrecursiveを理解するのがよいでしょう。データ構造によっては、なんせ、発行するSQL文の量がはんぱじゃないですから。&本来の使い方じゃないですから。
recursiveの正しい使い方は -> recursiveを正しく理解する
ただし、お断りしておくと、このオプションは実は隠されているのでなく、複雑なテーブル間連携を別途記述するためのオプションを流用する、というだけの話です。
できれば、recursiveを正しく理解するを読んでいただいたほうがいいかと思います。
本当は違うけど多くのCakePHP初学者が思い込むパラメータ
recursiv=0 とすると、アソシエーションしているデータはとってこない。という動作にするにはどうするか。
結論 CakePHP 1.2 ですよ!
まず、結論。モデルのアソシエーションの記述に以下のように external を付け加えてください。
他のオプションを設定している方は、それは消去せず、externalオプションだけを付け加えてください。
ただし、決して、 finderQuery オプションは設定してはなりません。
(例)
class Spok extends AppModel{
var $belongsTo = array( 'Kirk' => array( 'extenrl' => true ));
}
すると、recursive=0 の場合、アソシエーションはとってきません。このオプションを設定しない場合の recursive= -1 と同じになります。
もちろん、recursive= 1 にすれば、第1次アソシエーションを取得します。recursive= 2 にすれば、第2次アソシエーションも取得します(以下同様)。
注意!
- こうすると、単純な hasOne,belongsTo の連携の場合も、複数回のSQL文を発行します。つまり、パフォーマンスは(かなり)悪くなります。
- external = false としても、true と同じ動作をします。このオプションを生かさない場合は、 null または、設定しない、としてください。
- この使い方は、externalオプションの本来の使い方ではありません。将来のバージョンアップ時の互換性についてはわかりません。
参考事例
'Virtual' associations Options -googleディスカッション-CakePHP もうひとつの例-googleディスカッション
図で表すと。。。
クリックすると拡大されます。recursiveを正しく理解するの図と比べるといいかと。
この件のヒント
すべては、dbo_source.php 内の function read にある。
$external = isset($assocData['external']);でした。
なんで、こんなことやってんのやろ?と思ったのがすべての始まり。
現実的には
現実的には、このような方法をとるのでなく、正しくrecursiveを理解するのがよいでしょう。データ構造によっては、なんせ、発行するSQL文の量がはんぱじゃないですから。&本来の使い方じゃないですから。
recursiveの正しい使い方は -> recursiveを正しく理解する
- TB-URL http://www.cpa-lab.com/tech/082/tb/
1: Carlos 2009年11月15日(日) 深夜4時25分
Hello,
posted the spanish translation of this article at:
http://gacimartin.com/2009/11/15/como-utilizar-sqlite3-en-cakephp-1-2/