cakephp1.2 Shellからモデルとコンポーネントを呼ぶ方法

コントローラーであれば$usesと$componentsを使うけど、Shellの場合は$usesは使えるけど$componentsは使えない、というなんとも中途半端な感じだったのでメモ。以下のようにする。

<?php
App::import('Component', 'Sample');
                           
class TestShell extends Shell {
    var $uses  = array('Hoge');

    function initialize() {
        parent::initialize();
        $this->Sample = new SampleComponent($this); 
    }
}

資産形成におすすめのFX口座はこちら。
http://s2fx.com/ranking/856.html

mysql group_concatにorder byを指定する時の注意事項

group_concatにorder byを指定しても意図したソートにならないケースがある。
まず以下のように、group_concatに指定したcategory_idをそのカラム順でソートするケースの場合は問題ない。

group_concat(category_id order by category_id)

問題となるのは以下のようなgroup_concatの対象(category_name)とソートの基準となるカラム(category_id)が異なるケース。この場合、category_nameがcategory_id順に並ばない場合がある。

group_concat(category_name order by category_id)

これを防ぐには【group by-group_concatする前に、データをorder byでソートしておく】ことが必要。なので一つのSQLではできない(はず)ため、サブクエリ等を使う必要がある。

select group_concat(category_name order by category_id) from
(
select item_id,category_id,category_name
from item i
inner join item_category ic on i.id = ic.item_id
inner join category c on i.id = ic.category_id
order by category_id
) as tmp_tb
group by item_id

資産形成におすすめのFX口座はこちら。
http://s2fx.com/ranking/856.html

php csrf対策

参考サイト
PHPでクロスサイトリクエストフォージェリ(CSRF)対策するときのメモ - Qiita

php

<?php
//トークンをセッションにセット
function setToken(){
    $token = sha1(uniqid(mt_rand(), true));
    $_SESSION['token'] = $token;
}

//トークンをセッションから取得
function checkToken(){
    //セッションが空か生成したトークンと異なるトークンでPOSTされたときは不正アクセス
    if(empty($_SESSIOIN['token']) || ($_SESSION['token'] != $_POST['token'])){
        echo '不正なPOSTが行われました', PHP_EOL;
        exit;
    }
}
//GETでアクセスされたとき
if($_SERVER['REQUEST_METHOD'] != 'POST'){
    setToken();
}
//POSTでアクセスされたとき
else{
    checkToken();
}

html

<form method="post" action="">
...
<!--hiddenで生成したワンタイムトークンの文字列をPOST送信-->
<input type="hidden" name="token" value="<?php echo h($_SESSION['token']); ?>">
<input type="submit" value="登録">
...
</form>

post時にtokenをポストする必要がある

cakephp1.2 componentの$enabledでNoticeが出る件

本来componentを使うと$enabledがセットされるんだけど、beforeFilter()の中でcomponentをnewしてしまうと、$enabledが定義される前にstarup()とかを呼んでしまい、Noticeが出るのでcomponentをbeforeFilter()の中でnewしないこと。

$enabledはcomponentのstartupやらのcallbackを実行するかどうかのフラグ。falseにするとそれらが実行されない。

追記
アホだった。controllerであれば「var $components」でcomponent設定するからcomponentをnewする必要ないの忘れてた。バッチと完全にごっちゃになっていた。注意すること。

mysql テーブル設計 カンマ区切りのカラムについて考察

例えば商品の属性とかレコードに紐づくデータが沢山あってレコード毎にデータ作ると色々面倒なケース。

DB設計の基本について 例えば一つのレコードが複数のカテゴリに… - 人力検索はてな
MySQLでカンマ区切りの値を登録してみる | シスデイズ技術ブログ
まさにこういうケース。基本はやはり忠実に正規化するのがベターらしいが、場合によってはカンマ区切りにするのもあり。ただカンマ区切りにしてしまうとjoinが面倒になるぽい。柔軟なのはやはり正しく正規化する方なんだと思う。

正規化した場合の問題について
まず正規化した場合、上の例でいうとAカテゴリとBカテゴリに属するレコードを取得する(AのみBのみは除く)となった場合、一番シンプルなのがgroup_concatでカンマ区切りにした結果に対してfind_in_setで絞り込む方法では無いかと思う。

しかし他のテーブルと結合したいと(group_concatでgroup byしてさらにの他のテーブルのカラムでgroup byしたい場合等)なった場合、いったんgroup_concatして、その結果を副問合せとかで結合、という手順を踏まないといけなくなるため、最初からカンマ区切りのデータであればその辺がシンプルになるはず。

データが膨大な場合は、「いったんgroup_concatして」すら出来なくなると思うので、最初からカンマ区切りならそのまま別テーブルと結合して、別テーブルの条件で絞り込んだデータに対してfind_in_setをすれば良い訳だから、データ量によってはパフォーマンスが圧倒的に変わると思う。

とはいえjoinが出来ない(面倒な)のも痛いしな~、両方用意して使い分けるのもありかもしれんけどな。片方更新したらもう片方も更新しないといけないから、その辺は覚悟せなあかんくなる。

全ての要件を満たす方法が無いとなると、後は何を優先させるかだな。パフォーマンスなのか開発工数なのか保守性なのか。ただ絶対に守らないといけないことが1つあって、それは「シンプルである」ということ。これだけは絶対に外してはいけない。

よく考えた結果、テーブル結合が発生する場合は副問合せはどうしても必要になるけど、サブクエリにするタイミングをずらすことで正規化でもパフォーマンス落とさずにできるかも。もうちょっと検証してみる。

結論
自分のSQLがダメだっただけで、やっぱり正規化でパフォーマンス落とさずに対応することができた。なので正規化のデメリットはデータ数が増えることくらいではないかな。でもそれはシステム化すれば大した問題ではない。なのでカンマ区切りにはやっぱりしないこと。

資産形成におすすめのFX口座はこちら。
http://s2fx.com/ranking/856.html

cakephp1.2 database.phpのdefaultに設定されているDBのテーブルはModelファイルの作成が必須ではない

ClassRegistry::init('ModelName');

こんな感じでmodelsフォルダに存在しないModelを読み込んでいた場合に、何故かエラーになる場合とならない場合があって1時間くらいはまってしまったのでメモ。

どうやらdatabase.phpで$defaultに設定されたDBのテーブルだとModelファイルが無くてもエラーにならないっぽい。(もちろん実際のテーブルが存在しなかったらエラーになるが。)

defaultのDBであればModelファイルが無くともfindとかも普通に使える。

今までModelファイルが必須だと思い込んでいたのでもう二度と忘れないこと。

php sqlのIN句をarray_fillでキレイにまとめる

以下のようにすれば一行で$idの数だけ?を記述するin句が作れる

i.id in ('. implode(',', array_fill(0, count($id), '?')). ')

str_repeatを使う方法もあるようだけどこっちの方がキレイ