Home > アーカイブ > 2007-04

2007-04

ITmediaでTwitter検索/Twitterで暇つぶしが紹介されました。

この記事の所要時間: 025

ref:ITmedia Biz.ID:Twitterでゴールデンウィークのヒマをつぶす

ITmediaでTwitter検索Twitter で暇つぶしが紹介されました。

昨日ちょうどアクセスログを眺めている時にリファラで知りました。;-) やはりこうやって取り上げて貰えると嬉しいですね。

特に暇つぶしが取り上げられたのが嬉しかったり。

どちらのツールも登録等せずに誰でも使えるので、お暇な時にお試し下さい。

CakePHP モデルのvalidates()に注意

この記事の所要時間: 138

Model#validates()ですが、「ちょっとどうなの?」な仕様になってます。

このメソッドはModel#save()内で自動的に呼ばれるのか、コントローラのアクションメソッド内で呼ばれるのが一般的な使い方だと思います。

問題が起こるのは後者の方で、空の配列をvalidates()に渡すと、Userl#$validateで何を定義していてもtrueが返ってきます。

下のソースならUserモデルの$validateに入力チェックをいれておけば「ng」が表示されそうですが、実はUser#$validateに関わらず「ok」が表示されてしまいます。

PHP:
  1. <?php
  2. class FooController extends AppController
  3. {
  4.     var $uses = array('User');
  5.  
  6.     function blank()
  7.     {
  8.         $this->data = array();
  9.         if ($this->User->validates($this->data)) {
  10.             var_dump('ok');
  11.         } else {
  12.             var_dump('ng');
  13.         }
  14.  
  15.         exit;
  16.     }
  17. }
  18. ?>

原因はModel#invalidFields()の以下の箇所にあります。(invalidFields()はvalidates()から呼ばれます)

[cake/libs/model/model_php4.php]

PHP:
  1. <?php
  2.  
  3.         foreach($this->validate as $field_name => $validator) {
  4.             if (isset($data[$field_name]) && !preg_match($validator, $data[$field_name])) {
  5.                 $this->invalidate($field_name);
  6.             }
  7.         }
  8.  
  9. ?>

$dataはvalidates()に渡される連想配列なのですが、$dataに$validateのキーが存在しない場合はpreg_match自体が処理されず、invalidate()にはなりません。

なのでコントローラからvalidates()を呼ぶ時は、$dataにModel#$validateのキーに存在するかを確認する必要があります。

ちなみにModel#save()ではvalidates()の後の処理でfalseが返るようになっています。ただこれも$dataが空の配列の場合の話で、Model#$validateにキーは無い(入力チェックはしない)が、テーブルにカラムがあるキーのみの配列なら通ってしまいます。

例えば下のソースなら「ok」が表示されます。

PHP:
  1. <?php
  2.     function blank()
  3.     {
  4.         // User#$validate に id が無い場合
  5.         $this->data = array('id' => '');
  6.         if ($this->User->save($this->data)) {
  7.             var_dump('ok');
  8.         } else {
  9.             var_dump('ng');
  10.         }
  11.  
  12.         exit;
  13.     }
  14. }
  15. ?>

やはりこちらも入力チェックするキーが$dataに存在するかを確認するしか無いようです。

CakePHP $_GET/$_POSTの値はどこに?

この記事の所要時間: 348

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:
  1. <?php
  2. class FooController extends AppController {
  3.   function index() {
  4.     // $this->params['url']['id'] => 1
  5.     // $this->params['url']['code'] => 'abcd'
  6.     // $this->params['url']['offet'] => 10
  7.   }
  8. }
  9. ?>

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:
  1. <?php
  2. class FooController extends AppController {
  3.   function index() {
  4.     // $this->params['form']['id'] => 1
  5.     // $this->params['form']['name'] => 'abcd'
  6.     // $this->params['form']['comment'] => 'Hello!!'
  7.  
  8.     // 以下3つは同じ
  9.     // $this->params['form']['data']['Foo']['name'] => 'Bar'
  10.     // $this->params['data']['Foo']['name'] => 'Bar'
  11.     // $this->data['Foo']['name'] => 'Bar'
  12.   }
  13. }
  14. ?>

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]

PHP:
  1. if (isset($_FILES['data'])) {
  2.             foreach ($_FILES['data'] as $key => $data) {
  3.  
  4.                 foreach ($data as $model => $fields) {
  5.  
  6.                     foreach ($fields as $field => $value) {
  7.                         $params['data'][$model][$field][$key] = $value;
  8.                     }
  9.                 }
  10.             }
  11.         }

$_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:
  1. <?php
  2. class FooController extends AppController {
  3.   function index() {
  4.     $_SERVER['foo'] = 'server';
  5.     $_ENV['foo'] = 'env';
  6.     putenv('foo=getenv');
  7.     // server を出力
  8.     var_dump(env('foo'));
  9.  
  10.     unset($_SERVER['foo']);
  11.     // env を出力
  12.     var_dump(env('foo'));
  13.  
  14.     unset($_ENV['foo']);
  15.     // foo を出力
  16.     var_dump(env('foo'));
  17.   }
  18. }
  19. ?>

6. $_REQUEST/$_GLOBALS

$_REQUEST/$_REQUESTの値は特に参照する方法は用意されていません。フレームワークでも参照している箇所は見当たりませんでした。

Twitterで暇つぶし

この記事の所要時間: 040

またまたTwitterネタ。

Twitter 検索某ネタのおかげで好評でした(@ieiriさん、ありがとうございました)ので、調子に乗ってこんなのも作ってみました。

Twitter で暇つぶし : みんな何してる?
twitter_viewer_qr.jpg

携帯でTwitterのステータス(投稿)を見るだけのサイトです。

外出している時に携帯でTwitterを見ていると意外と更新されずに何度もリロードする事ありません?

Friendsをバンバン増やせば良いのでしょうけどあまり増やしすぎると今度PCで見るときがつらかったり。

知らない人のステータスをただ見るだけなのですが意外と面白かったりします。

登録等は一切不要なので「Twitterって聞くけど、何?」な方も一度どうぞ。

Twitter 検索

この記事の所要時間: 040

先日はじめたTwitterですが、なかなか楽しいです。

Twitterの仕組み自体ももちろん面白い良いのですが、日々ユーザが増えていき、システムが改良され(今日は日本語入力が改善されたもよう)、どんどん完成度が高まっていくさまが何だか楽しいです。

で、ちょうどCakePHPで何か作ろうと思っていたので、こんなの作ってみました。

Twitter 検索

Twitter 検索 : PHPで検索

Twitterの検索では、既にtwittersearchがあるのですが、それとは違いこちらは日本語ユーザのみの検索ができます。

興味のあるキーワードを入れて、Friends探しに使って貰えたら良いかと。

# 超見切り発車気味な公開なので、突然消える可能性もありますが。;-)

Twitterで一人言

この記事の所要時間: 059

ちまたで話題のTwitterはじめてみました。

http://twitter.com/shin1x1

当初IMのようなサービスだと思っていたので、登録はしたものの身近でやっている人もいないので2,3日放置していました。

で、今日何気に眺めているとIMとは違うものだと何となく気づきました。(遅)

IMというよりはどちらかというとblogの方が近いのかな。

ようは相手がいて初めて成り立つものではなく、基本は自分から発信するだけで、あとはそれを見た誰かが反応を返すものだと。

blogのような情報発信 + IMのような手軽さ + SNSのような繋がり / 3 といった感じですかね。(SBMのコメントを書く感覚にも近いような)

「Twitter」は「さえずり」だそうです。自分で使った感触では「さえずり」というより、ぼそぼそ言う「一人言」という感じでした。

一人言でも何気に面白いので、「相手がいない」と躊躇している方も一度始められてはいかがでしょうか。

# やってみようかな、という人には「第1回 Twitterキホンのキホン|gihyo.jp」が参考になりますよ。

CakePHP SQLをログに記録

この記事の所要時間: 227

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:
  1. <?php
  2. uses ('model' . DS . 'dbo' . DS . 'dbo_postgres');
  3.  
  4. class DboPostgresLog extends DboPostgres {
  5.   /**
  6.    * @var integer
  7.    */
  8.   var $queryNo = 1;
  9.  
  10.     function execute($sql) {
  11.     $ret = parent::execute($sql);
  12.  
  13.     if (defined('LOG_SQL') && LOG_SQL) {
  14.       $this->log(sprintf("%d. %s", $this->queryNo, $sql), LOG_DEBUG);
  15.       $this->queryNo++;
  16.     }
  17.  
  18.     return $ret;
  19.     }
  20. }
  21. ?>

2. 作成したDboPostgresLogを使用する

DATABASE_CONFIGのDB設定でdriverを'postgres_log'を指定します。

[app/config/database.php]

PHP:
  1. class DATABASE_CONFIG
  2. {
  3.   var $default = array('driver' => 'postgres_log',
  4.                        'connect' => 'pg_connect',
  5.                        'host' => 'localhost',
  6.                        'login' => 'user',
  7.                        'password' => 'pass',
  8.                        'database' => 'foo',
  9.                        'prefix' => '');
  10. }

3. core.phpでLOG_SQLを定義

SQLをログに出力するか否かを設定するLOG_SQLをcore.phpで定義します。

LOG_SQLがtrueならSQL出力し、LOG_SQLが未定義もしくはfalseの場合は出力しません。

[app/config/core.php]

PHP:
  1. <?php
  2. /**
  3. * If set to true, logging sql queries
  4. */
  5.     define('LOG_SQL', true);
  6. ?>

これで実行されたSQL文が[app/tmp/logs/debug.log]に出力されます。

この方法はDEBUGの値に関係無くログ出力ができるのは良いのですが、DboSourcesを継承するのがイマイチです。フレームワーク(DboSources自身)で対応して貰えれば一番良いですけどね。;-)

Firecakeのコメントにcakeinfoが

この記事の所要時間: 027

FirecakeはCakePHPのデバッグ情報をFirebugに表示する便利なHelperです。

Bakeryでソースを見ているとコメントにこんな記述が。

Looking for more ideas, I also used cakeinfo.php, * a script released under MIT license by Masashi Shinbara.

cakeinfoを参考にして頂いているようです。嬉しい事です。

PostgreSQL 誤って削除したデータを復元する

この記事の所要時間: 037

PostgreSQLの仕組みを考えれば「なるほど」な内容なのですが、実際にできるんですね。

VACUUM していなければ、データベースファイル内に残っている可能性があります。 トランザクションIDをDELETE 文を発行した時点よりも昔に「巻き戻す」ことで、 削除したデータが再び見えるようにできるかもしれません。

[pgsql-jp: 38276] Re: データの復旧について

トランザクションIDを戻せばOKなのか。うーんスゴイ。

CVSやSubversionでリビジョンを指定する感覚に似ているかも。(そこまで柔軟じゃないか)

間違ってDELETE/UPDATEした時は慌てず騒がず、この方法を思いだそ。

CakePHP Model#save()内でvalidates()を呼ばない

この記事の所要時間: 11

ref: CakePHPの何か-CakePHPのModelを使う

お馴染みyandoさんのプレゼン資料で気になったソースがあったので勝手に添削w。

35p:Validation設定例

PHP:
  1. <?php
  2. class UsersController extends AppController {
  3.   function fuga(){
  4.     $data['User'] = array(
  5.       'login_id' => '+*+*+*+*',
  6.       'password' => 'abcde',
  7.       'name' => ''
  8.       );
  9.       if ($this->User->validates($data)) {
  10.         $this->User->save($data);
  11.       } else {
  12.         $this->validateErrors($this->User);
  13.       }
  14.    }
  15. }
  16. ?>

User#save()の部分ですが、Model#save()メソッド内ではModel#validates()が呼ばれるので、このままだとUser#validates()が2度実行されることになります。

このソースのようにsave()の前にvalidates()を実行するならsave()の第2引数にfalseを渡します。これならsave()内でvalidates()は実行されません。

PHP:
  1. <?php
  2.       if ($this->User->validates($data)) {
  3.         // validates()はsave()内では呼ばれない
  4.         $this->User->save($data, false);
  5.       }
  6. ?>

あとこの例のようにController#validationErrorsを参照しないのなら、Controller#validateErrors()は呼ばなくても良いです。

なので↓のようにシンプルに書けたりします。

PHP:
  1. <?php
  2. class UsersController extends AppController {
  3.   function fuga(){
  4.     $data['User'] = array(
  5.       'login_id' => '+*+*+*+*',
  6.       'password' => 'abcde',
  7.       'name' => ''
  8.       );
  9.       $this->User->save($data);
  10.    }
  11. }
  12. ?>

まあ実際は登録完了後はリダイレクトしたりビューテンプレートを変えたりするので結局上のソースのようになるのですが。;-)

Home > アーカイブ > 2007-04

検索
フィード
メタ情報

Return to page top