目指せ1級!30代サラリーマンボルダリング日記

都内IT企業で働く30代サラリーマンのクライミング記録

タグ:CakePHP

20100630211430 よくありそうなこんなかんじのログイン画面。
かんたんログインボタンと、ID/PASS入力するフォーム。

かんたんログインはいろいろありますけど、とりあえず今回は触れません。
キャリアのゲートウェイのIP制限はしましょうってかんじ。


標準のAuthコンポーネントと、ktaiライブラリを使ってます。

UIDが設定してあれば「かんたんログイン」できて、してなければID/PASS入力してUID設定することもできるという機能。
今回はトップページで行う設定です。
Cakeのバージョンは1.3

APP/controllers/top_controller.php

class TopController extends AppController
{
  var $name = 'Top';
  var $uses = 'User';
  var $helpers = array('Ktai','Session','Form');
  var $components = array('Ktai','Session','Auth');

  function beforeFilter() {
    $this->Auth->loginAction = '/';
    $this->Auth->autoRedirect = false;
    $this->Auth->authorize = 'controller';
    $this->Auth->allow('index');
    parent::beforeFilter();
  }

  function beforeRender() {
    parent::beforeRender();
  }

  function index () {
    if ($user = $this->Auth->user()) {
      if (isset($this->data['add_uid'])) {
        $user['User']['uid'] = $this->Ktai->get_uid();
        $this->User->save($user['User']);
/* 7/14追記 */
        $user = $this->User->findById($set['id']);
        $this->Auth->login($user);
/* 7/14追記ここまで */
      }
      $this->redirect($this->Auth->redirect());
    } else {
      if (isset($this->data['auto_login'])) {
        if ($user = $this->User->findByUid($this->Ktai->get_uid())) {
          if ($this->Auth->login($user['User'])) {
            $this->redirect($this->Auth->redirect());
          } else {
            $this->Session->setFlash('再度かんたんログイン設定をしてください',null);
          }
        } else {
          $this->Session->setFlash('かんたんログイン設定がされていません',null);
        }
      }
    }
  }

  function isAuthorized () {
    $user = $this->Auth->user();
    $user['User']['last_login'] = date('Y-m-d H:i:s');
    $this->User->save($user['User']);

    return true;
  }
}

やっていることは以下の3点。
・ID/PASSでログイン成功して、「かんたんログイン設定する」にチェックがされていれば、UIDをとってテーブルに突っ込む。
・かんたんログインの場合はUIDをキーにusersテーブルからデータを取ってきてAuth->loginに突っ込んでログインさせる。
・isAuthorized()でログインが成功したらログイン日時を更新。

usersテーブル

+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(10)      | NO   | PRI | NULL    | auto_increment |
| username    | varchar(100) | NO   |     | NULL    |                |
| password    | varchar(100) | NO   |     | NULL    |                |
| uid         | varchar(100) | NO   |     | NULL    |                |
| flag        | int(1)       | NO   |     | NULL    |                |
| last_login  | datetime     | NO   |     | NULL    |                |
| created     | datetime     | NO   |     | NULL    |                |
| updated     | datetime     | NO   |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+


あ、クッキーが使えない携帯がまだまだたくさんあるため(主にdocomo)、そのための設定も忘れないようにメモ。

APP/config/bootstrap.php

if (!isset($_SESSION)) {
  if ($ini_set) {
    ini_set('session.use_trans_sid', 1);
    ini_set('session.serialize_handler', 'php');
    ini_set('session.use_cookies', 0);
  }
}


2010/07/14追記
ログインの際にUIDをテーブルに追加した場合、
$this->Auth->user()で取れるユーザデータはセッションなので再度ログインしないといけないみたい。
    このエントリーをはてなブックマークに追加

テーブルのカラムのタイプをtinyint(1)にして、saveしようとしたんだけど値がおかしい。

  $set = array('type' => 2);
  $this->Data->create();
  $this->Data->save($set);

typeに2を入れようとしてるんだけど、データベース見てみると1になってる!!
ぐぐったらいっぱいでてきたので、みんなやっぱりハマるんだね。。
ちなみにCakePHP1.3です。

Cakephpはtinyintをbooleanにするらしい。。。なんてこった。

CORE/libs/model/datasources/dbo/dbo_mysql.php line474
    if (($col == 'tinyint' && $limit == 1) || $col == 'boolean') {
      return 'boolean';
    }

これは実際体験してみないと気づかないね。。
無駄に時間とられた。
    このエントリーをはてなブックマークに追加

位置情報取得

位置情報を扱うライブラリはこちらを使いました。
docomoのiエリアデータを利用して土地名も返してくれます。
http://labs.unoh.net/2008/08/phpgeomobilejp_converter.html
これをvendors/Geomobilejp/につっこむ。

2点間の緯度経度から直線距離を求める

こちらを参考。
http://www.pahoo.org/e-soul/webtech/php02/php02-21-01.shtm
上記のライブラリのデータをそのまま突っ込めるように、ちょっとだけ修正。
計算部分はそのままです。


で、これらをCakePHPのコンポーネントで使いたかったのでまとめました。
続きを読む
    このエントリーをはてなブックマークに追加

サイト全体で使いたい定数、たとえばサイトURLとかを
環境によって切り替えたいなと。

本番、開発どちらでも使うものは
APP/config/common.php

環境に依存するものは
dev.php、production.php
みたいなかんじに。

環境を設定するのは
apacheのconfのvirtualHost書いてるようなところ。
CAKE_CONFIG的な名前で・・・。

SetEnv CAKE_CONFIG dev

APP/controller/app_controller.php

ここでconfigを設定した。
で、beforeRendeerでconfigにセットして
viewでは$configで使えるように。

  function beforeFilter() {
    Configure::load('common');
    if (isset($_SERVER['CAKE_CONFIG'])) {
      Configure::load($_SERVER['CAKE_CONFIG']);
      $this->config = array_merge(Configure::read("common"), Configure::read($_SERVER['CAKE_CONFIG'\
]));
    } else {
      $this->config = Configure::read("common");
    }
  }
  function beforeRender() {
    $this->set("config", $this->config);
  }
    このエントリーをはてなブックマークに追加

OpenIDでログインして、ニックネームを登録してもらうような画面でのバリデートメモ。

usersテーブルのnicknameカラムにバリデートをかけたい。


チェック項目は3つ。

  • ユニークなニックネームか
  • 文字数制限内か
  • 空白のみではないか

空白のみの入力をfunction notSpaceでチェック。

APP/model/user.php

<?php
class User extends AppModel {
    var $name = 'User';
    var $validate = array(
        'nickname' => array(
                            'notSpace' => array(
                                                'rule' => array('notSpace'),
                                                'message' => '空白のみは登録できません。'
                                                ),
                            'between' => array(
                                               'rule' => array('between', 1, 45),
                                               'message' => '1文字以上入力してください。(全角15文字まで)'
                                               ),
                            'isUnique' => array(
                                                'rule' => 'isUnique',
                                                'message' => 'すでに登録されています。'
                                                ),
                            ),
        );
    function notSpace($field=array()) {
      foreach($field as $name => $value){
        if (preg_match("/^( | )+$/", $value)) {
          return false;
        } else {
          return true;
        }
      }
    }
}
?>
    このエントリーをはてなブックマークに追加

とりあえずテストでモバイルサイトで作ってみようと、
既存のぼくが管理してるサイト(ネトゲ)のモバイル版作ってみた。

とくにモバイル用に機能追加などはしておらず、
デイリーだったランキングを数時間単位にして表示したくらい。
あとPC版はZendFrameworkで作ってるけど、モバイル版はCakePHPってところが主な違い。

2010年1月18日にPCサイトでモバイル版の告知。
約1週間の計測。

■モバイル版


moPV

moUU



■同期間のPC版。


pcPV

pcUU



■考察


UUをみると、このままでもPC版の1/10くらいのユーザは来そうな感じ。
まーでも、このサイトの9割近くのユーザがネトゲヲタなのでPCの方が使い勝手とか親和性が高いってことか。
ゲームの裏画面でチェックすることが多そうだし。
残りの1割のリア充(笑)が移動中とかに見てるって考えると、この結果は非常に納得。

あ、よく見るとモバイル版のPVが1/23、1/24とそこだけ凹んでる。
この2日間は土日・・・。

要するに外には出ずに、家でずっとネトゲっていうパターンね。


なんかここまで考察に確信が持てるってなかなかないよね。。
    このエントリーをはてなブックマークに追加

前回に引き続き、次はGoogleAnalyticsの導入。
直接書いてもいいんだけど、とりあえずヘルパーに突っ込んた。
vendorにそのままいれるほうがいいのかな。

■APP/views/helpers/ga.php

公式のサンプルソースをヘルパー用に書き換え。

<?php
class GaHelper extends Helper {
  private $GA_ACCOUNT="MO-XXXXXX-X";
  private $GA_PIXEL="/ga.php";

  function getUrl() {
    $url = "";
    $url .= $this->GA_PIXEL . "?";
    $url .= "utmac=" . $this->GA_ACCOUNT;
    $url .= "&utmn=" . rand(0, 0x7fffffff);
    $referer = isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : '';
    $query = $_SERVER["QUERY_STRING"];
    $path = $_SERVER["REQUEST_URI"];
    if (empty($referer)) {
      $referer = "-";
    }

    $url .= "&utmr=" . urlencode($referer);
    if (!empty($path)) {
      $url .= "&utmp=" . urlencode($path);
    }
    $url .= "&guid=ON";
    return str_replace("&", "&", $url);
  }
}
?>

■APP/controllers/app_controller.php

すべてのページで出すから、app_controllerで宣言。

<?php
class AppController extends Controller {
  var $helpers = array('ga');

■APP/views/layouts/main.ctp

メインのレイアウトのbodyタグ上に書く。

<html>
<head></head>
<body>
   ・
   ・
<?php $googleAnalyticsImageUrl = $ga->getUrl(); ?>
<img src="<?php echo $googleAnalyticsImageUrl; ?>" />
</body>
</html>

■APP/webroot/ga.php

あとはGAの管理画面からDLできるga.phpをwebrootに置いて準備完了。
ちゃんと動いてるみたい。

    このエントリーをはてなブックマークに追加

■xml宣言

<?xml version="1.0" encoding="Shift_JIS"?>

これだけで書くとエラーになる。
たぶんみんな最初にこれでつまづくんだろね。
ぐぐったらすぐでてきた。

解決方法はいくつかあって、
・echoでだす。
・apache
とか。

今回はechoで出します。
<?php echo'<?xml version="1.0" encoding="Shift_JIS"?>'."\n"; ?>

■DOCTYPEの指定

<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">

・3キャリア別にDOCTYPE分ける場合のメモ
http://ma-san.org/2009/10/htmlphpdoctype.html

■Content-Typeヘッダの指定

<meta http-equiv="Content-Type" content="application/xhtml+xml ; charset=Shift_JIS" />

今回はapacheでの設定ではなくレン鯖とか汎用的にいけるようにソースに。
app_controller.phpのbeforerenderで出力。
*)継承先のbeforerender()でparent:beforerender()を忘れずに。

APP/controller/app_controller.php

<?php
class AppController extends Controller {
  function beforerender() {
    header('Content-Type: application/xhtml+xml');
  }
}

■エミュレータ

docomo
http://www.nttdocomo.co.jp/service/imode/make/content/browser/html/tool2/index.html

softbank
http://creation.mb.softbank.jp/web/web_tool.html

au
http://www.au.kddi.com/ezfactory/tool/ue/
http://web.archive.org/web/*/developer.openwave.com/ja/tools_and_sdk/openwave_mobile_sdk/SDK62K/Openwave_SDK_62K.exe

・fireMobileSimulator
http://firemobilesimulator.org/
    このエントリーをはてなブックマークに追加

各ページによって読み込むjsを変えたいときに。
簡単なことだけど、ふと忘れそうなので。

/path/to/app/webroot/js/foo.jsを読み込みたい場合。

・controller

コントローラではヘルパーの呼び出し。
<?php
class HogeController extends AppController {
  var $name = 'Hoge';
  var $helpers = array('Javascript');

  public function index() {
  }


・view

ビューではaddScriptで<head>タグ内に記述される。
($scripts_for_layoutの部分)
<?php $this->addScript($javascript->link('foo')); ?>

その場所に出力したい場合はそのまま
<?php echo $javascript->link('foo'); ?>

・layout

レイアウトはとりあえずこんなかんじで$scripts_for_layoutを書く。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<?php echo $scripts_for_layout; ?>
</head>
<body>
    このエントリーをはてなブックマークに追加

どうTwitterを楽しむべきか、
なにが楽しいのかいまいちよくわからんので
自分で興味が持てるようにAPI使ってサイト作りました。
あとCakePHPの練習も兼ねて。

いちおうECサイト担当っぽく「欲しい」をキーワードに集めてみました。

とりあえずぶっぱなしぎみで作ったので中身ごり押しな感じです。


欲望なう

欲望なう

キャッシュだったり、もうちょっとリアルタイム性だしたりと改善点は多々あると思うのでぼちぼち作ります。
    このエントリーをはてなブックマークに追加

app/vendors/shells/以下にファイルを作成。

/path/to/app/vendors/shells/foo.php
<?php
class FooShell extends Shell {

  var $uses = array('Hoge');

  function bar(){
    $this->args[0]; /* コマンドラインからの引数 */
    /* 処理 */
  }
}
?>


cronで使用するケースが多いと思うので、下記のように-appにappディレクトリのパスを設定。
/path/to/cake/console/cake foo bar -app /path/to/app/
    このエントリーをはてなブックマークに追加

cakePHPを使ってみる。
PHPカンファレンス2009(前回の記事)でも使用者が一番多かったフレームワーク。
MVCのフレームワーク使ったことある人にはわかりやすいディレクトリ構成。

慣れるまでのメモをつらつら。

基本構成(post_typesテーブルの場合)

コントローラURL: (複数)/post_types/メソッド名/パラメータ
コントローラ: (複数)PostTypesController (extends AppController)
モデル: (単数)PostType (extends AppModel)
ビュー: (複数)app/views/post_types/各メソッド.ctp

モデル

  • テーブルの主キーは、"id"というフィールドで、primary、auto_increment で作るのが基本。
  • "モデル名_id"というフィールドを作るだけで、他のテーブルモデルへの外部キーとして設定。 例:users テーブルの中に、 "post_id" を作成  ⇒ posts テーブルの id を参照。 "post_type_id" を作成 ⇒ post_types テーブルの id を参照。
  • "created" (DATETIME)というフィールドを作成するだけで、新しいデータが入ると時間が自動的に記録。
  • "modified" (DATETIME)というフィールドを作成するだけで、データが更新されるたびに時間が自動的に記録。
  • "updated" (DATETIME)というフィールドを作成するだけで、データが更新されるたびに時間が自動的に記録。
  • "title"というフィールドを作ると、そのページの標準のタイトル名として利用される。
  • "name"や"title" というフィールドを作ると、外部から参照された場合に(指定がなければ)その値がセレクトボックスなどの「選択項目名」として自動的に利用される。(言い換えると、generateList()の引数が指定されていない場合にはこのように動作するということになります。)どちらもなければ、idの値がインデックスと項目名の両方に入ります。(このあたりの処理は、model_php4.phpと model_php5.php に書かれています。)

ビューを出力する必要がないメソッドの場合

<?php
$this->autoRender = false;
?>
あれ、絶対テーブル必要なの・・・!?って思ってしまったから これもメモ。

テーブル未使用モデル、モデル未使用コントローラ

・DBを使用しないモデル⇒$useTable = false;
・モデルを使用しないコントローラ⇒ var $uses = array();、または var $uses = nullでOK。
    このエントリーをはてなブックマークに追加

このページのトップヘ