2012年 10月 の投稿一覧

レガシーなシステムにCakePHPを使ってスマートフォンサイト作った時のメモ

zendで作られたWEBサイトのスマートフォン用を作ることになったのですが、zendでシステム化されているところは外注で作られた所らしく、
しかも暗号化されており、ソースコードも契約上もらえないという状況。
仕方がないので、既存の仕組みはそのままにしてCakePHPでスマートフォン版を作成する事にしました。
サーバー環境からPHP5.1なのでCakePHP1.3を使用しました。

目次

  • 基本
  • その他

基本

モデル

テーブルはCakePHPの命名規則には当てはまってないので、キーとテーブル名を指定して作成します。

class UserTbl extends AppModel {
    var $name = 'UserTbl';
    function __construct () {
        $id = array(
            'id'    => false,
            'table' => 'user_tbl',
        );

        parent::__construct($id);
        $this->primaryKey = 'user_id';
    }

}

また、複合キーを使ったテーブルもあるのでそこは諦めてSQL直書きで対応する事にしました。

class UserImageTbl extends AppModel {
    var $name = 'UserImageTbl';
    var $useTable = false; // 複合キーのため

    function findById($user_id, $image_id) {
        $sql = <<<SQL
SELECT
        *
    FROM
        user_image_tbl AS UserImageTbl
    WHERE
        user_image_user_id = ?
        AND
        user_image_id = ?
SQL;

        $userImage = $this->query($sql, array($user_id, $image_id));
        if (count($userImage) === 1) {
            return $userImage[0];
        }
        return $userImage;
    }
}

コントローラ

viewに渡すために$this->dataにモデルからとった値を設定します。

$profile = $this->UserProfileTbl->read(null, $id);
$this->data['ProfileBasic'] = $profile['UserProfileTbl'];

フォーム用モデル(バリデーションまで行う)とデータ登録用モデル(SQL発行)を別々に作成し、一つのフォームに一つのフォーム用モデル、
一つのテーブルに一つのデータ登録用モデル、という風に決めて作成しました。
取得後のキー名(テーブル名のキャメルケース)とフォームのキー名が違うので、フォームに渡す際や更新する際は都度設定する必要が有りましたが、その方がわかりやすいと思ったのでその様にしました。

ビュー

ヘルパーにモデル名とurlを指定して作成しました。

<?php echo $this->Form->create('Contact', array('url' => '/contact/', 'name' => 'contact-form'));?>

<?php echo $this->Form->text('Contact.mail', array('class' => 'w0')); ?>
<?php echo $this->Form->error('Contact.mail');?>

その他

ページング

複合キーの場合は、paginateも使用できないので自分で仕組みを作成する必要がありました。
ページングで必要な情報は、件数と表示するページの情報が最低限必要です。
件数はapp_model.phpに件数をFOUND_ROWS()を使って件数を返す関数を用意しました。

class AppModel extends Model {
    function found_rows () {
        $rows = $this->query('SELECT FOUND_ROWS();', false);
        if (!empty($rows)) {
            return $rows[0][0]['FOUND_ROWS()'];
        }
        return 0;
    }
}

あとは、検索用のSQLでSQL_CALC_FOUND_ROWSを指定すれば全件数は取得できます。

        $sql = <<<SQL
SELECT
        SQL_CALC_FOUND_ROWS
        *
    FROM
        {$tbl}
    WHERE
        {$where}
    ORDER BY {$order} DESC
    LIMIT    ?
    OFFSET   ?
SQL;

        $users = $this->query($sql, $param);
        $rows = $this->found_rows();
        $result = array();
        foreach ($users as $user) {
            $i = key(array_slice($user, 0, 1));
            $result[]['UserTbl'] = $user[$i];
        }
        return array($result, $rows);
    }

ページングのリンクは共通処理を行うコンポーネントを作成してリンクを生成するようにしました。

    /*----------------------------------------------------------
     検索結果共通処理(ページング)
     ----------------------------------------------------------*/
    function _setPagingLink ($query, $members, $all, $page, $type) {

        $this->set('query', $query);
        $this->set('members', $members);
        $this->set('all', $all);

        $maxpage = ceil($all / 10);

        // ページングリンク
        if ($page != 1) {
            $this->set('prev', '<a href="/s/search/'.$type.'/?page='.($page - 1).'&'.http_build_query($query).'">前へ</a>'."\n");
        }
        $start = ($page - 1) * 10;
        $end = $start + count($members);
        $this->set('counter', sprintf('%d件中/%d~%d件表示', $all, $start + 1, $end));
        if ($page != $maxpage && $maxpage != 0) {
            $this->set('next', '<a href="/s/search/'.$type.'/?page='.($page + 1).'&'.http_build_query($query).'">次へ</a>'."\n");
        }

        // セッション登録(検索結果戻る対策)
        $this->Session->delete('Search.back');
        $this->Session->write('Search.back.url', '/s/search/'.$type.'/?page='.($page).'&'.http_build_query($query));
        $this->Session->write('Search.back.pagename', '検索結果一覧');
    }

初めて使ったコンポーネント・プラグイン

SSL Component https://github.com/plank/secured
→メソッド単位でSSLの切り替えができるのでめっちゃ便利でした。(ちょっとだけパフォーマンス悪くなる!?)
Yalog: Yet Another Logger for CakePHP https://github.com/k1LoW/yalog
→Log4phpが簡単に使える様になります。オリジナルのログローテーションクラスもLog4phpには引けをとらないと思いました。

参考になったサイト

AutoLoginComponentを参考に自動ログイン実装しました。 http://d.hatena.ne.jp/sanojimaru/20091210/1260447577

Auth Componentを使用したかったのですが、ユーザー認証がAPI経由で行う仕様だったので非常に参考になりました。http://weble.org/2011/04/05/cakephp-oauth

portを開いているプロセスを特定する

portを開いているプロセスは、
lsof -i:[port]コマンドで確認できます。

VPSを使っていると急にサービスが停止してしまう事があり、そういう場合httpdのプロセスが残ってしまい再起動しても

(98)Address already in use: make_sock: could not bind to address [::]:80

の様に既に80ポートが使われている為、起動できないというケースが有るので、

lsof -i:80
kill [PID]

の様にプロセスIDをkillすれば起動できるようになります。

/usr/sbin/lsof -i | grep http

のようにして、httpのプロセスIDを表示する方法も有るようです。