CakePHPの環境を判定するTips (CakePHP Advent Calendar 10日目)

posted 2010-12-10 | written by mon_sat

@mon_sat です。

というわけで、CakePHP Advent Calendar に参加してみました。
本日12月10日で起業して4年目を迎えました。
今後ともどうぞよろしくお願いします。

CakePHP Advent Calendar については、下記のリンクを参照のこと。
http://cakephp.jp/modules/newbb/viewtopic.php?topic_id=2510&forum=16&post_id=6333

「そんな軽いネタで大丈夫か?」
「大丈夫だ。問題ない」

ということで、さくっとしたネタでいきます。

この記事の内容を140文字でまとめたものはこちら(笑)

CakePHPの環境を判定するTipsです。

本番環境/テスト環境/開発環境という3つの環境で同じコードを使っているとき、コード内で環境を判定したいときがあります。

有名なのは @cakephper さんのこの記事。
http://d.hatena.ne.jp/cakephper/20081222/1229915518

私もずいぶんお世話になったものです。

ですが、いくつか問題があります。

  • httpd.confの編集が必要になるため環境によっては使えないこともある
  • cronやcakeのshell等、webサーバーを通さずに実行すると環境変数がとれない

元記事が追記されました。CakePHPの env() を使うと、shellでも使えて便利と。なるほどですねー

というわけで私のやり方を。(解説等は後回しで)

<?php
# app/libs/environment.php
class Environment {
  static $production = "prod";
  static $test = "test";
  function getEnv() {
    return basename(ROOT);
  }
  function isProduction() {
    return self::getEnv() === self::$production;
  }
  function isTest() {
    return self::getEnv() === self::$test;
  }
  function isDevelop() {
    return !self::isProduction() && !self::isTest();
  }
}

 

<?php
# app/config/bootstrap.php
# Import & Example

App::import('Lib', "Environment");
if (!Environment::isProduction()) {
  Configure::write('debug', 2);
}

(上記のEnvironmentクラスはPHP5の書き方をしています。PHP4の方は適宜修正が必要です)

使い方は、CakePHPの各ディレクトリを以下のように配置します。

/var/www/sample.com/prod/
    cake
    app
    vendors

CakePHPのappを入れるディレクトリ名で判定するのです。
(上記の例ではprodのところ)

※本番環境の例
※テスト環境では prod の代わりに test とします。
※開発環境では prod,test 以外にします。

このやり方の利点

  • 判定ロジックが簡便なため、Environmentクラス無しでも利用できる ex if (basename(ROOT) == "dev") debug("Develop"); せっかくROOTなんていう定数あるのですから。
  • CakePHPのアプリケーションの中であれば、テストでもシェルでもどこでも判定できる
  • 開発と本番で同一のコードで記述できる
  • ロジックを拡張することで、複数人で開発するときに自分の環境だけで実行するようなチェック用コードを簡単に書ける

というわけでCakePHPを始めて間もない方、ご参考あれ。
もっと良いやり方があるよ!という方はぜひ @mon_sat へ。(このブログに追記したいと思います)

そして明日は http://www.msng.info/ でお馴染みの @msng さんです!楽しみですねー。

追記

  • 140文字の要約リンクを追加しました
  • 元記事修正に伴い、一部改訂しました

CookBook の すべてを1ページで ブックマークレット

posted 2010-11-29 | written by mon_sat

お久しぶりの更新でございます。
@mon_sat です。

CakePHPユーザーには座右のマニュアルサイトであるCookBook。

章立てが細かいため、左カラムの下にある「すべてを1ページで」というリンクをよくクリックします。
すると、その章(や項目)全体が表示されるため、読みやすくなることがあります。

というわけで、それをする(ためだけの)ブックマークレットです。(俺得なブックマークレット)

CookBook1Page

ブックマークバーにドラッグアンドドロップで貼付けて下さい。

使い方は簡単です。
通常モデルのようなページを開くと http://book.cakephp.org/ja/view/1000/Models が開きます。
その状態で当Bookmarkletをクリックすると http://book.cakephp.org/ja/complete/1000/Models に変わるという、そんな感じ。そんだけw

いちいち左カラムの下のリンクを押すのが煩わしいので作りました。

そんな面倒くさがりのあなた(いや私)のために。

ひとりハッカソンでCakePHP+TwitterAPIアプリ

posted 2010-10-07 | written by mon_sat

@mon_sat です。
PHP Matsuri でのハッカソンネタ「愛の告白 Power of Love」ができるまでをチュートリアル形式で。
(チュートリアルとしては詳細が書かれていないのですが・・・)

まずは、何を作るか考えた

さて。ハッカソン。
何を作ったら良いかというところを考える。
過去作ったやつをCakePHPで修正したい気持ちは満々なのですが、せっかくなので1から作りたいですよね。

そこで、作りたいものリストの中からハッカソン期間中に作れそうなものをピックアップし、最終的に「告白」をするアプリに決めました。
これは、単に(このアプリ上から)「つぶやくと、一定期間次のつぶやきができなくなる」というTwitterアプリです。
アプリ名を「愛の告白」とすれば、送られた側は、そのアプリ名を認知することで、ああ真面目な告白なんだと分かるというもの。

ま、基本的にはネタですよね。

ですが、一応以下の選考基準に則って選びました。

  • ハッカソンの期間中に「完成」すること
  • 分かりやすいこと
  • 仕様がある程度はっきりしていること

今回はあくまで完成にこだわりました。
中途半端なものをつくっても、その後完成させるのは、私の性格では無理なので、ハッカソンのみで完成させることを第一に考えました。
多少機能は少なくても良いと割り切るしかありません。

次に「何をするアプリで」「何が嬉しいか(楽しいか)」が伝わりやすいものにしました。
どういう形式で発表するかは分かりませんでしたが、最悪140文字で紹介できるような単純なものを作ろうと思いました。

最後に、難易度の高いものは控え、過去の経験と現在ある知識で製作可能なものにしました。
そのため、サービス内容が固まっていて、仕様を固めやすいものにしました。
結果的に残ったのは一つでしたが。
 

仕様を決める

サービスの仕様を固めなくてはなりません。
あまり大風呂敷を広げすぎるとたためなくなるのはいつものこと。今回ばかりは多少時間が余る程度のものを検討します。

まず、サービスの核として

  • TwitterのOAuthを利用してログイン
  • TwitterAPIを利用して投稿
  • 投稿時に「次につぶやくまでの時間」を設定しDBへ保存
  • 上記の期間内に投稿されたらバリデーションエラー

ここまでを決めてました。

その後、あったら嬉しいor面白いものおよびやりたいことを列挙します
あったら嬉しいor面白いもの

  • つぶやきは吹き出し風に表示したい
  • フォローしている人を選択できるようにしたい
  • バリデーションエラーを凝ってみたい

やりたいこと

  • CSS3使って(意味も無く)アニメーションさせたい
  • Ktai Library 使って(意味も無く)ケータイ対応させたい
  • 国際化してコアデベロッパーにも使ってもらいたい

だいたいこんなところでしょうか。
この時点では、モデルの数もビューの数もぼんやりとしかありません。
その辺は作りながらで平気なボリュームなので、今は決めないのが、オレ流。(良い子はまねしないほうが良いです)

クイズです。さて、次にしたことは何でしょう?

正解は、プレゼンの内容を考える、です。(誰も当たらないな、この俺俺クイズw)
えっと、今回の場合、ハッカソンのためのアプリという面があり、さらに、当面のゴールは発表です。
発表の方法はおそらくプレゼンでしょう。
というわけで、真面目にネタアプリを作るためには、ゴールであるプレゼンを意識しようと思いました。
(ちなみにこの時点では10分以上はあるだろうという能天気なことを考えていましたw)

前回のイメージがあるので、コアデベロッパーも聞いてくれているという認識であれこれ想像してみました。
実はこれを考えているときが一番楽しく、3日くらい帰宅時にはこればっかり考えていました。
で、大体こんな感じ

  • コアデベロッパーいるから資料は英語で作ろう。もちろん話すのは無理。
  • デモも英語版を使おう。
  • デモはiPhone Simulator でやるのはどうか?
  • KeyNoteとかいうおしゃれなやつは持ってないから、最近使ってるNerineでいこう。
  • 最初にポストするのは何にしよう?嫁の名前をつぶやいて、有名人の名前でバリデーションエラーを起こそうか。
    • その場合(インパクトがあるのは)誰だろう?AKB48とか?広瀬香美とか?マツコデラックスとか?
    • 会場にいる女性からというのも良いかな?でも仲の良い子がいないなあ。
    • I Love PHP っていいかもね。みんなPHPerな訳だし。
      • その場合は I Love Perl かな?@dankogai宛につぶやくとか(笑)
  • ハッカソンなのに「ガラケー対応しています。誰得だけど」とか良いかも。
  • バリデーションエラー時は「愛が足りません」みたいな感じが良いかも。

あれ?後から考えると別に面白くないね。
でもまあ、つまり、できる限り小ネタを入れていこうと。

まあこの辺はあまり仕様に影響しませんが、ただ、開発の優先順位は変わりますよね。
最後何か対応できないってなったときに、何から削っていくのか。そのとき、プレゼンで影響の薄いところやサービスの核ではないところは切る覚悟ができます。

そして開発へ

いろいろ考えて開発は事前に済まそうと考えました。
理由は

  • セッションを聞きたい
  • コアデベロッパーとたっぷり交流したい
  • 人がいても開発できるような集中力は無い(いつもひとり)
  • 酒飲んだら寝る。確実に
  • スタッフ業務手伝えてないから、当日ぐらいできることしたい

というわけで、9/29(水)の10~18時で作ることに。
1日で終われば良いが、8時間弱はタイトなので、最悪10/1(金)の10~18時を使おうということで。
合計16時間なので、まあぎりぎりハッカソン中につくるのと一緒ですから(笑)

その模様はTwitterでハッシュタグつけてつぶやいていました。
まとめはこちら

というわけでスタートです。

まずは、Twitterでアプリの登録をします。

そして華麗にConsumer Key とConsumer Secret をゲット。

まずはtwitterでConsumer key & secret をget #phpmatsuri
http://twitter.com/mon_sat/status/25839176701

Twitter API か OAuth用のプラグイン等を探します。教えてグーグル先生!(今からかい!と自分で突っ込み)
過去Google AppEngine / Python にて、簡単なTwitterアプリは作ったことがあります。
そのときの、OAuthとTwitter API の知識があるとはいえ、結構楽観的です。(笑)

というわけで、でてきたのがこちら。OAuth Consumer Component

(ただ、PHP Matsuri で@nojimage さんがTwitterプラグイン作ってたことを知りました。Twitterで聞いたらそれを使えたかも)

事例はあるので、さくっと共用のvendorsディレクトリへ放り込みます。シンボリックリンクでapp/vendors内にリンクを作成します。
(cake/vendorsやapp/vendors内に直接DLで普通は何の問題もありません。ただ私の場合こうすることでシンボリックリンクごとgitで管理できるのでこうしています。ローカルもサーバーも同じディレクトリ構成にして)

で、アクセストークンを取得・・・取得・・・取得、できない。

AccessToken が取得できん・・・ #phpmatsuri
http://twitter.com/mon_sat/status/25845347460

最初はリクエストトークンが返ってこなかった。修正後もいろいろ試行錯誤したが、どうやってもアクセストークンが取得できない。
リクエストトークンは取得されている。コールバックまで返ってきている。
何だ?
というわけでソース見て、返ってきたjsonを表示させつつ、試行錯誤。

結局、駄目だったのはURL。
サンプルコードからtwitter APIのURLが変更されている主に、httpsのところとapi.twitter.comのところ。
この時点で、URLはbootstrap.phpにすべて書こうということで、変更した。

# config/bootstrap.php
Configure::write('tw' ,array(
  'request_token' => "https://api.twitter.com/oauth/request_token",
  'authorize' => "https://api.twitter.com/oauth/authorize?oauth_token=",
  'access_token' => "https://api.twitter.com/oauth/access_token",
  'post' => "http://api.twitter.com/1/statuses/update.json",
  'verify_credentials' => "http://api.twitter.com/1/account/verify_credentials.json",
));

開始から2時間後、無事アクセストークンをゲット。
しかし、ポストできない。

AccessToken 取得できた。が、OAuthConsumer Component で post しても Incorrect Signature となる。あれ? #phpmatsuri
http://twitter.com/mon_sat/status/25848412301

その後、試行錯誤してポストできた。
原因はやっぱりURL(だったと思う。記憶が...)

@mon_sat ‚ł«‚½IURLˆá‚Ă½Borz #phpmatsuri
http://twitter.com/mon_sat/status/25849669884

えっと、ポストできたけど(笑)

というわけで、原因を究明する。
この時点でひとりのフォロワーからBOFではないかというありがたい助言が入る。
しかし、BOFが何のことだか分からない。BOMやEOFではないらしい。が、いずれにせよ文字コード周りと当たりを付けて調査するも不発。
そして、TwitterAPIのエラーの内容が、送信する内容によって、Incorrect Signatureだったり、別のエラーだったりするという摩訶不思議。

結果的にはファイルの文字コードを見たらANSIとか書いてあった。なぜ?
日本語なんてほとんど使ってなかったが、とりあえず使ってるファイルをUTF-8で片っ端から保存し直して無事ポスト成功。

@directsearchjp あいうえお
http://twitter.com/mon_sat/status/25853121710

開始から早3時間20分経過。まずい。

そして昼飯後再開。

再開 #phpmatsuri
http://twitter.com/mon_sat/status/25854634041

Bake all

いよいよbakeするかということでまずはDBを作成。
その後に作ったschema.phpより抜粋

# config/schema/schema.php
var $posts = array(
  'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
  'user_id' => array('type' => 'integer', 'null' => false, 'default' => NULL),
  'post' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'date' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
  'power' => array('type' => 'integer', 'null' => false, 'default' => NULL),
  'created' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
  'modified' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
  'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
  'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
);
var $users = array(
  'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
  'twid' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 128, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'), 'twname' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 128, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'image' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 1024, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
  'created' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
  'modified' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
  'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
  'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
);

公開サーバー用にスキーマファイルを生成

$ ./cake schema generate

そしていよいよbake x 2。

$ ./cake bake all
Welcome to CakePHP v1.3.4 Console
---------------------------------------------------------------
App : app
Path: /home/example/allapps/love.tklabo.net/local/app
---------------------------------------------------------------
---------------------------------------------------------------
Bake All
---------------------------------------------------------------
Possible Models based on your current database:
1. Post
2. User

この辺はさくっといけますね。

そして不要なアクションを削ります。
使うのは、Postのindex,view,addのみ。(adminおよびtwitterコールバック等をのぞく)
UsersControllerは、admin用ならいるかもしれませんが、ユーザー用には不要ということで、すべてをPostsControllerで作成することにしました。

この辺り、モデルも含めて、というかモデルですが、いろいろ試行錯誤しています。
後からフィールド名失敗したなあとか思うこともあるけど、まあ適当に。リファクタリング等も含めて今はやらない。

今回は、addアクションから作り始めました。
まずは骨格ということで、通常のバリデーションとして、postの文字数チェックと、powerが数値かどうかのみをチェックします。
この段階で、国際化の準備をする必要があることを思い出しました。
すべての書き出しは __() 関数を使っているのですが、バリデーションメッセージはこの書き方ができません。
というわけで、CakePlusの出番です。

このプラグインは複数の機能が乗っていますが、この中から、マルチバイトで文字数をチェックしてくれるバリデーション機能を追加しているAddValidationRuleBehaviorと、バリデーションメッセージを国際化可能なValidationErrorI18nBehaviorを使います。

わたしの共用のプラグインディレクトリにはすでにCakePlusがあるので、あとはシンボリックリンクをapp/pluginsに貼るだけです。

# model/post.php
var $actsAs = array(
  'Cakeplus.AddValidationRule',
  'Cakeplus.ValidationErrorI18n' => array( 'withFieldName' => false ),
);
var $validate = array(
  'post' => array(
    'maxLengthJP140' => array(
      'rule' => array('maxLengthJP', 140),
      'required' => true,
      'allowEmpty' => false,
    ),
//(中略)
function beforeValidate() {
  $messages = array(
    'maxLengthJP140' => __("Love words are always short. Please enter strings within 100 characters", true),
    'numericSelected' => __("Please select one", true),
  );
  $this->setErrorMessageI18n($messages, false);
  $this->replaceValidationErrorMessagesI18n();
  return parent::beforeValidate();
}

無事バリデーションができたので、動作を確認します。
(ただしこの段階ではDBに書き込まないし、当然Twitterにも投稿されません)
その後Twitterへの投稿とDB保存のロジックを加えました。
この時点のaddアクションはこんな感じ(だったはず)です。

# controllers/posts_controller.php
function add() {
  if (!empty($this->data)) {
    // set
    $this->Post->setPost($this->data);
    if ($this->Post->validates() && $result = $this->Love->post()) {
      $this->Post->data["Post"]["date"] = date("Y-m-d H:i:s", strtotime($result->created_at));
      $this->Post->save();
      $this->Session->setFlash(__('Your loving tweet has been sent', true));
      $this->redirect(array('action' => 'view', $this->Session->read("User.twid")));
    } else {
      $this->Session->setFlash(__('Your tweet could not be sent. Check your love. Please, try again.', true));
    }
  } else {
    $this->data["Post"]["status"] = $this->Love->readSessionIfExist();
  }
  $powers = $this->Post->powers();
  $this->set(compact('powers'));
  $this->set('title_for_layout', " | Tweet");
}
# models/post.php
function setPost($data) {
  // format
  $post =& $data["Post"]["post"];
  $post = $this->stringBeforePost . $post . $this->stringAfterPost($data["Post"]["power"]);
  parent::set($data);
}

setの前に、つぶやきの前後に必要な文字列を加えています。
これによりそれらを加えた文字数でバリデーションがかかるので便利ですね。
(最終的にはこの段階で、Bit.lyのAPIより短縮URLを取得し加えていますが)

$this->Love->Post()は、LoveComponent内でTwitterの送信をしていて、エラーの場合はfalseを正常に送れている場合はjsonの戻り値を返しています。

次の行で、TwitterAPIの返した投稿日時をDBに保存するべくモデル内のデータに書き入れています。
(ここには書かれていませんが、Userモデルの処理も後で追加されます)

メソッドがPOSTではないとき(つまりaddアクションをはじめて読み出すとき)の処理ですが。
これは、投稿した結果、TwitterAPIより認証エラーが返ってきた場合に、再ログインさせてからもう一度addアクションに帰ってくるために入れています。
再ログインする前にセッションに投稿した内容を書き入れて、addアクションに戻ってきたときに書き出しています。
※今考えたら、powerフィールドの値は保存していませんね。これ

初日にやったのはあとは画面を作ったくらいです。
画面は過去に作った別件のお遊びのやつを流用しました。
(後で修正した分も上記に追加されていますので、本当はもうちょっと細部は手を入れる必要がありました)

@directsearchjp test3 #ainokokuhaku [Power of Love: 2,592,000p]
http://twitter.com/mon_sat/status/25863635878

ひとりハッカソン終了。進捗40%・・・続きは金曜日 #phpmatsuri
http://twitter.com/mon_sat/status/25866576200

開発2日目

今日も一昨日のひとりハッカソンの続き。18時迄。合計16時間で何が作れるかワクワクすっぞ。 #phpmatsuri
http://twitter.com/mon_sat/status/26032504445

bit.ly で短縮URLを取得してツイートに混ぜる工程から。
例によって共用vendorsディレクトリにコードを突っ込み、シンボリックリンクを貼ります。
http://planetcakephp.org/aggregator/items/2649-new-bitly-class-to-use-with-cakephp-or-by-it-self

# controllers/components/love.php
function shortenUrlFromPostView() {
  $this->verifyCredentials();
  $twid = $this->Session->read("User.twid");
  if (!$this->Session->read("User.short_url")) {
    $long_url = Router::url(array('controller'=>"posts", 'action'=>"view", $twid), true);
    App::import('Vendor','Bitly', array("file" => "bitly/bitly.class.php"));
    $bitly = new Bitly(BITLY_USER, BITLY_API_KEY);
    $short_url = $bitly->shorten($long_url);
    $this->Session->write('User.short_url', strval($short_url[0]));
  }
  return $this->Session->read("User.short_url");
}

セッションに書き込まれていなかったらBit.lyより取得し、書き込まれていたらそれを読み出すということにしました。
今回は、ユーザーページのみなので、作成が楽です。
addアクションでPOST後の先頭で上記のメソッドを呼び出して終了です。
1時間弱で実装完了しました。

bit.ly API で短縮URLゲット #phpmatsuri
http://twitter.com/mon_sat/status/26036306558

@directsearchjp test #ainokokuhaku [Power of Love: 60p] bit.ly/bdTNGr
http://twitter.com/mon_sat/status/26037339525

Routingを設定。

# config/routes.php
Router::connect('/', array('controller' => 'posts', 'action' => 'index'));
Router::connect('/logout', array('controller' => 'posts', 'action' => 'logout'));
Router::connect('/add', array('controller' => 'posts', 'action' => 'add'));
Router::connect('/oauth/callback', array('controller' => 'posts', 'action' => 'callback'));
Router::connect('/a/*', array('controller' => 'posts', 'action' => 'view'));

もちろんテストも設定。
(でも今回書いたテストはRoutingだけだなあ。反省)

Userモデルに手を入れます。

流れだけを書くと。
ログインした時点で、Userデータを保存します。
Updateになる場合は、画像URLの変更有無だけチェックし、変更されている場合はupdateします。
(その方がログインするまで古いアイコンが表示され続けます)
そして、つぶやきの投稿時にそれらのデータを読み出し、$this->Post->data['Post']['user_id']等に格納します。

重複エラーも実装しなくてはいけません。

重複エラーは、独自バリデーションで実装します。

# models/post.php
function isDuplicate() {
  $conditions = array('user_id'=>$this->data["User"]["id"]);
  $recursive = -1;
  $recent = $this->find("first", compact("conditions", "recursive"));
  return time() > strtotime($recent["Post"]["date"]) + $recent["Post"]["power"];
}

あとは、基本的にバグ修正でした。
ログイン後のリダイレクトがループして上手く行かなかったり、addアクションにリダイレクトさせると、もう一回ログイン処理が走ったりと、ロジックの見直しを繰り返しました。

あとは、時間までデザインを修正します。
CSS3のグラデーションやつぶやきを吹き出し風にするのに苦労しました。
後者は結局できませんでしたが。

あと、バリデーションメッセージも変更しました。
バリデーションエラー時に「愛を確認してください」「愛が重複しています」という表現を思いつき、それを実装しました。

というわけで2日目終了。

アプリとしては16時間で完成しました。
イベント当日は、手が空いたら、デザインの修正等を行うということで、この日は作業終了です。

実装できなかった機能

  • フォローしている人を選択できるようにしたい
  • Ktai Library 使って(意味も無く)ケータイ対応させたい

うーん、いつもながら工数を見積もるのが下手だなあ。

当日

この日の開発時点と完成物との差異は、主に国際化とデザインのところです。

国際化

CakePHPは簡単に国際化ができますね。
注意点としては、国際化ファイル(.po)の作成を単純化するのであれば、ある程度サイトが固まってから(書き出すテキストがおおよそ決まってから)の方が良いということですね。
まずは、default.potの作成です。各言語ファイルの元になります。
その後、日本語用のディレクトリを作成し、ファイルをコピーします。

$ ./cake i18n
$ mkdir -p app/locale/jpn/LC_MESSAGES
$ cd app/locale/jpn/LC_MESSAGES
$ cp -p ../../default.pot ./default.po
$ vi default.po

poファイルを編集後、サイトにアクセスすると日本語になっているはずです。
このままでは、チェック等が大変なので、英語と日本語を交互に切替可能にしましょう。
まず、以下のアクションを作成し、フッターにそれぞれのリンクをつけました。

# posts_controller.php
function japanese() {
  $this->Session->write('love.language', "jpn");
  $this->redirect($this->Session->read('love.redirect_url'));
}
function language() {
  $this->Session->delete('love.language');
  $this->redirect($this->Session->read('love.redirect_url'));
}
function english() {
  $this->Session->write('love.language', "eng");
  $this->redirect($this->Session->read('love.redirect_url'));
}
# app_controller.php
function beforeFilter() {
  if ($lng = $this->Session->read('love.language')) {
    Configure::write('Config.language',$lng);
  }
}

今回は/posts/languageへのリンクは貼りませんでした。

デザイン

自分自身への課題としてHTML5のバリデーションとCSS3 animationを使ってみようというテーマを持っていました。

まずは、HTML5から。
これはまず、required属性で、つぶやき入力が入力されていなかったら、投稿できないようにしました。
そして、placeholder属性で、入力初期値を規定しました。
それぞれ、コードは次の通りです。

# views/themed/pc/posts/add.ctp
echo $this->Form->input('post', aa('placeholder', __("ex) I Love @mon_sat", true),'required',"required"));

Textareaにplaceholderが使えるかどうかは分かりませんでしたが、Google Chrome ではあっさり使えました。
(他のブラウザは未確認です)

また、投稿後、TwitterAPIが結果を返すまでに数秒かかるので、何らかの方法でSubmitボタンを押されないようにしたいと考えました。
オーバーレイするjQueryプラグインを過去に使ったことがあるので試みましたが、そのプラグインは使えませんでした。
時間もないので、とりあえずdisabledすることで対応。
こういうのも、HTML5で対応して欲しいところですねー。

他は例の無駄にふわふわしているやつですが、それはプレゼン資料に書いてありますのでご参考に。
というわけで完成です。

テスト

自分自身では何度もテストしたのですが、できれば自分以外の投稿が載っていた方が見栄えが良いかなと思い、夜、Grahamを捕まえてテストしてもらいました。

I love #phpmatsuri #ainokokuhaku [Power of Love: 300p] http://bit.ly/bVOVmQ
http://twitter.com/predominant/status/26169095412

というわけで完成です。
Ktai Library 対応とか他にも対応したかったアイディアはあるのですが、そんなことよりも、交流をしようと。
そのために事前に作ったのです。
おかげさまで楽しい夜が過ごせました。

プレゼン

プレゼンの模様は別の記事で書きました。
なので、別の観点より。

わたしは、作ったものを上手にプレゼンすることが苦手です。
そんなわたしが出会ったのが、踊る大捜査線シリーズの監督として有名な本広克行監督のこのつぶやき

> トミ子さんへ、一生懸命に創ったんだから一生懸命に売らないとね‼ (後略)
http://twitter.com/kmotohiro/status/22140801650

わたしはこの言葉にしびれました。
作って終わりではないのですね。作って、それを周知させて(ビジネスなら売り上げを立てて)はじめて完成なのです。
とかく魂込めて作っていると、完成した段階で、魂抜けます(笑)
でも、そこまでで半分なのです。

というわけで今回は。
満足感溢れる自分にむち打って、どうプレゼンするかを考えました。
どう頑張ってもしゃべりは上手くないので、内容を練ります。ひとり作戦会議です。
プレゼン内容は概ね固まっているので、いかにインパクトを与えるかを考えてみました。

その結果。

  • デモ中心。説明は極力省く。
  • 時間が余ったら解説。
  • ということで行こうと。

というのも、技術的に大したことをしていることは無いので、そこはアピールポイントではありません。
ネタで勝負というほど、爆笑をかっさらうネタでもありません。

なので「完成したアプリ」であることを最大限に利用したいと思いました。

その後時間が4分しかないことが分かり、結局デモするだけのプレゼンに(笑)

終わってから考えたこと。

ちゃんと、出る前に、URLをつぶやいたにもかかわらず、その旨を言わなかったのがまず失敗。
そこにアクセスしてくれれば、完成したアプリであるということは自ずと分かってくれたのに。

そして、「最後にこれを言おう」を決めていなかったのが失敗。

それだけ言えれば満足というような何かを言うべきだった。
気づいたらあと20秒弱という状況でしたが、事前に準備していれば話ができたはず。

もうひとつ。
デモでつぶやくときに、ハッシュタグをつけてつぶやかなかったのは最低。
投稿後「つぶやかれているのが確認できる」という言葉も無かった。
とっさに出た「この機会にフォローしてw」で軽く笑いをとれ救われたものの、痛恨のエラーでした。

他の人のプレゼンは参考になるものばかりだったのですが、何よりのけぞったのは@takamunetaniiさんのやつ。
なんと通信が失敗したときに自動的に笑いが取れるようになっていた。
うーん、これは準備の賜物ですねえ。
例えばTwitterAPIが想定外のエラーを返したときとかのバリデーションメッセージは準備しておくべきだったかも。くじらねたの何かとか。

さて、そんなこんなで、愛の告白アプリのできるまでは終わりです。
何かの参考になれば幸いです。
ならなくても半年後の私が参考にするでしょう。
それでは。
See you the next hack with beer !

http://love.tklabo.net/
完成品はこちらです。

PHP Matsuri 2010終了。お祭りは夜通し行われた!

posted 2010-10-05 | written by mon_sat

こんにちは。@mon_sat です。
先週末の土曜・日曜の2日間。都内の晴海グランドホテルにて、PHP Matsuri 2010 が開催されました。

先日も記事にしましたが、このイベントは、昨年行われたCake Matsuri を大幅にスケールアップし、CakePHP以外にもSymfonyやLithium等、PHPのフレームワークを利用しているユーザーを対象とした、カンファレンス & ハッカソンです。

海外からCakePHPのコアデベロッパーGraham氏(@predominant)。今年はLithiumのコアデベロッパーとして参加するjoel氏(@jperras)、同じくLithiumのコアデベロッパーであるNate氏(@nateabele)、最後は急遽来日が決定したSymfonyのコアデベロッパーKris氏(@kriswallsmith)の4名がいらっしゃいました。
豪華すぎますよね!

今年は会場が自宅から近いということもあり、スタッフとして関わることにしました。
何かしらお手伝いできることもあるかと思い。

しかし、当日までほぼ全く手伝えることはなく、自身の未熟さを反省しつつも、当日は受付を担当。
まあこんなものです。私にできるのは。

というわけで当日8時に集合。 10時開始(9時開場)なので、おおよそ1時間程度で準備を。 詳細は省略しますが、他のスタッフのてきぱきとした動きに戸惑いつつ、まずはスタッフTシャツに着替えて気合いを入れました(笑)
何とか受付できるようにしたところで、続々と参加者の皆さんが到着。

お祭りスタート!

そんなこんなで、はじまりましたよ、お祭りが。
午前中こそ受付エリアにいたものであまり参加できませんでしたが、手のすき出した午後からは、開発しつつ、交流しつつと、徐々にペースがつかめてきました。

開発したWebアプリについては、別途開発の模様を記事にします。触りはこちらのまとめをご覧ください

会場後方から見ていると、セッションを聞いている方もいればワークショップに参加している方もいて、黙々と開発する方もいれば、他の人との交流を楽しむ方もいるという、それぞれがそれぞれのスタイルで楽しんでいるのが分かります。

契約の関係があり残念ながらUstreamによる中継はできませんでしたが、Twitterのハッシュタグ #phpmatsuri を見ているだけでも、その内容の濃さが分かると思います。(まとめてくれている方に感謝しつつ→ http://togetter.com/li/56219

夜は長いのだよ。夜は

そして夜。

突然ホテル脇に止まるこの車
会場に入ってきたのはこの二人。
Red Bull Girls
テンションの上がる参加者達(笑)

というわけで本格的な開発作業に入る夜へ向けて、栄養が補給されたのでした。

さらに、恒例のケーキ。

しかも今回は3つ。
CakePHP Lithium Symfony

SymfonyとLithiumのロゴをかたどったものも用意されました。
今度はテンションの上がるコアデベロッパー達。

※ケーキは、スタッフが・・・いや参加者が美味しくいただきました(笑)

JIREI NIGHT - 事例ナイト

その後 JIREI NIGHT と称して @slywalker さんがスターウォーズ的な何かになり、軽快なトークとともに、事例発表会がスタート。

CakePHPとSymfonyの選りすぐりの事例が、長時間にわたって紹介されました。
さすがに両雄とも言えるFW。どちらも豪華な発表です。

夜は続くよ。いつまでも

さてさて。 開発者が大好きな深夜(笑)を迎えます。

事前にアプリを作っていった私は、ここぞと、コアデベロッパー達との交流にチャレンジ。
ビールを片手に Joel や Graham を始め、Nate , Kris とお話ししました。

途中、Graham に声をかけて、作ったアプリのテストをしてもらいました。
はい。コアデベロッパーがテスターという、贅沢なアプリです(笑)
「面白いね。いつ作ったの?」「昨日です」「デザインも好みだ」と言ってくれて、いと嬉し。

気づいたら1時(業界用語で25時w)。
でもちっとも眠くありません。普段は12時回ることも少ないのに。

@cakephper さんに Joel がプレゼントしたカナダビールを飲みつつ語らいました。
「精のつく」ビールだそうです(笑)
あるの?そんなの。
でもラベルを見たら妖艶な女性がこっち見てる(笑)
寒い地方だからでしょうか、どれもアルコールが10%近くあります。 味もユニークでちょっと醤油っぽい。でも美味しいという(笑)

寝て、起きる。

2時を迎えてまだ30人以上いましたが、さすがに私はダウン。
部屋で速攻で横になったのですが、なぜか5時に目が覚めた。ぱっちりと。
Twitterを確認すると、みんな普通にやってるで。おい。

というわけで、5時半に再参戦。

すでにテンション低く皆黙々とやるなか、Grahamの横にはハイテンションガールが(笑)
Graham が必死にコミュニケートしようとしています。

Twitterで返事をしようとしているところのようですが、ハイテンションガールの説明では何に困っているのか分からない(笑) お酒をたくさん嗜まれているようです(笑)

結局「日本語で返信したいんだけど上手く行かない。Google Translate の訳を一文字編集したい(Grahamは日本語勉強しています)けど、『ば』を出せない。ソフトウェアキーボードの環境が今無くて」ということらしい。
じゃあAjaxIMEがあるじゃんと、サイトへ行ってみるも、使い方が分からん(笑)
最終的に「コピペしよう」と「ば」を探すも見つからず(笑)
手詰まりorz

この時間になると、黙々と開発を続ける猛者と睡眠欲を貪る方々がほとんどでした。
さすがに深夜、いや明け方ではねー。

ハッカソン2日目

そんなこんなで7時になり。朝食。
今回は計4回食堂での食事があり、そこで普段交流無い方ともお話しできた。
意外と会場での食事よりも(そういう意味で)良かったかも。

午前中は、プレゼン資料を書く時間にあてました。
「真面目に作ったネタアプリ」としては、ここで手を抜くわけには行きません(笑)

当然、英語。
せっかくコアデベロッパーにも伝わるようにアプリは国際化したのです。
文法めちゃくちゃでも、伝わればいいかと呑気に考えて、作ります。

発表の時間が4分と決まって、あら、困った。
4分じゃ収まりません。 (資料はWebで。要HTML5対応ブラウザ by Nerine which is one of excellent apps for me. thanks)

というわけで「ひとり作戦会議」をしました。
結果、「デモ中心」の発表をし、伝える内容を以下の3点にしぼりました。

  • 16時間+αで「アプリを完成させること」をテーマにしたこと
  • このアプリの特徴は「愛をつぶやく」アプリで「一定期間空けないと次がつぶやけない」ということ(機能概要)
  • 細部まで手を抜かずにつくったこと(国際化したり、バリデーションメッセージに凝ったり、HTML5+CSS3使っていたり)

おそらく普通は「What is this」を話してからデモだと思うのですが、これはネタアプリなので最初に落ちを持ってきては意味がありません。
というわけで「愛が重複したらつぶやけない」というところまでやってみせようと。
(時間がもったいないのでOAuth認証も事前に済ませることに)

そして最後のやつについては「なるべく語らずに」気づいてもらえるようにしました。
とくにHTML5+CSS3については単なる趣味なので、そこをアピールすることはさけました。
多分どこに使っているか分からなかったと思いますが(笑)

HTML5は、つぶやき内容のtextareaに使っています。
required属性をつけて入力が無かったら送信できないようにしているのと、placeholder属性をつけて例文を入れました。 (後者はtextareaにも使えることを知りました)
CSS3はもしかしたら気づいた方もいると思いますが。 ヘッダー等で「無駄に」ふわふわしているアレです。
他にもバリデーションエラーがふわっと表示されたりするのにも使っています。

以前ブックマークしたサイトを参考に修正しつついろいろ試すことができました。面白いですねCSS3 animation。
(もちろんほかにも角丸やグラデーションで使ってます)

JavaScriptは1箇所しか使っていません。
もしかしたらplaceholder等をJavaScriptで実装していると思った方がいるかもしれませんが、そこではなく、投稿後TwitterAPIが結果を返すまで画面が遷移しないので、submitボタンを無効にするために使っています。
本当はoverlayで画面をかぶせたかったのですがそれは失敗しました。
他にもつぶやきの結果は吹き出し風にしたかったのですが、CSSの技術が備わっていませんでした(笑)

発表!

そんなこんなで発表です。
順番はアルファベットの昇順ということで、ちょうど中間点くらいのベストな位置になりました。
なんせネタアプリです。初っぱなになっては滑ること請け合いです(笑)
肩肘張らずに聞いてもらいたいものです。

他の発表者が「みんな真面目」な中、奇しくも私と次の@mochizさんはネタ系で続きます。(I like MoreGoro!)
@nojimageさんの男前な発表のあと、いよいよ私。

>会場にいなかった方へ
Ustreamでは分かりづらいですが、オーディエンスの反応は大絶賛でした。
もうあちこちでスタンディングオベーション。拍手や笑い声で私の声がかき消されるくらいの。

>会場にいた方へ
正直、反応なんて確認する余裕ありませんでしたから(言い訳)
あと記憶違いってあるよね。過去を美化することもよくあること。

>結論
Ustreamは正直。

LT全体としてはすごいというひとこと。

Joelのこのつぶやきにも現れてますが、発表ベースで30人以上、おそらく発表しなかった方も含めると相当数の方々から、新たな何かが生まれたわけです。
2日間で。
日本のコミュニティのパワーは、すごい。

発表者の皆さんも聴講者の皆さんもお疲れさまでした。(トータル3時間の連続LT。途中休憩はあり)

発表

最後に各賞の発表があり、私のWebアプリ「愛の告白」は、まさか、まさかのCakePHP賞の次点入賞

景品は、CakePHP辞典Nifty Cloudの無料チケット!(MASA-PさんNiftyさん、ありがとうございます)
いやー有り難いです。
発表すること自体が自分を成長させるものと思って発表したら、もったいない評価を頂戴してしまいました。

よっぽどのことが無い限り削除しませんので、気が向いたら使ってください。
彼女の誕生日に1年分の愛をつぶやくというのもいいですよー。

正直「誰得?」ですが、本来の使い方をすれば、「愛をつぶやかれた方、得」です。
@hampomさんは使うといいですね!

他にも書きたいことはいろいろあり、半年後の自分へのメッセージとしてまとめたいところですので、いろいろと続きます。
とりあえず以上で。

得点入れていただいた方、ありがとうございましたー。

お祭りレポートの残りおよび周辺記事はまた明日。

PHP Matsuri 今年のお祭りはスケールアップ

posted 2010-09-22 | written by mon_sat

こんにちは。mon_sat です。

CakePHP勉強会@東京(2010年5月)の記事で書いたイベント PHP Matsuri が、いよいよ、来週末10月2日~3日で開催されます。

参加者一覧のページで、参加メンバーのtwitter ID が確認できます。
現在、50名以上の方が登録されていますね。

去年の話( Cake Matsuri 2009 )

去年、わたしは、参加表明が遅れたこともあり、ワークショップとカンファレンスの2日間のうち、カンファレンスの日のみしか参加できませんでした。

カンファレンスは、CakePHPを使ったさまざまな活用事例に始まり、コアデベロッパー@predominant 氏や@jperras 氏による講演などなど、普段、知りたくても知れないことが盛りだくさんで、非常に有意義でした。
そして、前日のワークショップに参加できなかったことを、ずいぶん後悔したものです。

しかし、このイベントが素敵だったのは、知識がついたことだけがその理由ではありません。
以前にも記事にしましたが、終了後は、立食形式の懇親会でした。
カンファレンスの会場からスクールタイプの机を片付け、その場で、パーティーのような懇親会がスタート。
勉強で疲れた頭をビールで冷やしつつ、CakePHPという共通語をもった人たちで、ときにコアデベロッパーを交えて、語らえたことです。
わたしも拙い英語で、コアデベロッパーにCakePHPの方向性を聞いたものです。
ふたりとも、紳士的で、かつきさくに、質問に答えてくれました。

他にも、同じフレームワークを利用している人たちとの交流は面白く、4時間くらいだったでしょうか、本当に長いこと情報交換できたことを覚えています。

PHP Matsuri へスケールアップ

そして、今年、イベントはCake Matsuri から、PHP Matsuri へ。

PHP MATSURI 2010 in TOKYO 10.02-10.03 attendee

はい。フレームワークがCakePHPに限定されず、Cakeの冠からPHPになりました。

名前だけではありません。
まず、セッションのスケジュールを見てください。

  • 基調講演 「大人のためのプログラミングキャンプ」(講演者 よしおかひろたか)
  • Practical PHP5.3 (実践PHP5.3)(講演者 Nate Abele)
  • ユニットホスティングではじめるクラウド(仮)
  • フレームワーク嫌いの人の為のフレームワーク Lithium(講演者 Nate Abele / Joel Perras)
  • Re-imagining CakePHP: The path to 2.0(講演者 Graham Weldon)
  • Introduction to Symfony2(講演者 Kris Wallsmith)

ワクワクしてきませんか?
Lithium、CakePHPのコアデベロッパーが揃うだけでも驚いていたら、何と、Symfony2のKris氏まで!
CakePHP色が強いかと思いきや、Symfonyユーザーこそ見逃せないイベントになりました。
そうそうこんなチャンスは無いのでは!?

事例紹介も凄いことになりそうだという話を聞きました。
だって、今やいろんなサイトで使われているCakePHPとSymfonyおよび他のFW。
そこから、誰しもが知っている事例が聞けるらしいです!

いろんな意味でスケールアップしている今回のお祭り。
申込みがまだの方は、ぜひ。
定員になる前に(笑)

交流したい!

わたし?もちろん申し込みました。
去年の失敗を経て、今回は申込み開始と共に(笑)

PHPユーザーのお祭りなんですから。参加しないともったいない。
お祭りには、通常のフェスティバルと参加型のカーニバルがあると言われることがありますが、PHP Matsuri は、間違いなく、後者。
肩の力を抜いて、みんなで交流しましょう-。

ちなみに今回はtwitter ID による登録となります。
OAuth でログイン後、チケット購入リンクよりペイパルで購入です。

ハッカソンについて

今回のイベントは晴海グランドホテルで2日間行なわれます。
夜通し行なうイベントのため、チケットは2日間通しのチケットで、宿泊用の部屋が割り当てられます。(料金内です)

で、ハッカソンについて。
ハッカソンというと、なんだかバリバリ開発し続けるイメージもあったりします。
わたしも一瞬躊躇したクチです。

ですが。そういった方も。
あまり、肩肘張らずに、気軽に参加してみるのが良いと思います。
カンファレンスを見るだけでも充分勉強になるし、同じFWや違うFWを使っている人と意見交換するだけでも充分意味はあります。

そして、そのうえで、何か作りたくなったら、その表現欲を思う存分発散してみては?
わたしも今回は至極簡単なアプリにしようかと思っています。
ネタ99%のアプリなので、こういった機会じゃないと、作るきっかけなかったし(笑)

2日間で22,000円。宿泊および4食つき。
一緒にビールを飲みながら語らいませんか?

それでは、PHP Matsuri で会いましょう

PS   follow me @mon_sat

Python4PHPer 第7回講習会

posted 2010-08-17 | written by mon_sat

mon_satです。
PHP使いの人に向けたPython勉強会に参加してきました。
この勉強会は定期的に開催されているようですので、興味がある方はATND内に記載のGoogleグループを登録しておくと良いですよ。
わたしも前回満員で今回参加できました。

Google App Engine for Python

以前、Amazon EC2 の勉強会に参加しましたが、同じクラウドであるGoogle App Engine (以下GAE)にも大変興味があります。

GAEは、JAVAもしくはPythonの使用に限定されているため、PHPだけでやってきたわたしとしては少々難易度が高いです。

ですが。
調べてみると、Pythonは、JavaScript に似たところも多く、非常に扱いやすい言語であることが分かりました。

実際、習作で、Tweepy というモジュールを利用しTwitterアプリを作ってみたところ、結構簡単に作れました。

しかしながらPHPであればつまずかないところでも、Pythonへの理解が甘いためにつまずくこともあり、もっと言語仕様に近い理解が必要であることを痛感し、今回の勉強会参加となりました。

勉強会への準備

結局キャンセル待ちが届かなかった以前の勉強会の前に、GAEのSDKのインストールおよび、Hello World 程度のチュートリアルは終わらせておきました。

SDKのインストールで詰まってしまって勉強会で学べることが限られてしまうのはもったいないので、よっぽどの事情がない限りはインストールしておくことをお勧めします。

いざ、勉強会へ

この勉強会は、10時〜22時という長丁場です。
いつも以上に朝食をがっつりとり、準備万端で臨みました。

PHPとの文法の違いを説明しつつ、章の合間に、各自手を動かして理解を深める。というのを各章ごとにやっていくかたちで勉強会は進んできます。

途中お昼休憩と自己紹介タイム、夕食タイム、GAE実践タイム、がありますが、基本的には、講師の@makotokuwataさんが作成した資料をもとにした、上記の流れで進むスタイルでした。

Python の特徴を知る

この資料が非常にためになりました。
というのも、PHPでは○○と書くところを、Pythonでは××と書く。というように書いてあるため、非常に頭に入りやすいです。

各サイトで事前に基礎的な文法が分かっていたようでも、勉強会を通じて、Python ならではの文法をいくつも知ることができました。

個人的に、とくに知りたかったのはモジュールの使い方のところです。
Python は、JavaScript が分かれば、非常に扱いやすい言語だと思います。PHPと比べても学習コストは同等程度に低いでしょう。しかし、もっとも大きな違いが、この、モジュールによる拡張ではないかと思いました。

何かを作る上で、再利用性を考えれば、いきおい自分でもモジュールを作っていくことになります。
実際、Tweepyを利用して簡単なWebアプリを作ったときもモジュール化を試しました。
しかし、モジュールがどのように扱われているかを分からないため、何度も試行錯誤を繰り返し、結局「よく分からないけど動いたからいいや」で終わらせたのでした。

この勉強会ではさらっと流れましたが、Python では必須のモジュール周りの理解は、GAEでアプリを作るにあたって大きな意味がありそうです。
この点が理解できただけでも、参加して良かったと思います。ホント。

交流しよう

勉強会は知識を習得する場ではありますが、もうひとつの側面として、参加者同士が交流をする場でもあります。
なるべく、知識習得に終わらず、交流もして帰ろうと思っているのですが、この勉強会でも、素晴らしい出会いがありました。

上述の通り、途中、夕食タイムに先立ち、自己紹介タイムがありました。
これは、講師の方が、夕食時に交流しやすいようにと設けているものです。
簡単な自己PRをすることで、その後の食堂での夕食時に、各参加者がいろいろ話しやすくなります。

驚いたことがふたつ。

ひとつは、以前つとめていた会社で同じ部署だった方(@uniom)が参加していたこと。
かぶった期間が短すぎて、お互い、よく覚えていなかったんですけどね(笑)
でも、再会できて嬉しいです。

もうひとつは、もっとびっくり。
席がとなりになった彼(@shinout)。bioを見て分かるようにすごいキャリアで「いやあ負けられないなあ」なんて呑気に思っていたんだけど。

GAE実践タイムのとき、以前NerineというHTML5+CSS3を使ったプレゼンツールを使って作った、某勉強会用のプレゼン資料をGAEであげておいたんです。
で、「GAEで静的サイト動きますよ。ほら、このプレゼンツールがそう。かっこいいよね」とぱらぱらとスライドをめくっていたら、、、自己紹介ページのブログの名前のところで「あれ?このブログ、、、最近見ましたよ」と。
え?またまたあ。ほとんどCakePHPのことしか書いてないし、(彼はSymponyユーザー)誰も見てないことには定評あるんだから、他人の空似ならぬ、他人の空サイトでしょ?と、思った訳です。

NerineはスライドをHTMLで作りますので、当然ブログにはリンクが張ってあります。
で、飛ぶ訳です。そしたら
「やっぱり見ました」って。

どうやら、gitではまってインターネッツを彷徨い、この記事を見つけて、そして『まったく同じ解決手法で解決した』んだって。びっくりですよね、奥さん!
彼の場合、さくらではなく、コアサーバーでつまずき、(例として提示した) spush ではなくご丁寧に cpush というエイリアスにしたって(コアサーバー用だから)

ちなみに続きの記事がないのは、いまだ根本的解決してないからなんですけどね(笑)

ひとりの前途多望な若者を救ってしまいました。こんな場末のブログが。

一方、見せてもらった、彼の開発ブログ
CSS3使って、右カラムがすごいことになってます(笑)

内容もためになります。たとえばこれ。 MacOSXのコマンドからGoogleトランジット

$ open -a "/Applications/Google Chrome.app" http://www.yahoo.co.jp/

Macでこんなことできるなんて思いませんでした。
たしかにこれを使っていろいろやると便利なこと多そうです。

せっかく仲良くなれたので、起業のときとかで、何か困ったことがあったら遠慮なく言ってくださいね。

まとめてきな何か

他にもTwitterでフォローしたりと、何人かと交流できました。
こうやって輪が広がっていくのは嬉しいことです。

そしてなによりPythonの知識が深まりました。
クロージャとかデコレータとか、解説が丁寧だったので分かりやすかったです。
(途中Googleグループからファイルがダウンロードできないという予期せぬトラブルあり、最後駆け足になってしまいましたが)

こんなためになる勉強会を、激安で開催されている@makotokuwataさんには、感謝してもしきれません。

GAEを生かしたウェブアプリつくることが恩返しだと思って頑張りたいと思います。
Python がもっと普及するように祈りつつ。

というわけで、同じ時間を共有できたみなさん、どうもありがとうございましたー。

View Cache のキャッシュファイル名を変更する

posted 2010-07-26 | written by mon_sat

久しぶりにCakePHPネタを。

ひとつのAPPで複数のサイトを運営しているAPPがあります。
bootstrap.php内でURL等を見てどのサイトかを判断しているのですが、キャッシュを設定して困りました。
aaa.example.com/index と bbb.example.com/index で生成されるキャッシュファイルが同じになってしまいます。

どちらへアクセスしても先にキャッシュされたファイルを表示してしまうのです。

しかたがないので、以下のようにキャッシュを生成することを試みました。
aaa_index.php
bbb_index.php

キャッシュヘルパーを上書きする

コアのキャッシュヘルパーをコピーする

コアのキャッシュヘルパーを、APP内にコピーします。

cp cake/libs/view/helpers/cache.php app/views/helpers

キャッシュヘルパーを書き換える

キャッシュヘルパーで書き換える箇所は1箇所です。
キャッシュを書き出している __writeFile() の↓に1行追加します。

# app/views/helpers/cache.php
# __writeFile()
$path = $this->here;
if ($this->here == '/') {
  $path = 'home';
}
$path = Configure::read("subdomain") . "/" . $path; // この行を追加
$cache = strtolower(Inflector::slug($path));

subdomain はbootstrap.php にて設定してください。

この状態でURLにアクセスすると、aaa_index.phpのように、サブドメインが付加されてキャッシュファイルが作られます。
しかし、このままではdispatcherがキャッシュファイルを認識してくれません。

Dispatcher等を修正する

dispatcherの該当箇所を書き換える前に、dispatcherをコピーし、そのファイルを読むように変更しなくてはなりません。

CakePHP1.2であれば、この記事内のスライドが参考になります。
しかし、CakePHP1.3 (1.3.0)の場合、dispathcerはキャッシュしてくれないようです。

以下のように対処します。

DispatcherとBootstrapをコピーする

CakePHP1.3から登場のAPP/libsディレクトリに、コアのdispatcher.php とbootstrap.php をコピーします。

cp cake/libs/dispatcher.php app/libs
cp cake/libs/bootstrap.php app/libs

Dispatcherを書き換える

cached() 内に、1行追加します。

# app/libs/dispatcher.php
# cached()
$path = $this->here;
if ($this->here == '/') {
 $path = 'home';
}
$path = Configure::read("subdomain") . "/" . $path; // この行を追加
$path = strtolower(Inflector::slug($path));

次に、当Dispatcherを読み込むように修正します。

app/config/bootstrap.php にて、dispatcherの読み込み先を変更する

bootstrap.phpの最後の方に、下記を加えます。

# app/config/bootstrap.php
App::import('Lib', 'Dispatcher');

このままでは、コアのDispatcherも読みにいってしまうようですので、それを止めます。↓

Bootstrapを書き換える

コアからコピーしたapp/libs/bootstrap.phpを書き換えます。
(app/config内のbootstrap.phpではありません)

# app/libs/bootstrap.php
# 最後の行をコメントアウト
// require CAKE . 'dispatcher.php';

最後に、webroot/index.phpのbootstrap.phpの読み込みを変更します。

webroot/index.phpを書き換える

# app/webroot/index.php
# 次のように変更します
//if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
if (!include(APP_PATH . 'libs' . DS . 'bootstrap.php')) {

以上です。

もっとエレガントなやり方がありそうだけど、とりあえず。

Amazon EC2 ハンズオン・セミナー(2010年06月13日)

posted 2010-06-13 | written by mon_sat

「勉強会はブログに書くまでが勉強会」と以前学んだため、早速ブログ書く。

Twitterで Amazon EC2 ハンズオン・セミナー の存在を知ったので、勢いで申し込みました。
ちょうど『よくわかるAmazonEC2/S3入門 ―AmazonWebServicesクラウド活用と実践』の購入を考えていたところで、 自力で手に入る情報はたくさんありますが、それでも限界があるかもと思っていたときです。

タイミングよく入手した情報には、なにかしらの縁があるはず。
実際、大変有意義なセミナーとなりました。

事前のアカウント取得が超重要!

事前にAmazon Web Services のアカウントの取得が推奨されていましたので、申し込みのあとに取得を試みました。

結果的に、これは大正解でした。

というのも、アカウントの取得後、Amazon EC2 のインスタンスを起動するためには、クレジットカード情報の入力と、それに付随するAmazonからの電話に対する応答が必要です。

電話はオペレーターがいてかかってくるのではなく、コンピューターによって自動的にかけてきて、ウェブ画面上の認証コードをプッシュホンで入力するだけなのですが、さすがにセミナー中にそれをする訳にはいかないため、手順の説明のみ。

実際にインスタンスを立ち上げてこそのハンズオンセミナーなので、参加される方は、事前にアカウントを取得し、海外からかかってくる電話にどぎまぎしながら待ってみるといいよ!

そして会場へ!

当日。会場の法政大学の外濠校舎へ。できてそれほど経ってないのでしょう、設備の整ったきれいな校舎です。

比較的小さめの教室でしたが、勉強会の会場としてはベストな大きさ。
さらに、主催者の方に電源タップも用意していただき、無線LANも完備されています。

始まってみると、講師の方の他、サポートをしていただける方もスタンバってます。
なんか至れり尽くせりな感じでした。

Amazon EC2 の概要を学ぶ

まずは、クラウドコンピューティング自体の定義や特徴を学び、つづいて、Amazon Web Services (AWS) の特徴や機能の詳細を学びます。

AWSの各サービスについてと、EC2 Compute Unitsなどの用語については、今後自分で知識を深めていくにあたって非常に重要な予備知識です。
こういう話はサイトや本で知るよりも、話を聞いた方が手っ取り早いですね。今までよくわかってなかったところが一発で理解できました。

ああ、今考えれば、課金体系のところは、質問しておけば良かったかも。
完全に、忘れてました。
が、料金シミュレーション料金早見表が載っているページを教えてもらったので、あとで調べてみよう。

実際に Amazon EC2 のインスタンスを立ち上げる

その後、AWSのアカウントの取得の流れを一通り説明し、その後いよいよ、実際にインスタンスを立ち上げるところまでやりました。

インスタンスの種類やサーバーが設置された場所を選択したあと、起動するOSを選びます。
ここではAmazonが用意しているFedora Core + LAMPセットを選択。
マクドナルドのバリューセットのようですね(笑)

驚いたことに、数分もまたずにステータスが「準備完了だから使えるよ!」に変わりました。
これだけで、ウェブルートにアクセスすると、phpinfo()が表示されます。

え?こんなに簡単なの?

ちょっとびっくりです。多分自力でやってたら、ここまでくるのに数時間を要したことでしょう。。。

そして、インスタンス起動時に作成した秘密鍵で、SSHログインを試みます。

# 鍵を指定してSSHで接続
$ ssh -i ~/Downloads/us-east.pem root@ec2-174-129-xxx-xx.compute-1.amazonaws.com

rootであっさり接続できました。(指定し忘れて permission denied になったことは内緒)
このあたりでセミナーの終了時間が迫ってきていたため、概ねfinish。
まああとは、普通のサーバー設定ということですね。

最後に管理画面からステータスを右クリックしてでてくる Teminate で、インスタンスを落として終了。
これをしないと課金され続けてしまいますので、忘れずに!

ただし、インスタンスを落とすと、(EBSに保存するなどして永続化しないと)データはすべて消え去るそう。
この辺は自分で勉強せねば。

所感とか。あと所感とか

今回主催していただいたのは、AWS User Group -Japanで活躍されている方々。
実践形式のセミナーであったにもかかわらず、大きなトラブルなく終了。目標のインスタンス起動のみならず、SSHによるログインまでいくことができました。

もちろん今回の内容だけでは、今すぐ「EC2使ってがんがんいこうぜ」とまではいけませんが、今後知識を深めるための基礎固めには、十分すぎるほどとなりました。

EBSやS3といったところを知らないと、実際に運用することができませんので、まずはそこからはじめていき、他サーバーで稼働している何らかのサイトを動かしてみるところまではやってみたいと思います。
(もし続きのセミナーがあるならぜひとも参加したい!)

今後、日本にもAmazon EC2のサーバーが置かれる日も来るかもしれません。
そうなる前に今から準備しておきたいと思います。

これを書いたのは勉強会当日だったのですが、翌日の日経新聞の朝刊一面にAmazonが年内をめどに日本で事業開始との記事が出ました。

講師の方々、有意義なセミナー(しかも無料!)を開催していただき、ありがとうございましたー!

あと、終了後に新しい友達もできた。機会があれば、また情報交換しましょうね。

CakePHP勉強会@東京(2010年5月)

posted 2010-06-03 | written by mon_sat

CakePHP勉強会に行ってきました。
昨年10月末に行われたCakePHP Matsuri以来のイベント参加。

今回は、札幌・名古屋・福岡でサテライト会場ができ、どこも盛況だったもよう。
勉強会の申し込みもすぐにいっぱいとなり、今回の勉強会もひとつの祭りだったのではないでしょうか。

全体的な所感としては、@cakephperさんのテンポの良い司会と各発表者の発表の上手さがあって、あっという間の4時間でした。
テーマがCakePHP1.3ということで、非常に実践的な内容も盛りだくさんで、「勉強になった」という感じです。

CakePHP以外にも、Rackspaceというクラウドサーバーや、WAFというセキュリティソリューションの存在を知ったりと、CakePHPに関わっている人たちのCakePHPに限らないお話があって良かった。
次の日早速調べてみて、使いたくなったし。

PHP Matsuri by @yandoさん

資料
トップバッターはニューヨークから。
サテライト会場が全国規模で展開され、発表者は世界規模という、まさにグローバル。
(ちなみにNYは深夜1時すぎだそう)

発表された内容も、いきなりのビッグニュース。
なんと、前回のお祭りをスケールアップして、今年も行うらしい!
その名もPHP Matsuri

PHP5.3向けのLithiumがstable間近ということもあってか、冠から"Cake"がとれました。(symfonyとかも含まれていますよ)
前回同様CakePHPのコアデベロッパーに加え、今回はLithiumのコアデベロッパーの講演も開かれる予定とのこと。
これは胸が熱くなりますね!

開催概要をまとめると(現時点で予定していること)

  • 名称
    • PHP Matsuri
  • テーマ
    • もっと交流、もっと刺激的に
  • 開催期間
    • 2010-10-02(Sat) - 2010-10-03(Sun) ※オールナイト!
  • 場所
  • 人数
    • 100名程度
  • 参加費用
    • 2万円前後(食事・軽食・懇親会費用込み) ※早期購入は割引
  • 内容
    • コアデベロッパーによる講演
    • ワークショップ(入門者向け・中級者向け・Lithium入門者向け)
    • コンテスト(プラグイン・ライブラリ、ハッカソン、ドキュメント、パフォーマンス、etc)
  • 主催者からのお願い
    • 協賛企業・団体参加企業募集
    • 参加チケットの早期購入
    • コンテストへの参加
    • 運営スタッフ募集
  • Twitter ID

まず何といっても今回は土日を通してのイベントであるということに驚きました。
プログラムは夜遅くまで組まれていて、夜通し開発(まさにハッカソン!)できる環境とのことです。
興味のある講演を聴きつつ、他の時間で隣のエリアで実践する。
素敵ですね!

しかも場所は晴海。
自宅から至近です。徒歩圏内とまではいかずとも自転車で楽々行けます。車なら数分。
近隣の地理にも明るいので、何かお手伝いできることがあれば是非協力したいな。
と思っていたら運営スタッフ(お祭りなので「青年団」)募集とのこと。
協賛企業にはなれなそうだが、他の3つは協力できますので、よろしくお願いしますー。

ともかく楽しみなイベントの告知から始まった勉強会なのでした。

CakePHP1.3の概要 by @cakephperさん

資料
さて本題のCakePHP1.3のお話。
会場アンケートではまだまだ1.3の開発経験がない方も多く、「何が変わった?」というところに興味が集中しているようです。
変更点がまとまっていて、すぐにでも導入できるということが分かったのではないでしょうか?

私としては、一番はRoutingだと思っていて、Routingの改善が、その他に良い影響を及ぼしている箇所が随所に見られます。
CakePHP1.2ではどうしても力技で解決しなくては行けなかったことも、普通に使えるようになっていることが多いのです。
あとは、plugin周りも整備されている印象です。
1.3からplugins/xxx/webroot以下にcssや画像を置けるようになり、配布・利用がより簡単になっています。

さすがCakePHP1.x系の最終バージョンといえると思います。
CakePHPでの開発経験があり、1.3を使ったことがない方は、次の案件からは1.3をお勧めします。

Ktai Library on CakePHP1.3 by @ecworks_masapさん

資料(PDF on ecworks.jp)
Ktai Library 勉強会でもお世話になってる、作者さんです。
Ktai Library は、現在は、CakePHP1.3上で開発されているため、もし1.2のアプリケーションを1.3に移行するという場合であっても、Ktai Library周辺は変更点はありません。

この辺は実際に双方で開発してみて経験済み。
安心して使えます。
根幹部分はライブラリ化する前から使っているものとのことで、十分に安定していますし。

今回は今後のバージョンアップ予定の機能を、多数、発表していました。
個人的には、早期にplugin化が希望です。
(branchがあれば、協力できることは協力します!)

コアライブラリのエレガントなハック by @hiromi2424さん

資料
今回一番男前な発表をしたのは、CakePHP界での多大な貢献者の@hiromi2424さん。
twitterでの発言や今回の発表を聞いていて感じるのは、CakePHPの中身についての調査をとことんやっているということ。
何事も仕様を理解するというのは重要ですが、表面的なところだけさらって終了することが私は往々にしてあります。コアのコードもちょっと読んで終了とか。
ですが、このかたは、深いところまで理解している。
だからこそ、どうしても必要なこと(ハック)にあたったときに、どのようにやるかということを、「分かりやすく」説明できるのでしょう。

CakePHP触って間もない人は多少振り切られた方もいるかもしれませんが、間違いなく、今回の発表資料は保存版といえます。
今すぐブックマークを!

twitterとcloud serverとcakeで新規サービス by @takamunetaniiさん

資料
つづいて、Ktai Library 勉強会でもご一緒している@takamunetaniiさんの発表。
こんなにためになる事例紹介が過去あったか?というくらい今の私にはためになりました。

タイムリーだったのは、Twitter API / Cloud Server あたり。
ちょうど勉強し始めたところだったので。

あと発表が面白い。惹き付けるなあ。

Rackspace は、Lithium実行環境として最適かも。
最安$10ちょいですって、奥さん。
インスタンスをオフれたり、保存したイメージをコピーできたら最強だなあ。

Twitter API は、GAEで実行しても面白いと思う。
ということでPython勉強中。
(CakePHPユーザーにこそお勧めできる>python)

起業するとのことです。
ぜひ今後もよろしくお願いしますー。

ライトニングトークス

そしてLTの時間。
もう盛りだくさんの内容。LTじゃなくて通常の発表枠でいける内容ばっかり!

最後に@cakephperさんから恒例のIRC集会の案内があって終了。
ちなみに次回は2010年6月11日(金) 15:00-19:00と後日発表されました。
IRCサーバーが変更になっているので注意が必要です。
初めての方もぜひ。気軽に参加できる素敵な会です。(業務中に参加できる方限定ですがw)

懇親会

私の場合、勉強は2の次で、飲むために行っているようなものです。
#cake_beer というタグもあります。

今回は会場の机といすを片付けて、その場で懇親会開始。
ほとんどの人が参加したのではないでしょうか?
ついにはビールが底をつきました(笑)

懇親会中に@sizuhikoさんのテストについての発表あり。
テスト書いておくことで1.2 -> 1.3 への移行も楽ということが分かった。
横着せずにヘルパーとかもテスト書かねば。カバレッジ低いからなあ。。。

毎度思うのが、CakePHPな人たちと飲むのは楽しい。
CakePHPに限らずいろんな話題が出てきてためにもなる。
徐々にCakePHP飲み会や勉強会後の懇親会でご一緒した人が増えてきて、それもまた嬉しい。

サテライトの人たちとも交流できたらいいなあ。
チャンスは他地域勉強会とPHP MatsuriかCakeFest2010か。

その後、懇親会は終了。(会場を貸していただいたトライコーンさんに感謝!主催者の方々、とくに@kaz_29さんおつかれさまでした!)

ぞろぞろと2次会へ。

2次会ではテーブルごとに分かれて交流。
飲み会や勉強会でお世話になってる@cakephperさんや@yashioさんらと飲む。
(もうひとりのかた、忘れてしまった。ごめんなさいい<twitterやってたら連絡ください)
他テーブル含めて雰囲気(うまく変換できた)が良い。

今年はいろんな勉強会に行ってみよう。
興味の幅を広げねば。
そこで仕入れた話をすれば、みんなハッピーになれるかもだし、いつまでも話を聞いてばかりじゃ申し訳ないし。

まとめ

最近いろいろあって疲れる日々もあったが元気出てきた。
今回交流できた方々、次回お会いしましょう。
交流できなかった方々、次回こそ是非!

Ktai Library 勉強会 #1-2

posted 2010-05-10 | written by mon_sat

Ktai Library 勉強会に行ってきました。
といっても、第1回が3月8日(月)で第2回が4月16日(金)でしたので、もうかれこれ1ヶ月近く経とうとしていますが。。。

でも、ちっちゃいことは気にしないで、両日を通して学んだことをまとめつつ、感想などを。(遅ればせながら)

準備編

せっかく作者の@ecworks_masapさんが来るわけですから、できるだけ準備をしておこうというわけで、とりいそぎセットアップを。

CakePHPは1.2を使用しました。
(当日聞いたら1.3でも普通に使えるということで、第2回からは1.3を使用)
Ktai Library は、今後のバージョンアップに備えて、gitで持ってくる。

$ git clone http://github.com/MASA-P/KtaiLibrary.git

(app/vendors/ecw となるように、KtaiLibrary/vendors/ecwをコピーする)

絵文字アイコンが必要なので、それもダウンロード

$ cd app/webroot/img
$ wget http://start.typepad.jp/typecast/download/emoticons.zip
$ tar xfvz emoticons.zip

Ktai Library で必要なファイルを、app内にコピーします。
以下は、シンボリックリンクで代替。

$ cd app/config
$ ln -s /path/to/KtaiLibrary/app/config/ktai_session.php .
$ cd ../../app/controllers/components
$ ln -s /path/to/KtaiLibrary/app/controllers/components/ktai.php .
$ cd ../../../app/views/helpers
$ ln -s /path/to/KtaiLibrary/app/views/helpers/ktai.php .

app_controller.php内で、redirect()を上書きするため、KtaiLibrary/app/controllers/ktai_app_controller.phpの内容を、写す。
ver0.2.2以前の場合で、リバースルーティングに配列を渡している場合は、コレを追加
すべてのcontroller,viewでktaiコンポーネントとktaiヘルパーを使用する場合はapp_controller内の var $componentsとvar $helpersにKtaiを追加する。
また、以下を環境に合わせて追加

# app/app_controller.php
var $ktai = array(
    'use_img_emoji' => true,
    'input_encoding' => "UTF-8",
    'output_encoding' => "UTF-8",
);

NamedParamsのセパレータを変更

# app/config/routes.php
Router::connectNamed(array(), array('argSeparator' => '~'));

FireMobileSimulator

すでにメインブラウザをGoogle Chromeに完全移行したのですが、いかんせんChromeの拡張機能では、現状ではFireMobileSimulatorのようなものを作れません。
というわけで、ケータイからのアクセスをシミュレートするFireMobileSimulatorをFirefoxにアドオンします。
初めて使ったのですが、超絶便利でした。しかもbetaながら、タブごとに別々のuser_agentに切り替えるバージョンまであり、開発スピードが段違い。素晴らしい!

Hello Ktai Library !

ここまでできたら、Ktai Library の教科書Chapter4のリスト04-04、04-05および04-06を参考に、サンプルページを表示します。

以下、Ktai Library 開発用デモサイトソースより一部改訂して引用

# app/controllers/mypages_controller.php
function index() {}

# app/views/layouts/default.ctp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <?php if($ktai->is_iphone()){ ?><meta name="viewport" content="width=260"><?php } ?>
    <title><?php echo $title_for_layout; ?></title>
</head>
<body>
    <?php if(!$ktai->is_ktai()){ ?><div style="width: 240px;"><?php } ?>
    <?php echo $content_for_layout; ?>
    <div align="center">
        <hr width="90%" size="1" color="#333333" noshade>
        (C)2009 <a href="http://shop.example.com/">MyShop</a>
    </div>
    <?php if(!$ktai->is_ktai()){ ?></div><?php } ?>
</body>
</html>

# app/views/mypages/index.ctp
<center><?php $ktai->emoji(0xe753); ?>Hello, ktai Library!<?php $ktai->emoji(0xe753); ?></center>

絵文字付きでHello, Ktai Library!と表示されたら準備完了です。

このブログは記憶を頼りにまとめているので、記述をそのままやっても動かない場合もあると思います。
動かない場合はtwitterでお知らせください。

勉強会 #1

じつは、勉強会の1回目は、第4章を実践することがテーマだったので、準備万端で臨むと、もう終わってたりします(笑)

というわけで私は、今後の開発がしやすいように、いろいろとカスタマイズしていくことにしました。

Themeを使って、KtaiからのアクセスとPCからのアクセスで、表示を分ける

CakePHPのTheme機能を使って、Ktaiからのアクセスの場合は、themed/ktai を使用し、PCからのアクセスの場合は、通常のviewを使用するケースです。

まず、Ktaiからのアクセスかどうかを判別して、必要に応じてテーマを使用するようにします。

Ktaiからのアクセスかどうかは、 Ktaiコンポーネント(かKtaiヘルパー)のis_ktai()を使用します。

# 使用例
if ($this->Ktai->is_ktai()) {
    debug("Ktaiからのアクセスです");
}

Themeを分ける場合は、beforeRender()あたりがよさそうですので、app_controller.php内のbeforeRender()を変更します。

# app/app_controller.php
function beforeRender() {
    if ($this->Ktai->is_ktai()) {
        $this->view = "Theme";
        $this->theme = "ktai";
    }
}

これでThemeの使用ができますので、必要なファイルを作成します。
まずは、必要なディレクトリを作成しましょう。
app/views内に、themed/ktaiディレクトリを作成し、その中に、layoutsやelements、今回の場合はmypagesを作成します。

$ cd app/views
$ mkdir -p themed/ktai/layouts
$ mkdir -p themed/ktai/elements
$ mkdir -p themed/ktai/mypages

そして、レイアウトファイルを移動します。

$ cd app/views
$ mv layouts/default.ctp themed/ktai/layouts

ここで、いったん動作確認。
ケータイによるアクセスとPCからのアクセスで、別々のレイアウトファイルが使われていれば成功です。

mypages/index.ctpも忘れずに移動します。

$ cd app/views
$ mv mypages/index.ctp themed/ktai/mypages

その他

elementsでヘッダーやフッターを作ったりして、初日終了。

感想など

@yashioさんの呼びかけで始まったKtai Library 勉強会@関東ですが、非常に素敵な会でした。
まず何といっても雰囲気がよい。少人数故なのかどうかは分かりませんが、堅苦しくなくて良かったです。
また、講義を聞くスタイルではなくて、実践形式で進めるというのが、振り返ってみて良かったなと。講義を聞いて、その場で分かったつもりになって、実践しないでほったらかす人種のわたしとしては、手を動かす絶好の機会となりました。

なかにはセットアップに苦労していた方もいましたが、そういったことも含めて、実際にやってみることの大事さを再確認することができました。

最後は、みんなで歓談し、CakePHPやそれ以外のことについて情報交換。
『ゼッタイに第2回も参加しよう』と心に誓った第1回でした。

勉強会 #2

そして、第2回。今回は第5章ということで、非常にボリュームがあります。
しかし、その多くはPCによる管理画面の作成ですので、ケータイで作る部分はさほど多くない。
ひととおり読み込んで準備して、、、と思ったが、タイミング良く入っているケータイサイトのお仕事を進めないと進捗が遅れていることに気づく。
内容的にはフォームの作成で、サンプルと一緒の画面遷移なので、第5章を参考にしつつ作成しました。

teltoリンク

Ktai Library には、便利なmailto()というメソッドがある。これはいわゆるmailtoリンクを、キャリアに合わせて作成してくれるもの。

# 使用例 (for CakePHP 1.3)
// Site.mail はメールアドレスが入っています
// 第3引数で件名を第4引数で本文を指定可能です
$this->Ktai->mailto("メール",Configure::read('Site.mail'),"携帯サイト問合せ");

telto()もあるかなとリファレンスを見たが、なさそうなので急ごしらえした。

function telto($telno = "") {
    if (empty($telno)) {
        $telno = Configure::read('Site.tel');
    }
    $telnoWithoutHyphen = str_replace("-","",$telno);
    return "<a href=\"tel:{$telnoWithoutHyphen}\">{$telno}</a>";
}

なんでHtmlヘルパー使ってないんだっけか?相対URLになっちゃうんだっけかな?
おかげで$optionsも指定できないからあんまり意味ないかも。
ハイフンを消す必要があるかどうかも不明だけど、確認するの面倒だからまあ良し。

あと対応できない端末があるかもなあ。

その他

主に見栄えのところを調整した。
Ktai Library には、スタイル調整用の機能があります。

# app/app_controller.php
var $ktai = array(
    'style' => array(
        'h2' => "color:#ffffff;background-color:#0000ff;",
    ),
);

としておき、viewで

# view on CakePHP1.3
<div style="<?php $this->Ktai->style("h2"); ?>">見出し文字列</div>

のように使います。便利ですね。

自ヘルパー内で拡張して、divでラップしても面白いかもしれませんね。

感想など

外は雨模様。帰りにはみぞれになっていた。4月なのに。
そんな悪天候の中、参加者は全員出席だったそう。それだけ注目のKtai Libraryであり、Ktai Library 勉強会ということでしょう。

帰ってから実機で確認してみると、キャリアによって細かい動作が違ってくるものがあるということも分かった。
携帯サイトはノウハウの集積が不可欠だと思っていたが、それでもその大部分を担ってくれるKtai Libraryを使うかどうかで、開発効率は大きく違ってくるでしょう。

そして。
Ktai Library を使っても、なお、知っておかなくてはならないことが多いのが、携帯サイトの開発だと気づく。
だからこそ、Ktai Library の教科書では、題材に『ショッピングサイト』を選んだのではないだろうか。
幅広い機能が必要となるため、それを実践することで、必要なノウハウも(多数)身についていくのですから。

続きは次回。
では。

さくらインターネットでbakeする前に

posted 2010-02-19 | written by mon_sat

自分用にメモです。

さくらインターネットでCakePHPのbake等を利用する前に、bashへのパスを変更する必要があります。
CakePHPのバージョンアップの時に注意が必要です。

 #!/usr/local/bin/bash

以上です。

CakePHPのdatetime validation

posted 2010-02-17 | written by mon_sat

かゆいところに手が届くCakePHPですが、バリデーションルールにdatetimeがありません。
(私が知らないだけ?)

ということで、オリジナルメソッドとして、作成しました。

現在テスト中なので、完全ではありません。

# model

var $validate = array(
	'any_field_name' => array(
		'datetime' => array(
			'rule' => array('datetime' ,'ymd'),
			'message' => '日付の形式が間違っています',
			'allowEmpty' => true,
		),
	),

function datetime($data ,$format) {
	$datetime = current($data);
	$s = strrpos($datetime ," ");
	$date = substr($datetime ,0 ,$s);
	$time = substr($datetime ,$s + 1);
	$Validation =& Validation::getInstance();
	$result = $Validation->date($date ,$format) && $Validation->time($time);
	return $result;
}

(ひとりごと1)突っ込み歓迎(twitterで)
(ひとりごと2)app_model.phpにおくと動かないな。。。

CakePHPのアソシエーション

posted 2009-11-26 | written by mon_sat

CakePHPには、非常に強力なアソシエーションという機能があります。
これは相互に関連する複数のModel(テーブル)を一括して扱うための優れた仕組みです。

便利なアソシエーションですが、マニュアルを読んで理解したつもりでも、実際に活用する段になって、どのように設定すればよいのか迷うことがあります。

CakePHPに不慣れな方がアソシエーションで迷っていたときに、どのように教えてあげると分かりやすいのかという視点で、アソシエーションについてまとめてみました。

ここではHABTM(Has And BelongsTo Many)については触れません。それはまた別の機会で。

よくある勘違い

一般にAとBというふたつのモデルがあったとき、AモデルがBモデルを『ひとつ持っているときはhasOne』で『複数持っているときはhasMany』と認識している場合がありますが、その理解のままだと、ときに混乱してしまいます。

よくあるのが、以下のようなケースです。

  • ブログの記事を扱ったArticleモデルと、その記事の『編集中・投稿済・削除』等のステータスを扱うStatusモデルがあり
  • ArticleモデルとStatusモデルのアソシエーションを検討したとき
  • Article hasOne Status と間違ってしまう

ということがあります。

記事が、ひとつのステータスを保持するわけですから、hasOneだろうと考えると、『あれ?どうやってこれ設定するんだ???』となってしまいがちです。

つまり、上記の『ひとつ持っている』『複数持っている』という考え方は、忘れたほうが良いのです。

アソシエーションの基本

それでは、まず、基本的なところからおさらいしつつ、なぜ上述のような混乱に陥るのかを説明していきたいと思います。

アソシエーションの基本原則其の一:外部キーを持っているときはbelongsTo

まず、belongsToからおさらいしましょう。
belongsToは、あるモデル(B)があるモデル(A)に属している時に、B belongsTo A とアソシエーションを設定します。

『属してるってなんぞや?』というと、簡単に言えば、『hasOneもしくはhasManyされている』ということです。
A hasOne B もしくは、A hasMany Bという関係が成り立っている場合、B belongsTo Aというアソシエーションとなります。

hasOne や hasMany が存在せず、belongsTo だけ設定した場合でも、アプリによっては何の支障もなく動作します。
しかし、ここでは基礎を学ぶという意味で、逆方向のアソシエーションの存在と『対』で理解してもらうことから始めます。

外部キー

相手方のモデルの主キー(id)を『外部キー』といい、belongsToが設定されたモデルには、必ずこのフィールドが存在します。

たとえば、上述のArticleモデルが複数のコメントを扱うCommentモデルとhasManyの関係にある場合、

  • Article hasMany Comment
  • Comment belongsTo Article

ですから、Commentモデルには、article_idというフィールドが必要になります。

逆に言えば、以下の原則が成り立つということです。

アソシエーションの基本原則其の二:hasOneおよびhasManyの相手方のモデルには外部キーが存在する

A hasOne B もしくは A hasMany B というアソシエーションの場合、AモデルにはBモデルの外部キーはありません。
分かりやすいhasManyの例で説明すると、上述の例で、Articleモデルにcomment_idというフィールドを設定することは不可能です。
だってコメントは複数存在できるので、フィールドがひとつでは足りませんから(笑)

hasOne も hasMany と同類

『じゃあ、hasOneならあり得る?』かというと、そういうものではありません。
hasOneの場合、どっちに外部キーを置けばよいか分かりづらいため、外部キーを頼りに考えていくと、より混乱をきたしやすいようです。

そういう場合は、心の中で『hasOne は、hasMany の特殊な一形態』と唱えるようにしましょう。

だって、相手方(関連モデル)が『複数くっついているか、ひとつしかくっつかないか』の差でしか無いわけですから、hasOneなら自モデルに外部キーとなるはずはありません。

もちろんhasOneとhasManyはもっと本質的に違いますが、ここではどのようにアソシエーションすればよいかを考えるために単純化しています

外部キーの位置関係のまとめ

外部キーは、hasOneおよびhasManyのときには『相手方のモデルに存在』し、belongsToしているときは『自モデルに存在』するのです。

この原則を守れば、先頭で提示したマスタテーブルの問題も簡単に答えを導けます。

なお、なぜ上記の原則が正しいのかは、ここでは深く追求しませんのであしからず

マスタテーブルはhasOneではなくhasMany

答えを先に言ってしまいました。
それでは順を追って、なぜそうなるのかを説明していきたいと思います。

例の確認

  • ブログの記事を扱ったArticleモデルと、その記事のステータスを扱うStatusモデルがある
  • ArticleモデルとStatusモデルのアソシエーションを検討する
  • Statusモデルは、editing , published , deleted の3レコードのみからなるマスタテーブル

選択肢はみっつ

hasOneと仮定した場合

  • Article hasOne Status
  • Status belongsTo Article

hasManyと仮定した場合

  • Article hasMany Status
  • Status belongsTo Article

belongsToと仮定した場合

  • Article belongsTo Status
  • Status has??? Article

1: 上記の原則を当てはめて考える

まず、外部キーをどちらに設定するかを考えます。

ArticleかStatusか、どちらになるのかは、あまり考える必要が無いでしょう。
マスタテーブルに外部キーを設定する可能性はありませんから。
つまりStatusには外部キーが存在しないと言うことになります。

ということは。
Article belongsTo Status が確定します。

2: Status has??? Article は、hasOne ? hasMany ?

逆に言えば、Status has??? Article ということになります。

では、hasOneなのかhasManyなのかが問題となりますが、この答えも自明でしょう。
記事は無限に増え続けるわけですから、hasManyでないと、statusが足りません(笑)
Status hasMany Article が確定します。

3: ふたたび上記の原則を当てはめて検証する

結果的にhasManyを用いて、以下のようになることが分かりました。

  • Article belongsTo Status
  • Status hasMany Article

当初イメージした『ArticleがStatusを持っている』という、やってしまいがちな解釈からいくと、逆になりましたね。

では、外部キーがどうなっているかを考えましょう

  • Article
    • id
    • status_id
    • title タイトル
    • body 本文
    • Other その他
  • Status
    • id
    • name

はい。間違いなく、上記の原則に沿っています。

それでも新人くんが迷っていたら

1: hasOneは関係を逆にしてもhasOne

hasOneかhasManyかですが、次のような考え方も成り立ちます。
CakePHPのアソシエーションには『1対多』は存在しても『多対1』はありませんよね。
一方でhasOneは『1対1』ですから、User hasOne Profile が存在する場合。それは、Profile hasOne User でもあるわけです。

つまり『関係性をひっくり返してみて成り立つかどうか』で、A hasOne B か A belongsTo B にするべきかを判断することが可能といえます。

上述のArticleとStatusの例で、Article hasOne Status と(間違って)考えたとしても、逆の関係性=『Status hasOne Article が成り立たないから間違っている』と判断することが可能です。
そんなときはArticle hasMany Status なんて突拍子もないことを考えることなく、Article belongsTo Status ( = Status has??? Article)と考えることができますね。

2: hasMany の場合は、Counter Cache を考える

Article hasMany Comment というアソシエーションがあるとき、Article モデルに comment_count というフィールドを設置し、Commentモデルの(belongsTo)アソシエーション時にcounterCache => trueとすると、commentsテーブルにコメントが追加されたり削除されたりするたびに、自動的にArticle.comment_count にコメント数が入ります。

Articleモデル単体を呼び出すだけで、コメント数が分かるようになり、非常に便利です。

Commentモデルを一緒に引っ張ってきた場合でも、すべてのコメントを取得することなくコメント数が分かります

上述の例に戻ると、たとえば『各ステータス別の記事数が知りたい』という場合、以下のようにするでしょう。

# Status Model
# * id
# * name
# * article_count
$statuses = $this->Status->find('list', array('fields' => array('Status.name', 'Status.article_count'));
debug( $statuses );
// example
// editing => 3
// published => 35
// deleted => 7

Status hasMany Article が正解ということは、Counter Cacheが可能であることからも分かりますね。

まとめ

アソシエーションの基本原則

  • 外部キーを持っているときはbelongsTo
  • hasOneおよびhasManyの相手方のモデルには外部キーが存在する

迷ったら

  • マスタテーブルはhasMany
  • 外部キーから考える
  • hasOneで良いのか迷ったら、関係性を逆にしてhasOneが成り立たなかったら、belongsTo
  • hasManyに違和感があったら、Counter Cache が設定できないかを考え、設定できればhasManyで問題ない

以上です。

アソシエーションは油断すると中級者でも混乱します。
CakePHPに不慣れな新人さんが、アソシエーションで迷っていたときの参考に是非。

参考

関連: モデルを結びつける :: モデル :: CakePHPによる開発 :: マニュアル :: 1.2 Collection :: The Cookbook
【CakePHP】アソシエーションで迷ったらこう考えよう | ECWorks Blog

CakePHPでRSS2.0 feed

posted 2009-11-20 | written by mon_sat

CakePHPでRSSフィードをはくのは、ものすごくカンタンです。
今回、当ブログで身をもって体験しましたので、ぜひ。

とはいえ、すでに他のサイトでRSSについては、記事になっています。
(わたしも今回参照しながら対応させていただきました。m(_ _)m)
ぜひ、以下のサイトを参考に設定してみてください。

基本的な流れは、上記のサイトの方が詳しいので、そちらを見ていただくとしまして、今回は、Web&MUSICブログ QUALLさんのところで対応していたCDATAについての別解です。

まずは、RSS配信の流れから

ほぼ、上述のサイトそのままですが。

config/routes.phpを編集する

# config/routes.php
// 追記
Router::parseExtensions();

controllerでRequestHandlerコンポーネントを読み込む

articles_controller に記事があるとします。

# controllers/articles_controller.php or app_controller.php
// 例
var $components = array('RequestHandler',);

viewにRSS用のviewを作成

レイアウトファイルに標準のものを使用する場合は、viewファイルだけを作ります。
ここで、view内にfunctionを書くのに違和感ある場合は、helperに記入すると良いでしょう。
以下は、そんなケースの例です。
ここではMyHelper (my.php) というのがあるとします。

# views/articles/rss/index.ctp
// 記事一覧が $this->data に入っている場合
echo $rss->items($this->data, array($my, 'rss_transform'));

# views/helpers/my.php
<?php
class MyHelper extends AppHelper {
function rss_transform($item)
{
	return array(
			'title' => $item['articles']['title'],
			'link' => array('action' => 'view', $item['articles']['id']),
			'description' => array(
				'value' => $item['articles']['body'],
				'cdata' => true,
				'convertEntities' => false,
			),
			'pubDate' => ['articles']['posted_date'],
		);
}

以上でフィードがはかれます。
デフォルトURLは、 上記の例なら、 /articles/index.rss です。

CDATA対応

上記の description のように配列で記述すると、HTMLタグがそのまま入ります。
HTMLタグをエスケープしたくない場合は、convertEntities を false に、中身をCDATAで囲う場合はcdata を true に、それぞれ設定してください。

というわけで、以上です。
このブログもRSS配信をするために、数時間で対応できました。(現在フィードのテスト中)
上述したブログのおかけで、実際の配信部分は30分程度で完了し、残りはRoutingに苦労したのと、CDATAに対応するために試行錯誤しただけ。
CakePHPさまさまです。

CakePHPのバージョンを取得する方法

posted 2009-11-19 | written by mon_sat

CakePHPのイメージ画像CakePHPでプラグインを作っていると、現在稼働中のCakePHPのバージョンが1.2なのか1.3なのか知る必要がでてくるかもしれません。

そんなとき、どうやってCakePHPのバージョンを取得するのだろうと思って、IRCで聞いたら、教えてもらいました。

CakePHPのバージョン取得

debug( Configure::version() );
# ex : 1.2.3.8166

Configureクラスを見てみると、どうやら、coreのconfig/config.phpに値がある様子。
で、ソースを見てみると、、、

<?php
return $config['Cake.version'] = '1.2.3.8166';
?>

コメント除くとこれだけでした。

CakeMatsuriは有意義で楽しかった

posted 2009-11-18 | written by mon_sat

ブログを書くまでが勉強会であり、ブログを書くまでがCakeMatsuri(祭り)です。

ようやくブログを書き上げた。いまだ、祭り気分が抜けていなかったから、というわけではありませんが。
お祭りもようやく終了です。

私が参加したのは2日目のカンファレンス。
事例紹介等の通常のセッションの他にも、CakePHPのコアデベロッパーの話を直接聞けるということもあり、盛りだくさんの内容。

セッションの様子は、他の方のブログに任せまして、CakeMatsuriで一番得られたものについて書きたいと思います。

私が参加した理由のひとつは、CakePHPの勉強以上に、CakePHPを使っている方々との交流にありました。
せっかくユーザー数が増えているCakePHPです。使っている方とひとりでも多くお話しするに越したことはありません。さらに、コアデベロッパーともお話しできるチャンス。これを逃す手はありません。(まあ英語できないんですけどね。そこは気持ちで押し切ろうと)

さて、カンファレンス当日。
早めに家を出たつもりも、着いてみたら開始5分前。席はまばらでしたが、ちょうど良い時間でした。
cakephperさんに受付してもらい、CakeMatsuri特製手ぬぐいをもらいます。
2種類あって悩みましたが、より祭りっぽい方をチョイス。これはうちに帰って家族にプレゼント。(デザインが素敵だと喜ばれた)

席に着くとほどなくして、隣に外国の方が!日本の人と話ながら座りました。
さすがCakeMatsuriです。インターナショナルですな。
でも、ばりばり日本語が堪能でした。
昼食時に3人で乾杯しお弁当をいただきながらいろいろお話しできました。日本語で。
聞くと、イギリス生まれで7~8年前から日本で働いているとのこと。
着くなりMacBookを開け、おもむろにWindowsを立ち上げました(笑)
何と、USBに開発ツールを始めとしたアプリをすべて詰め込み、家(win)でも会社(Mac)でも同じ環境で作業できるんだそうです。ソノハッソウハナカッタワ。

わたしも今ではその人に教わったportableappsで、USB開発環境を作ってしまいました。Macは持ってないけど取引先のPCでもネットカフェでも開発ができるっていうのは、便利すなあ。
何せUSBさえ持っていけばgitも使えるんですから、ポータブル環境最高です。

さらに、会社でもMacBookにキーボードとディスプレイを外付けしてUSBに入れたアプリで作業していると、笑いながら話してくれました。うん、Macの意味ないですね(笑)
でも見ててMac欲しくなった。

ちなみに会場に来ている方のノートPCはほとんどMacBook。私は今のところノートPCを持たないでやっていけてるのですが、必要になったらこりゃ初Macですね。開発しやすそうだし。

ちなみにちなみに私が持って行ったのはPomera。テキストエディタだけです(恥)
もちろんひとりぼっち。

この陽気なイギリスの方とは、この日いちにち最後まで一緒でした。
そしてこの方と談笑しながら席に着いたのが、帰ってきてからこれ書いた人だと分かって大層びっくりしたヨシダスタジオの中の人。
大体同じくらいのCakePHP歴で、しかも、Croogoの話を聞いた後は、CakePHP製のCMSというお互いの興味がかぶっていることも判明。
職場も私の出身高校の近くということで何かと縁のある方。

さっきサイト見たらTwitter始めたらしいからさっそくフォローした。

カンファレンスの最後のプログラムは懇親会。
ここで、他にも多数の方とお話しできた。
なかには私と同姓同名(漢字一字違い)という方も。よくある名字とよくある名前の組み合わせでも、なかなか同姓同名には当たらないものです。
もしかしたら人生で初かも。

懇親会ではおもに、コアデベロッパーにアタック。
ここで、最近英語に触れてなかったことに激しく後悔。
簡単な表現すらでてこない。orz

気持ちでは押し切れないということが分かりました。

で、何とか話をしたのは。
まず、Grahamさん(@Predominant)とは。
CakePHP1.3やCakePHP2.0ではNameSpaceがサポートされるのかという質問をしたところ、両方ともPHP5.2をサポートするから使わないというお返事。
そうだよね。そうすると結局名付け問題には当分気を遣うことになりそうです。

あとは逆にCakePHPについての不満は?と聞かれてしまう。
実は、正直言って、今のところ不満はないんです。
で、「ない」と応えると、「It's Perfect???(さすがに完璧ってことはないよね?)」と言われてしまった。
「いやあ、パフォーマンスもキャッシュ使ったりで何とかやっているので問題ないんだ、今のところは」って言おうとして、3歳児レベルの片言の英語で必死に伝えようとするものの、伝わるわけはありませんので、最終的には「Yes. Perfect!」といって笑ってごまかした。

でも、Grahamさんは、そんな私のつたない英語を必死に聞いてくれて、ああ、心底良い人だと思いましたよ。わたしは。

イケメンJoelさん(@jperras)とは。
「今までKey Valueストアを避けてきたけど、これからは使います」と、言いたくて、後半だけ伝えた
「ぜひ使ってみてよ」と笑顔で言われた。
Joelさんは話をするとき、まっすぐ目を見て話す。真剣さが伝わってくる。見習いたい。

他にもいっぱいしゃべったが覚えていないのは、昼からビールを飲み続けたからかも知れない。
そして、2次会にもお邪魔した。
ここでは日本の食事や食材の話が多かった。

いわし明太子やらゴーヤチャンプルやら烏賊の一夜干しやら、続々と「食べたこと無いよね」という料理が注文される中、Grahamさんのベストは「たこわさ」だった。
日本食には全般的に満足してくれたみたいだ。しかもヘルシーすぎて日本から帰ったら7kgもやせていたらしい。
なんで日本人(とくに私)は、日本食で太れるのか。

振り返ってみると、2次会で見たコアデベロッパーの素の姿がとても印象的。
いたって普通だ。いや普通じゃない。いたって良い人たちだ。
デベロッパーが、人間的にも素晴らしいから、CakePHPのプロジェクトは成功しているんだと、改めて思った。
いつか海外のお祭りにも参加してみたい。

この日の解散は深夜0時に近かった。
2週間以上経っても、一瞬たりとも色あせない。とても有意義な一日だった。

運営に携わったすべての方々へ
本当に楽しめたお祭りでした。直接御礼の言葉を言えませんでしたが、どうもありがとうございました。

gitでhookを使ってWebサイトの自動更新

posted 2009-11-17 | written by mon_sat

以前のエントリでは、最終的に、テスト環境および本番環境で git pull することによって、それぞれを更新しています。
しかし、git には、push等が実行されると、その後に自動実行してくれるフックメカニズムが用意されています。
これを利用すれば、いちいちbareリポジトリにpush後、手動でgit pullすることなく、自動的に行なわせることができます。

下準備(テスト用と本番用でbranchを分ける)

これは必ずしも同じようにやる必要はありませんが、私の場合は、テスト環境用と本番環境用で、それぞれbranchを分けることにしています。

本番環境用を masterブランチ、テスト環境用を developブランチとします。
まずは、ブランチを作りましょう。

#localhost
# 現在のブランチを確認する
$ git branch -l
* master
$ git checkout -b develop
$ git branch -l
  master
* develop
$ git push origin develop

#server
$ cd /var/dev ;#テスト環境
$ git checkout -b develop origin/develop

開発はdevelopブランチもしくは、developブランチから分岐したブランチで行ない、テスト環境でのチェック後、developブランチをmasterブランチへmergeします。

#localhost
$ git branch -l
  master
* develop
$ edit;edit;edit;
$ git commit -a;
$ git push origin develop
#server
$ cd /var/dev ;#テスト環境
$ git pull origin develop ;#後述のフックを使用し不要になる操作
#本番サーバーでのテストが完了したら
#localhost
$ git cehckout master
$ git merge develop
$ git push origin master
#server
$ cd /var/www ;#本番環境
$ git pull origin master ;#後述のフックを使用し不要になる操作

これでふたつのブランチで運用することができました。

hookを使用する

hookは、bareリポジトリに設定します。

#server
$ cd /repos/bs.git ;#bareリポジトリ
$ cp -p hooks/post-update.sample post-update
$ vi hooks/post-update
# 追記
(cd /var/dev && git --git-dir=.git pull origin develop)
(cd /var/www && git --git-dir=.git pull origin master)

これで終了です。
実際にpullされるかを試してみましょう。

#localhost
$ git checkout develop
$ edit;edit;edit;
$ git commit -a
$ git push origin develop
# このときにテスト環境がdevelopブランチをpullしていることが確認できます。
$ git checkout master
$ git merge develop
$ git push origin master
# このときに本番環境がmasterブランチをpullしていることが確認できます。
$ git checkout develop ;#開発は常にmaster以外のブランチで行なう

めでたく自動化できました。
一度設定すると、本番サーバーでの作業が激減しますね。

さくらインターネットで git push するとgit-receive-pack: Command not found. と言われる問題の応急処置

posted 2009-11-17 | written by mon_sat

さくらインターネット(Sakura Internet)で、git push すると、git-receive-pack: Command not found.と表示されてうまくいかないという問題があります。

問題の原因としては、gitコマンドへのパスが通っていないためなのですが、その解決方法が分からずじまい。

この問題については、以下のようなサイトが参考になります。
git [てきとうにめも]

問題の原因が分かっても、どうにも対処できなかったので、pushする側で対処しました。

#localhost
$ cd
$ vi .gitconfig

# 以下を追加
[alias]
    spush = push --receive-pack=/home/username/local/git/bin/git-receive-pack
    sclone = clone --upload-pack=/home/username/local/git/bin/git-upload-pack

これで git spush origin master とすればreceive-packを指定したことになります。

#localhost
$ git commit;git commit;git commit
$ git spush origin master
$ git spush --all
$ git spush --tags

根本的解決が分かったらまたブログ書く。

CakePHPのCache(Viewキャッシュ)について考える

posted 2009-08-07 | written by mon_sat

今までさほどシビアにキャッシュについて考えてこなかったのですが、ここらでキャッシュについてまとめてみます。

まずView系のキャッシュのうち代表的なものを。

  1. element ( & requestAction ) キャッシュ
  2. Viewキャッシュ
  3. Html Cache Helper

3は、Bakeryに掲載されているMatt氏作成のヘルパーです。初回アクセス時にHTMLファイルを生成するため、以後PHPすら使われません。
多少の制限事項がありますので、それについては、後述。

下に行くほど(1→2→3)キャッシュの威力が大きくなります。
以下順番に見ていきましょう。

1.element ( & requestAction ) キャッシュ

Viewをelement単位でキャッシュしていく方法です。
ヘッダーやフッター、メニューバーやHTMLのheadなど、使い勝手は良いでしょう。
記述の方法は次の通り。

# view
$this->element('element_name' ,array('cache'=>array('time'=>"+2 hours"));
$this->element('element_name' ,array('cache'=>array('time'=>"+2 hours" ,'key'=>$id));

通常は前者で良いのですが、同じelementを別々にキャッシュしたいというときは、後者のようにkeyを渡します。
データが大きいときは'key'=>$id.'_'.md5($data)とでもすれば良いでしょう。

$idは無くてもかまいません。特定のキャッシュファイルを削除するときに分かりやすくしているだけです。

そして、そのelementファイルの中では、requestActionを使用して、データを取得すると良いでしょう。
requestActionは、任意のControllerの任意のactionを個別に呼びだす仕組みです。
右側のカラムにある「○○リスト」等を作成するために重宝します。

# view(element)
echo $this->requestAction('/controller_name/action_name/params' ,array('return'));

第二引数にreturnという値を含めるとレンダリングしたViewを(ただしlayoutは使用せず)返してくれます。
もしデータだけがほしい場合は、actionの最後に return $this->data; のようにしておいて、element側で取得したデータを整形して表示させましょう。

requestActionのパフォーマンスはあまり良くないようですので、elementのキャッシュを使用して効率化してください。

elementのキャッシュファイルを削除する

最後に、requestActionで取得しているデータが更新された場合に、elementのキャッシュファイルを削除する方法です。
ModelのafterSave()とafterDelete()を使用して削除するのが良いようです。

# Model
function afterSave() {
  @unlink(CACHE.'views'.DS.'element__action_name');
}
function afterDelete() {
  @unlink(CACHE.'views'.DS.'element__action_name');
}

この場合は、当該Modelの全elementのキャッシュが削除されます。

2.Viewキャッシュ

CakePHPのキャッシュは主にこのViewキャッシュで実現します。
このViewキャッシュの最大の特徴は、キャッシュファイルが存在する場合は、Controllerを通らずに、キャッシュされたページを表示する点にあります。

Controller , Component , Model , Behavior , View , Helper 等々のファイルを一切読み込みませんので、非常に高速にページが表示されます。

使用方法は、以下のようにします。

  • core.php や bootstrap.php 等で Configure::Cache.check を true にする // Configure::write('Cache.check ', true);
  • Controller で CacheHelper を読み込む // var $helpers = array('Cache');
  • Controller で cacheAction でキャッシュする時間を指定する // var $cacheAction = 3600; // 秒

$cacheAction には配列を渡して、細かく制御することが可能です。
通常はアクション名(と$id等)を使用します。詳しくはマニュアルをご参照のこと。

URLルーティングを行なっているときはURLになります。

次のいずれかの設定をした場合にはViewキャッシュは働きません。
キャッシュを(一時的に)使用しない場合には、ここを変更すると一発です。

  • Configure::Cache.disable を true にする // Configure::write('Cache.disable', true);
  • Configure::debug を 1 以上にする // Configure::write('debug', 2);

 

3.Html Cache Helper

これはViewをレンダリングした後にHTMLファイルを生成しておき、次回のアクセスからはそのHTMLファイルが直接読みだされるという仕組みのキャッシュです。
生成されるファイルがHTML(正確には非PHP)なので、動的に変化する要素の記述はできませんが、静的ページやRSSなどの書き出しには非常に優れています。

ヘルパーのコードは、HTML Cache - GitHub より取得し、APP/plugins/html_cache に配置してください。(downloadリンクよりzip形式でダウンロードすることも可能)

# directories
plugins/
        html_cache/
                   views/helpers/html_cache.php

使い方は簡単です。Controllerで$helperの最後にヘルパーを記述し、 webroot/.htaccess を編集するだけ。具体的には。

# Controller (他のHelperよりも後に記述)
var $helper = array('XXX','YYY','ZZZ','HtmlCache.HtmlCache');
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} ^GET$
    RewriteCond %{DOCUMENT_ROOT}/cache/$1/index.html -f
    RewriteRule ^(.*)$ /cache/$1/index.html [L]
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>

すると webroot/cache以下にURLをもとにファイルが生成されます。(cacheディレクトリはパーミッション707等で作成)
CakePHPは標準の設定で、CSS等ファイルが実在する場合は、ルーティングを行いません。そのため生成されたHTMLファイルが直接WEBサーバーより読み込まれます。

注意事項としては、
・/controller_name 等のURL(action名のないURL)やルートに対してはキャッシュが働かない
→/controller_name/indexのようなURLにはキャッシュが働きます
(仕様変更により可能になりました)
・キャッシュファイルの生存期間(expires)は設定できない
→bakeryの記述を参考にcron等で一定期間ごとに自動的に削除するようにします
・パーミッションを適切に設定する
→ファイルの書き出しはwebサーバー等の権限で行われます。webroot/cache以下に書き込み権限がないといけません。また、webroot/cacheはあらかじめディレクトリを作成しておく等の必要があります。

用途と環境に応じて選択しましょう

以上です。
利用する状況に応じてキャッシュの種類を使い分け、快適なサービスを提供していきましょう。

第1回CakePHPオフ会@関東

posted 2009-08-06 | written by mon_sat

昨日(2009/08/05)、前回のIRC集会終了後に話題が上がった「勉強会ではなく懇親会」(つまりいわゆるオフ会)が開催されました。

過去勉強会とは縁がなかったわたしも、今回は参加申込みをし、いそいそと曙橋の会場へ。
10数人という非常に適切な規模による、素敵なオフ会がスタートしました。

こういった場所に初参加のわたしは、もちろんみな初対面。CakePHP界では名の知れた方々もちらほら。
でも、IRC等ではちょっとだけ繋がりがあったりするので、遠慮することを遠慮させてもらいました。勝手に(笑)

プレミアムビールで乾杯し、ピザや差し入れのお菓子をつまみつつ、LTを聞いてあれやこれやとCakePHPについての話題を語る。
非常に幸せな空間がそこにありました。

会場はビルの屋上のテラスのようなところ。屋根を解放すると、心地よい風が吹き抜ける。
ビールが進むこと、進むこと。ぐいっぐいっ

ドイツで行なわれたCakePHPフェスタの貴重な話も聞けて、大満足な一夜でした。

<< previous
| 1 | 2 next >>

プロフィール

@mon_sat

CakePHPをよく利用しています。

理解の浅かった半年前と、何も知らなかった一年前の自分への教科書として書いています。
当たり前のことも平易に。

RSS2.0

カテゴリ別エントリ一覧

タグ別エントリ一覧

アーカイブ