2015年8月13日木曜日

【cakePHP】ヴァリデーション時、UPDATEの場合であればプライマリキーのセットを忘れずに

cakePHPではデータベースを扱うMODELクラスにバリデーションの設定を書いておくことで入力内容の妥当性のチェックが容易にできる。

今回、バリデーションでしくじったケースがあったのでメモ。

モデルクラスによるバリデーションが行われるタイミングは主にふたつ。
■保存系:例・モデルクラスのインスタンスの save() が実行されるとき。
■明示的ヴァリデート系:例・モデルクラスのインスタンスの validates() を実行したとき。

save()の挙動は、新規レコードの追加(INSERT)と既存レコードの更新(UPDATE)とを兼ねるので、今書いている処理がINSERTなのかUPDATEなのかをしっかり意識して制御する必要がある。
主要な区別は、
■INSERT:モデルに渡される配列が、そのテーブルのプライマリキーの値を含んでいない。
■UPDATE:モデルに渡される配列が、そのテーブルのプライマリキーの値を含んでいる。


今回やってしまったエラーは以下のケース。
あるテーブルについて、レコードを新規追加する処理を追加した。
その際、そのテーブルを扱うモデルクラスについて、ヴァリデーションルールを追加した。
そのヴァリデーションルールは「isUnique」。そのテーブル内で値が重複していたらエラー。

そのモデルでのヴァリデーションを使用している箇所は、今回作った新規追加だけではなく、既存の処理にもあった。
過去に作った既存の処理においては、UPDATEにおけるヴァリデーションであるのにも関わらず、プライマリキーを配列に含めないでチェックさせていた。これまではそれで問題なかった。
しかし、「isUnique」のヴァリデーションルールは、INSERT時とUPDATE時とで挙動が違うため、既存の処理側でエラーを起こしてしまった(エラーとしたくないケースの入力においてもエラーになってしまった)。


今回の教訓:
ヴァリデーションが必要な場合、INSERTかUPDATEかを意識して、UPDATEであればプライマリキーを忘れずにモデルに渡すこと。

【cakePHP】半角・全角・カタカナ・ひらがななどの混じった文字列でのあいまい検索

cakePHPで、データベースからの検索をする場合。
キーワード入力欄などで、いい加減な入力にも柔軟に対応したいという要望がある場合。

MySQLの機能をうまく使うと、以下の曖昧入力での検索が簡単にできるようになる。
■アルファベットが、半角/全角、大文字/小文字にかかわらず、検索可能
■数字が、半角/全角にかかわらず、検索可能
■平仮名、カタカナ、半角カナにかかわらず、検索可能


参考URL:
http://tutty.info/527
http://qiita.com/kazu56/items/108075fcb5e98c0d637a
https://www.softel.co.jp/blogs/tech/archives/1877


要点:
■「collate utf8_unicode_ci like」を使う
    select * from item where item_name collate utf8_unicode_ci like '%りんご%';

■cakePHPの場合、テーブル名やコラム名にバッククオーテーションがつくので、カッコで囲む。
    $this->find('all',[
        'conditions'=>['(name) collate utf8_unicode_ci like'=>'%hoge%'
    ]);

■データベースがUTF8でない場合はもう一段階変換を挟む。「convert(コラム名 using utf8)」という書式。

    select * from member where convert(namae using utf8) collate utf8_unicode_ci like '%サトウ%';



現在の案件だと、データベースの文字コードがUTF8ではなかったので、以下のような感じになった。

コントローラーにおける記述:
---------------------------------------------------
//検索結果
$items = array();
if(empty($this->request->data) == false){
    //検索条件のセット
    $condition = array();

    //会員名あいまい検索
    $condition['and'][] = array('(convert(Member.name using utf8)) collate utf8_unicode_ci like'=>'%'.$this->request->data['Member']['name'].'%');

    //~ここにさらに検索条件を追加する処理~

    //ページネイターを使っての検索実行
    $items = $this->paginate('Member', $condition);
}//if
//検索結果をセット
$this->set("items", $items);
---------------------------------------------------


【cakePHP】Paginator を使っての検索結果並び替え

cakePHPで作るシステムはたいていデータベースと連動する。

基本的な構造として、ひとつのDBテーブルあたり、以下の機能がワンセットで作られる。
■一覧:検索フォームと検索結果
■新規登録
■詳細&編集
■削除


検索結果ページにおいて、検索結果にページネーション機能をもたせたりいろいろな条件での並び替えをさせたかったりする。

cakePHPには「Paginator」という便利な機能があるので、一覧画面のためのDB検索にはこの機能を使う。
今回、ページネイターを使ってのレコードの並び替えをはじめて使ってみて便利だったのでメモを残す。

Viewにおいて、

<?php echo($this->Paginator->sort("並び替え対象モデル名.並び替え対象コラム名", "リンク文字列")); ?>

これが基本。
これで、並び替えのためのURLが仕込まれたリンクを作成することができる。
この場合、指定されたコラムでの並び替えになり、昇順・降順がトグルする(クリックするたびに昇順降順が入れ替わる)。

トグルするのが気色悪いという場合、昇順降順指定込みのリンクをワンセットで作っておくとよい。sort() の第三引数にオプションを設定できるのでここに書く。
こんな感じ。たいてい検索結果はテーブルで出すだろうからテーブルの一部を抜き出した感じのソースで。

------------------------
<th>
    会員ID
    <?php echo($this->Paginator->sort("Member.id", "▲", array('direction' => 'asc' , 'lock' => true))); ?>
    <?php echo($this->Paginator->sort("Member.id", "▼", array('direction' => 'desc', 'lock' => true))); ?><br>
</th>
------------------------