Home > アーカイブ > 2007-04
2007-04
ITmediaでTwitter検索/Twitterで暇つぶしが紹介されました。
ITmediaでTwitter検索とTwitter で暇つぶしが紹介されました。
昨日ちょうどアクセスログを眺めている時にリファラで知りました。;-) やはりこうやって取り上げて貰えると嬉しいですね。
特に暇つぶしが取り上げられたのが嬉しかったり。
どちらのツールも登録等せずに誰でも使えるので、お暇な時にお試し下さい。
- コメント (Close): 3
- トラックバック: 0
CakePHP モデルのvalidates()に注意
Model#validates()ですが、「ちょっとどうなの?」な仕様になってます。
このメソッドはModel#save()内で自動的に呼ばれるのか、コントローラのアクションメソッド内で呼ばれるのが一般的な使い方だと思います。
問題が起こるのは後者の方で、空の配列をvalidates()に渡すと、Userl#$validateで何を定義していてもtrueが返ってきます。
下のソースならUserモデルの$validateに入力チェックをいれておけば「ng」が表示されそうですが、実はUser#$validateに関わらず「ok」が表示されてしまいます。
<?php class FooController extends AppController { var $uses = array('User'); function blank() { $this->data = array(); if ($this->User->validates($this->data)) { var_dump('ok'); } else { var_dump('ng'); } exit; } } ?>
原因はModel#invalidFields()の以下の箇所にあります。(invalidFields()はvalidates()から呼ばれます)
[cake/libs/model/model_php4.php]
<?php foreach($this->validate as $field_name => $validator) { if (isset($data[$field_name]) && !preg_match($validator, $data[$field_name])) { $this->invalidate($field_name); } } ?>
$dataはvalidates()に渡される連想配列なのですが、$dataに$validateのキーが存在しない場合はpreg_match自体が処理されず、invalidate()にはなりません。
なのでコントローラからvalidates()を呼ぶ時は、$dataにModel#$validateのキーに存在するかを確認する必要があります。
ちなみにModel#save()ではvalidates()の後の処理でfalseが返るようになっています。ただこれも$dataが空の配列の場合の話で、Model#$validateにキーは無い(入力チェックはしない)が、テーブルにカラムがあるキーのみの配列なら通ってしまいます。
例えば下のソースなら「ok」が表示されます。
<?php function blank() { // User#$validate に id が無い場合 $this->data = array('id' => ''); if ($this->User->save($this->data)) { var_dump('ok'); } else { var_dump('ng'); } exit; } } ?>
やはりこちらも入力チェックするキーが$dataに存在するかを確認するしか無いようです。
- コメント (Close): 6
- トラックバック: 2
CakePHP $_GET/$_POSTの値はどこに?
PHPのスーパーグローバルの値をどのように参照すれば良いかまとめてみました。
Controller#dataやアクションメソッド引数のようにフレームワークで想定された使い方をしている分には特に問題無いのですが、ちょっと他のことをやろうとすると、どこに値が格納されているか分からず困った事がありました。
# もちろん$_GET/$_POSTを使えば値は取れますが、せっかくのフレームワークなのでなるべくその中で値を使いたいものです。
1. $_GET
$_GETの値はController#params[‘url’]に格納されます。
ちなみに$_GET[‘url’]はURLルーティング(リクエストURIからコントローラ・モデル等を決定)で、$_GET[‘file’]は[app/webroot/js/vendors.php]で参照されています。
[http://example.com/foo/index/?id=1&code=abcd&offset=10]でアクセス
<?php class FooController extends AppController { function index() { // $this->params['url']['id'] => 1 // $this->params['url']['code'] => 'abcd' // $this->params['url']['offet'] => 10 } } ?>
2. $_POST
$_POSTの値はController#params[‘form’]に格納されます。
なお中でも$_POST[‘data’]は特別で、Controller#data/Controller#params[‘data’]にも格納されます。
[id=1&name=abcd&comment=Hello!!&data[Foo][name]=Bar]をPOST
<?php class FooController extends AppController { function index() { // $this->params['form']['id'] => 1 // $this->params['form']['name'] => 'abcd' // $this->params['form']['comment'] => 'Hello!!' // 以下3つは同じ // $this->params['form']['data']['Foo']['name'] => 'Bar' // $this->params['data']['Foo']['name'] => 'Bar' // $this->data['Foo']['name'] => 'Bar' } } ?>
3. $_COOKIE
$_COOKIEの値を参照する方法は用意されていません。もし$_COOKIEの値を使用する場合は直接参照するしかないようです。
ちなみにフレームワーク側はCakeSessionクラスがこの値を参照しています。
4. $_FILES
$_FILESはおおよそ$_POSTと同様にキーによって格納される場所が異なります。
まず$_FILES[‘data’]以外の箇所についてですが、これは$_POSTと同じようにController#params[‘form’]に格納されます。
つぎに$_FILES[‘data’]ですが、こちらは$_POST[‘data’]と同じようにController#data/Controller#params[‘data’]に格納されます。ここでは$_FILES[‘data’]がそのまま格納されるわけではなく、下のソースのように[$model][$field][$key]の連想配列に値を格納されます。
[cake/dispatcher.php:340]
if (isset($_FILES['data'])) { foreach ($_FILES['data'] as $key => $data) { foreach ($data as $model => $fields) { foreach ($fields as $field => $value) { $params['data'][$model][$field][$key] = $value; } } } }
$_POST[‘data’]の場合と異なり、$_FILES[‘data’]の値はController#params[‘form’]には格納されません。
なお、$_FILESの格納処理は$_POSTの値が格納された後に行われます。つまり$_POSTと$_FILESが同じキーを持つ値は$_FILESの値で上書きされてしまいますのでご注意を。
5. $_ENV/$_SERVER
$_ENV/$_SERVERの値は[cake/basics.php]にあるenv()関数で参照できます。
どちらの変数にも同じキーで値がある場合は、$_SERVERの値が優先され、次に$_ENVの値になります。双方に該当するキーが無い場合はgetenv()の値が返ります。(全てのキーが無ければnullが返ります。)
<?php class FooController extends AppController { function index() { $_SERVER['foo'] = 'server'; $_ENV['foo'] = 'env'; putenv('foo=getenv'); // server を出力 var_dump(env('foo')); unset($_SERVER['foo']); // env を出力 var_dump(env('foo')); unset($_ENV['foo']); // foo を出力 var_dump(env('foo')); } } ?>
6. $_REQUEST/$_GLOBALS
$_REQUEST/$_REQUESTの値は特に参照する方法は用意されていません。フレームワークでも参照している箇所は見当たりませんでした。
- コメント (Close): 0
- トラックバック: 0
Twitterで暇つぶし
またまたTwitterネタ。
Twitter 検索が某ネタのおかげで好評でした(@ieiriさん、ありがとうございました)ので、調子に乗ってこんなのも作ってみました。
携帯でTwitterのステータス(投稿)を見るだけのサイトです。
外出している時に携帯でTwitterを見ていると意外と更新されずに何度もリロードする事ありません?
Friendsをバンバン増やせば良いのでしょうけどあまり増やしすぎると今度PCで見るときがつらかったり。
知らない人のステータスをただ見るだけなのですが意外と面白かったりします。
登録等は一切不要なので「Twitterって聞くけど、何?」な方も一度どうぞ。
- コメント (Close): 0
- トラックバック: 0
Twitter 検索
先日はじめたTwitterですが、なかなか楽しいです。
Twitterの仕組み自体ももちろん面白い良いのですが、日々ユーザが増えていき、システムが改良され(今日は日本語入力が改善されたもよう)、どんどん完成度が高まっていくさまが何だか楽しいです。
で、ちょうどCakePHPで何か作ろうと思っていたので、こんなの作ってみました。
Twitterの検索では、既にtwittersearchがあるのですが、それとは違いこちらは日本語ユーザのみの検索ができます。
興味のあるキーワードを入れて、Friends探しに使って貰えたら良いかと。
# 超見切り発車気味な公開なので、突然消える可能性もありますが。;-)
- コメント (Close): 1
- トラックバック: 1
Twitterで一人言
ちまたで話題のTwitterはじめてみました。
当初IMのようなサービスだと思っていたので、登録はしたものの身近でやっている人もいないので2,3日放置していました。
で、今日何気に眺めているとIMとは違うものだと何となく気づきました。(遅)
IMというよりはどちらかというとblogの方が近いのかな。
ようは相手がいて初めて成り立つものではなく、基本は自分から発信するだけで、あとはそれを見た誰かが反応を返すものだと。
blogのような情報発信 + IMのような手軽さ + SNSのような繋がり / 3 といった感じですかね。(SBMのコメントを書く感覚にも近いような)
「Twitter」は「さえずり」だそうです。自分で使った感触では「さえずり」というより、ぼそぼそ言う「一人言」という感じでした。
一人言でも何気に面白いので、「相手がいない」と躊躇している方も一度始められてはいかがでしょうか。
# やってみようかな、という人には「第1回 Twitterキホンのキホン|gihyo.jp」が参考になりますよ。
- コメント (Close): 0
- トラックバック: 0
CakePHP SQLをログに記録
CakePHPで発行したSQLをログに記録する方法です。
DEBUG>=2にすれば画面下に表示されるSQL文ですが、DB処理後にリダイレクト等で遷移すると消えてしまいます。さらに本番稼働時は画面に表示するわけにはいかないのでやはりログに出力したいところです。
フレームワークに手を入れるのが一番簡単なのですが、バージョンが上がると面倒なので既存のDboSourceを継承したクラスにログ記録を追加します。
1. DboSourceを継承
dboはフレームワークで用意されているものだけでなく、app/model/dboにあるものもフレームワークで利用する事ができます。
ここではPostgreSQLを使うとしてDboPostgresを継承したDboPostgresLogを作ります。
ログ出力をON/OFFする定数LOG_SQLは後でcore.phpで定義します。
[app/model/dbo/dbo_postgres_log.php]
<?php uses ('model' . DS . 'dbo' . DS . 'dbo_postgres'); class DboPostgresLog extends DboPostgres { /** * @var integer */ var $queryNo = 1; function execute($sql) { $ret = parent::execute($sql); if (defined('LOG_SQL') && LOG_SQL) { $this->log(sprintf("%d. %s", $this->queryNo, $sql), LOG_DEBUG); $this->queryNo++; } return $ret; } } ?>
2. 作成したDboPostgresLogを使用する
DATABASE_CONFIGのDB設定でdriverを’postgres_log’を指定します。
[app/config/database.php]
class DATABASE_CONFIG { var $default = array('driver' => 'postgres_log', 'connect' => 'pg_connect', 'host' => 'localhost', 'login' => 'user', 'password' => 'pass', 'database' => 'foo', 'prefix' => ''); }
3. core.phpでLOG_SQLを定義
SQLをログに出力するか否かを設定するLOG_SQLをcore.phpで定義します。
LOG_SQLがtrueならSQL出力し、LOG_SQLが未定義もしくはfalseの場合は出力しません。
[app/config/core.php]
<?php /** * If set to true, logging sql queries */ define('LOG_SQL', true); ?>
これで実行されたSQL文が[app/tmp/logs/debug.log]に出力されます。
この方法はDEBUGの値に関係無くログ出力ができるのは良いのですが、DboSourcesを継承するのがイマイチです。フレームワーク(DboSources自身)で対応して貰えれば一番良いですけどね。;-)
- コメント (Close): 4
- トラックバック: 0
Firecakeのコメントにcakeinfoが
FirecakeはCakePHPのデバッグ情報をFirebugに表示する便利なHelperです。
Bakeryでソースを見ているとコメントにこんな記述が。
Looking for more ideas, I also used cakeinfo.php, * a script released under MIT license by Masashi Shinbara.
cakeinfoを参考にして頂いているようです。嬉しい事です。
- コメント (Close): 0
- トラックバック: 0
PostgreSQL 誤って削除したデータを復元する
- 2007-04-04 (水)
- Web+DB
PostgreSQLの仕組みを考えれば「なるほど」な内容なのですが、実際にできるんですね。
VACUUM していなければ、データベースファイル内に残っている可能性があります。 トランザクションIDをDELETE 文を発行した時点よりも昔に「巻き戻す」ことで、 削除したデータが再び見えるようにできるかもしれません。
トランザクションIDを戻せばOKなのか。うーんスゴイ。
CVSやSubversionでリビジョンを指定する感覚に似ているかも。(そこまで柔軟じゃないか)
間違ってDELETE/UPDATEした時は慌てず騒がず、この方法を思いだそ。
- コメント (Close): 0
- トラックバック: 0
CakePHP Model#save()内でvalidates()を呼ばない
ref: CakePHPの何か-CakePHPのModelを使う
お馴染みyandoさんのプレゼン資料で気になったソースがあったので勝手に添削w。
35p:Validation設定例
<?php class UsersController extends AppController { function fuga(){ $data['User'] = array( 'login_id' => '+*+*+*+*', 'password' => 'abcde', 'name' => '' ); if ($this->User->validates($data)) { $this->User->save($data); } else { $this->validateErrors($this->User); } } } ?>
User#save()の部分ですが、Model#save()メソッド内ではModel#validates()が呼ばれるので、このままだとUser#validates()が2度実行されることになります。
このソースのようにsave()の前にvalidates()を実行するならsave()の第2引数にfalseを渡します。これならsave()内でvalidates()は実行されません。
<?php if ($this->User->validates($data)) { // validates()はsave()内では呼ばれない $this->User->save($data, false); } ?>
あとこの例のようにController#validationErrorsを参照しないのなら、Controller#validateErrors()は呼ばなくても良いです。
なので↓のようにシンプルに書けたりします。
<?php class UsersController extends AppController { function fuga(){ $data['User'] = array( 'login_id' => '+*+*+*+*', 'password' => 'abcde', 'name' => '' ); $this->User->save($data); } } ?>
まあ実際は登録完了後はリダイレクトしたりビューテンプレートを変えたりするので結局上のソースのようになるのですが。;-)
- コメント (Close): 2
- トラックバック: 1
Home > アーカイブ > 2007-04
- 検索
- フィード
- メタ情報