照合順序をutf8_unicode_ciにしない理由の一つ

utf8_unicode_ciとutf8_general_ci

どっちにするのか、ちょっと悩みましたが、最近よく言われるのは全文検索の場合
正確性を求めるならutf8_unicode_ci
速度を求めるならutf8_general_ci
ということになるらしいです。

utf8_unicode_ciにしない理由の一つ?

それはLIKE文を書く際の「\」文字に対する処理にあります。
どうやらutf8_unicode_ciにしたフィールドに対する全文検索は下記の書き方だと「\」が検出できなくなっています!

Select * from table where `utf8_unicode_ci` LIKE '%\\\\%';

  • 具体的にこのようなテーブルがあるとします。

  • このような値が格納されています。

  • そしてSQL文を発行してみます。
    • 「utf8_general_ci」に「\」を含むレコードを検索します。

Select * from table where `utf8_general_ci` LIKE '%\\\\%';
結果:

    • 今度は「utf8_unicode_ci」に「\」を含むレコードを検索します。

Select * from table where `utf8_unicode_ci` LIKE '%\\\\%';
結果:

なんと!空行でした!これは何なんでしょう?わかる方がいらっしゃいましたらぜひ、教えてください!

  • ちなみに、下記のように「\」の前の文字を含めればちゃんと結果が出てきます!

Select * from table where `utf8_unicode_ci` LIKE '%字\\\\%';
結果:

原因がわかる方がいらっしゃいましたらぜひ、ぜひ教えてください!

PHPで経緯度から沖縄標準メッシュー番号に変換する関数

案件で住所からメッシュー番号の変換が必要になって、いろいろ調べた結果をここでメモしておきます。
  • メッシュー番号はなに?

地域メッシュwikipediaより)

  • まず、住所から経緯度に変換する

Google Maps APIを利用します。
Google Maps APIで住所から経緯度の変換はジオコーディングを使います。

ジオコーディングは実はjsからだけではなく、HTTP リクエスト経由からの利用も可能となっています。Google様!最高!

利用方法はここのページに情報が載ってます。

自分は一番シンプルのCSV形式で利用します。このように

/**
    * 住所の文字列から経緯度を取得
    *
    * @param <type> $address
    * @return <type> 変換が成功した場合 35.8569991,139.6488487 のような経緯度結果文字列を返す
    */
    function address2latlng($address=null) {
        if(!$address) return null;
        $address = urlencode($address);
        $rs = file_get_contents("http://maps.google.com/maps/geo?q=$address&output=csv&sensor=false&key=".あなたのきー;
        if($rs) {
            list($status,,$lat,$lng) = explode(",", $rs);
            if($status == 200) {
                return "$lat,$lng";
            }else {
                return null;
            }
        }else {
            return null;
        }
    }
  • そして、経緯度から沖縄標準メッシュー番号に変換する

このサイトのJSファイルを参考にPHP用の変換スクリプトを作りました。
沖縄標準メッシュ番号 表示サービスVer1.3

/**
 * $lat 経度
 * $lng 緯度
 *
 * 経度緯度から沖縄標準メッシュコードに変換する
 * 標準メッシュコード例: 29 45 B-3 f-7
 * 参考:
 * http://www.okikosai.or.jp/kenkyusho/any_data/mc/mc.html
 */
function mc_get($lat,$lng) {
    /* 緯度経度を基に標準メッシュ番号を算出 */
    /* Level1用配列 */
    $M1 = array();
    $M1[0] = Array("33","00","00","00","00","00");
    $M1[1] = Array("31","32","00","00","00","00");
    $M1[2] = Array("29","30","00","00","00","00");
    $M1[3] = Array("26","27","28","00","00","00");
    $M1[4] = Array("22","23","24","25","00","00");
    $M1[5] = Array("18","19","20","21","00","00");
    $M1[6] = Array("00","14","15","16","17","00");
    $M1[7] = Array("00","9","10","11","12","13");
    $M1[8] = Array("00","4","5","6","7","8");
    $M1[9] = Array("00","00","00","00","2","3");
    $M1[10] = Array("00","00","00","00","98","99");

    /* Level2用配列 */
    $M2 = Array();
    $M2[0] = Array("91","92","93","94","95","96","97","98","99","100");
    $M2[1] = Array("81","82","83","84","85","86","87","88","89","90");
    $M2[2] = Array("71","72","73","74","75","76","77","78","79","80");
    $M2[3] = Array("61","62","63","64","65","66","67","68","69","70");
    $M2[4] = Array("51","52","53","54","55","56","57","58","59","60");
    $M2[5] = Array("41","42","43","44","45","46","47","48","49","50");
    $M2[6] = Array("31","32","33","34","35","36","37","38","39","40");
    $M2[7] = Array("21","22","23","24","25","26","27","28","29","30");
    $M2[8] = Array("11","12","13","14","15","16","17","18","19","20");
    $M2[9] = Array("1","2","3","4","5","6","7","8","9","10");

    /* Level3用配列 */
    $M3 = Array();
    $M3[0] = Array("A-5","B-5","C-5","D-5","E-5");
    $M3[1] = Array("A-4","B-4","C-4","D-4","E-4");
    $M3[2] = Array("A-3","B-3","C-3","D-3","E-3");
    $M3[3] = Array("A-2","B-2","C-2","D-2","E-2");
    $M3[4] = Array("A-1","B-1","C-1","D-1","E-1");

    /* Level4用配列 */
    $M4 = Array();
    $M4[0] = Array("a-10","b-10","c-10","d-10","e-10","f-10","g-10","h-10","i-10","j-10");
    $M4[1] = Array("a-9","b-9","c-9","d-9","e-9","f-9","g-9","h-9","i-9","j-9");
    $M4[2] = Array("a-8","b-8","c-8","d-8","e-8","f-8","g-8","h-8","i-8","j-8");
    $M4[3] = Array("a-7","b-7","c-7","d-7","e-7","f-7","g-7","h-7","i-7","j-7");
    $M4[4] = Array("a-6","b-6","c-6","d-6","e-6","f-6","g-6","h-6","i-6","j-6");
    $M4[5] = Array("a-5","b-5","c-5","d-5","e-5","f-5","g-5","h-5","i-5","j-5");
    $M4[6] = Array("a-4","b-4","c-4","d-4","e-4","f-4","g-4","h-4","i-4","j-4");
    $M4[7] = Array("a-3","b-3","c-3","d-3","e-3","f-3","g-3","h-3","i-3","j-3");
    $M4[8] = Array("a-2","b-2","c-2","d-2","e-2","f-2","g-2","h-2","i-2","j-2");
    $M4[9] = Array("a-1","b-1","c-1","d-1","e-1","f-1","g-1","h-1","i-1","j-1");


    /* 算出結果格納用配列 */
    $MC = Array();

    /* 沖縄標準メッシュ基準点 */
    $lat_sp = 93600;
    $lng_sp = 459450;

    /* 座標値を秒単位へ変換 */
    $lat_ss = $lat * 3600;
    $lng_ss = $lng * 3600;

    /* Level1算出 */
    $lat_diff = ($lat_ss - $lat_sp);
    $lng_diff = ($lng_ss - $lng_sp);
    $lat_no = ceil($lat_diff / 300) - 1;
    $lng_no = ceil($lng_diff / 450) - 1;
    $MC[0] = $M1[$lat_no][$lng_no];


    /* Level1_No = 1 の場合の補正 $lngを半分左にシフト*/
    if (($MC[0] == "98") || ($MC[0] == "99")){
     if (($lng < 128.1875) || ($lng > 128.3125 )){
      $MC[0] = 0;
     }else{
      $lng_diff = $lng_diff - 225;
      $MC[0] = "1";
     }
    }
    /* 沖縄標準メッシュエリア外判定 */
    if (($MC[0] === 0) || !($MC[0])){
     return "";
    }

    /* Level2算出 */
    $lat_diff2 = $lat_diff - floor($lat_diff / 300) * 300;
    $lng_diff2 = $lng_diff - floor($lng_diff / 450) * 450;
    $lat_no = ceil($lat_diff2 / 30) - 1;
    $lng_no = ceil($lng_diff2 / 45) - 1;
    $MC[1] = $M2[$lat_no][$lng_no];

    /* Level3算出 */
    $lat_diff3 = $lat_diff2 - floor($lat_diff2 / 30) * 30;
    $lng_diff3 = $lng_diff2 - floor($lng_diff2 / 45) * 45;
    $lat_no = ceil($lat_diff3 / 6) - 1;
    $lng_no = ceil($lng_diff3 / 9) - 1;
    $MC[2] = $M3[$lat_no][$lng_no];

    /* Level4算出 */
    $lat_diff4 = $lat_diff3 - floor($lat_diff3 / 6) * 6;
    $lng_diff4 = $lng_diff3 - floor($lng_diff3 / 9) * 9;
    $lat_no = ceil($lat_diff4 / 0.6) - 1;
    $lng_no = ceil($lng_diff4 / 0.9) - 1;
    $MC[3] = $M4[$lat_no][$lng_no];

    return implode(" ", $MC);

}

  • 最後

ここまできたら、もう言うまでもないですね^^

CakeMatsuriTokyo2009楽しかった!

10月30日、31日に東京渋谷で開催されるCakeMatsuriTokyo2009に二日間参加して来ました!

本当に濃密な内容で楽しい雰囲気でめちゃめちゃ楽しめました〜〜〜

開催者の皆様本当にありがとうございました。

自分は一人で参加したんですが、いろんな同じ業界のCakePHP好きな人と出会って、日本に来てよかったと幸せな感じをします。^^

もちろん一番わくわくしたのは、自分の発表でした。こんな公式なイベントで発表することがなかったもん^^;でも、無事に終えてよかった。

そして、一番分かったのは発表自体は自分に大きな力を与えられたこと。

やっぱり、普段もいっぱいOUTPUTしたほうが成長しやすいですね!

で、写真をアップしたいですが、自分はカメラ持っていきませんでしたので、公式な写真など発表されればアップします。

マジで、有名人はいっぱい来て感動しました。「俺、こんな有名人と話してるぜ!」ってこころから叫び続けましたよ^^;

今後もCakeMatsuriのようなイベントがいっぱい開催されればいいなと思いますね。

trigger_error()を投げられるとセッションが消えます

メモ:プログラムのどこかでtrigger_error()を投げられるとセッションがまっさらになります。詳しくは要調査ですが、とりあえずConfigure::write('Security.level', 'medium');の際のことでした。

Password validatation in CakePHP1.2

Password validatation?

  • そうです、UserテーブルにID/PASSを登録する際に、そのPASSをバリデーションしたい!

既存問題点

  • 通常はID/PASS登録フォームはAuthComponentと一緒に利用するのは普通です。
  • だけど、AuthComponentのstartupではModelとControllerに渡す$this->dataの前に自動ハッシュ変換を行われます。

$this->data = $controller->data = $this->hashPasswords($controller->data);

  • そうすると、普通にModel側にバリデーション書いても、ハッシュ後の値に対するバリデーションなので、意味はありません!しかも、空白で送信しても、ハッシュ変換後はハッシュ文字列になりますので、必須チェックも動作しません。

解決方法

  • ModelでhashPasswordsをオーバーライトしましょう!
    • Controller側オーバーライトするModelを指定!
 function beforeFilter() {
                parent::beforeFilter();
                if($this->action == "add") $this->Auth->authenticate = $this->User;
                $this->Auth->allow('add');
        }
    • Model側新しいhashPasswordsを定義
 /**
     * Authコンポネントの自動パスワードハッシュをオーバーライト
     * バリデーション段階では自動ハッシュを阻止する
     * ※これを動作するためにはController側に下記を追加するのを前提とします
     * ※$this->Auth->authenticate = $this->User;
     *
     * @param unknown_type $data
     * @param unknown_type $enforce
     * @return unknown
     */
    function hashPasswords($data, $enforce=false) {
       if($enforce && isset($this->data[$this->alias]['password'])) {
                   if(!empty($this->data[$this->alias]['password'])) {
                       $this->data[$this->alias]['password'] = Security::hash($this->data[$this->alias]['password'], null, true);
                    }
                }  
        return $data;
    }  
 
    /**
     * データをSaveする前に自動ハッシュを動作させます。
     *
     * @return unknown
     */
    function beforeSave() {
         $this->hashPasswords(null, true);  
         return true;
    }

参考:http://teknoid.wordpress.com/2008/10/08/demystifying-auth-features-in-cakephp-12/

ClassRegistryの備忘録

ヤベー、超長い間に書いてないな、このブログ。。。

ClassRegistryを見つかったので、ちょっとmemoを

1,新しいモデルとかを読み込む際
$M = ClassRegistry::init('Post');
$M->findAll();

2,すでに読み込んだモデルをビューの中に利用する際
$M = ClassRegistry::getObject('Post');
$M->getSelectLists();