2016年3月14日月曜日

【PHP】__DIR__ は使わない

お馴染みのcakePHPとMySQLとでシステムを作っています。商社のサイトで取扱商品の配布販促物(サムネイルやバナーなど)を表示・管理するシステム。

cakePHP3系を使おうと思ったんですがサーバーにインストールされているPHPのヴァージョンの関係で使えませんでした。

ファイルのインクルードをするとき、現在のファイルのディレクトリ名を取得するのに「__DIR__」という定数を使っていて、ZAMPPでは動いたんですがサーバーではエラーになりました。これ結構新しい定数なんですね。

 dirname(__FILE__) と書き直したら大丈夫でした。

同様に、ある読み込みファイルが画像だったらその幅と高さとを取得したいというとき、
exif_imagetype() 関数を使ってたんですが、これも古いPHPでは真っ白エラーになりました。
getimagesize() 関数のほうが重いらしいんですがこっちは動いたのでこっちで書き直しました。

古いサーバーにシステムを作る際はいろんな注意が必要になりますね。

2015年10月29日木曜日

【Android】メモリリークを防ぎたい 弱参照(WeakReference)を使おう

最近やっていたAndroid案件で、メモリリーク対策についてまた理解が進んだのでメモ。

主要な参考先:
http://tomokey.blogspot.jp/2011/05/android.html


コツ:
■内部クラスは static にする。
■「弱参照」という仕組みについて理解して、アクティビティやフラグメントといったライフサイクルのあるインスタンスを他のクラスに保持させるときはこの弱参照を使う。
■内部クラスから外側クラスにアクセスする必要がある場合、内部クラスのプロパティに外側クラスのインスタンスを持たせて、内部クラスのコンストラクタで外側クラスのインスタンスを渡す。この時、弱参照インスタンスとして保持しておく。
■処理の効率化のために作成した色々なクラスでも、プロパティにアクティビティやフラグメントを保持するときは弱参照経由にしておく。
■あとは、とにかくフラグメントやアクティビティを遷移するときは古いインスタンスを初期化していく。onResume()で準備したインスタンスはonPause()で片付けて、onStart()で準備したインスタンスはonStop()で片付ける。


---------------------------------
弱参照について。
通常、あるインスタンスへの参照を作成すると、それは「強参照」という強い参照になるらしい。
弱参照というのは弱い参照で、ガベージコレクションの対象になりやすい。
あるインスタンスについて、参照がひとつでも残っているとガベージコレクションの対象にならないという。
しかし、あるインスタンスについて、そこに残っている参照が弱参照のみだったら、それはGCの対象になるという。

アクティビティやフラグメントなど、ライフサイクルを持つものは、開発者が意図してないタイミングで破棄されたり再生成されたりするらしい。
しかしそうやってこっそり破棄された時、そのアクティビティやフラグメントへの参照(強参照)が残っていると、きちんと破棄されないでメモリを圧迫するという。
なので、これらへの参照は弱参照がいいのだということだ。

例:フラグメントクラスでアクティビティのインスタンスを持っておこうという時
//メンバ変数
private Activity activity;

//onStart() あたりで
× this.activity = getActivity();
○ this.activity = new WeakReference<Activity>(getActivity()).get();

弱参照とは、参照インスタンスである。
WeakReference クラスのインスタンス。
new WeakReference<型指定>(参照されるインスタンス) というコンストラクタで作成する。
このインスタンス自体は参照であってお目当てのインスタンスではない。
get() メソッドによって参照されているインスタンスを取り出す。


---------------------------------
インナークラスを持つフラグメントの見本
---------------------------------


public class MyFragment extends AbsFragmentWithViewFlipper{

    ///////////////////////////////////////////////////////////
    //フィールド
    ///////////////////////////////////////////////////////////

    //ボタン
    private Button btn;

    //ボタンのクリックリスナー
    private MyOnClickListener listener;


    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル:onStart()
     * フラグメントがユーザーに見えるように生成された
     * タイミングで呼び出される。
     *
     * アニメーションの開始など、表示に関わる初期化処理を行う。
     */
    @Override
    public void onStart(){
        super.onStart();

        //ボタン
        this.btn = (Button)getActivity().findViewById(R.id.btn);

        //クリックリスナー作成
        this.listener = new MyOnClickListener(this);

        //クリックリスナーをセット
        this.btn.setOnClickListener(listener);
    }//function


    ///////////////////////////////////////////////////////////
    //クラス内クラス
    ///////////////////////////////////////////////////////////
    /**
     * スイッチ操作時のリスナー
     */
    private static class MyOnClickListener implements OnClickListener{
        //外側クラスのインスタンス
        private MyFragment parent;

        //////////////////////////////
        /**
         * コンストラクタ
         */
        public MyOnClickListener(MyFragment fragment){
            //弱参照経由で外側クラスを受け取る
            parent = new WeakReference<MyFragment>(fragment).get();
        }//function

        //////////////////////////////
        /**
         * クリック時
         */
        @Override
        public void onClick(
            View view
        ){
            //外側クラスにアクセスする時は「parent」を使う

        }//function
    }//class
}//class

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>
------------------------

2015年7月17日金曜日

【cakePHP】/プレフィックス/クラス/メソッド ルーティングプレフィックスの話



コントローラーのメソッドに、アンダーバーを持ったプレフィックスを設定して、変則的なURLを生成する技がある。

class TestsController extends AppController{
    public function prefix_index(){
    }//function
}//class

こういうクラスがあるとき、

/tests/prefix_index
というのが本来のURL

これに、ルーティングプレフィックスという設定を組み合わせる。
参考:
https://24nwakahana.wordpress.com/2013/02/13/cakephp-2-0-routing-prefixes-%E3%82%92%E4%BD%BF%E3%81%86%E3%80%82%E3%81%9D%E3%81%97%E3%81%A6%E4%BD%BF%E3%81%86%E3%81%AE%E3%82%92%E6%AD%A2%E3%82%81%E3%82%8B%E3%80%82/

Configure::write('Routing.prefixes', array('prefix'));

このコンフィグは、/app/Config/core.php に書かないといけないらしい。

この設定を有効にすると、上記メソッドによるURLは以下の形になる。

/prefix/tests/index

注意しなくてはならないのは、

/クラス/プレフィックス/メソッド
というURLにはならず、

/プレフィックス/クラス/メソッド
というURLになること。

prefix_index()

prefix/index
とはならないので注意。

2015年6月11日木曜日

【cakePHP】ヴァリデーションエラーをDIVで囲わせたくない

cakePHP で、ヴァリデーションエラーを明示的に表示したい場合、以下のようなコードをViewに書く。
-------------- 

<?php echo $this->Form->error("モデル名.フィールド名"); ?>
-------------- 

すると、実際は以下のようなHTMLが出力される。

-------------- 
<div class="error-message"> 
    郵便番号の形式が不正です。(半角数字と半角ハイフンのみ有効)
 </div>
-------------- 

しかし、DIVが邪魔であると。
DIVで囲わせたくない時、以下のように書く。第2引数、第3引数(配列)が必要。

[Viewに書くコード]
-------------- 

<?php echo $this->Form->error("モデル名.フィールド名", null, array("wrap"=>false,)); ?>
--------------