CakePHPの環境を判定するTips (CakePHP Advent Calendar 10日目)
@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
「そんな軽いネタで大丈夫か?」
「大丈夫だ。問題ない」
ということで、さくっとしたネタでいきます。
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文字の要約リンクを追加しました
- 元記事修正に伴い、一部改訂しました
ひとりハッカソンでCakePHP+TwitterAPIアプリ
@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終了。お祭りは夜通し行われた!
こんにちは。@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 )
夜は長いのだよ。夜は
そして夜。
突然ホテル脇に止まるこの車。
会場に入ってきたのはこの二人。
テンションの上がる参加者達(笑)
というわけで本格的な開発作業に入る夜へ向けて、栄養が補給されたのでした。
さらに、恒例のケーキ。
しかも今回は3つ。
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さんの男前な発表のあと、いよいよ私。
- http://www.ustream.tv/channel/phpmatsuri phpmatsuri2 の 27分頃から
>会場にいなかった方へ
Ustreamでは分かりづらいですが、オーディエンスの反応は大絶賛でした。
もうあちこちでスタンディングオベーション。拍手や笑い声で私の声がかき消されるくらいの。
>会場にいた方へ
正直、反応なんて確認する余裕ありませんでしたから(言い訳)
あと記憶違いってあるよね。過去を美化することもよくあること。
>結論
Ustreamは正直。
LT全体としてはすごいというひとこと。
Joelのこのつぶやきにも現れてますが、発表ベースで30人以上、おそらく発表しなかった方も含めると相当数の方々から、新たな何かが生まれたわけです。
2日間で。
日本のコミュニティのパワーは、すごい。
発表者の皆さんも聴講者の皆さんもお疲れさまでした。(トータル3時間の連続LT。途中休憩はあり)
発表
最後に各賞の発表があり、私のWebアプリ「愛の告白」は、まさか、まさかのCakePHP賞の次点入賞。
景品は、CakePHP辞典とNifty Cloudの無料チケット!(MASA-PさんNiftyさん、ありがとうございます)
いやー有り難いです。
発表すること自体が自分を成長させるものと思って発表したら、もったいない評価を頂戴してしまいました。
よっぽどのことが無い限り削除しませんので、気が向いたら使ってください。
彼女の誕生日に1年分の愛をつぶやくというのもいいですよー。
- http://love.tklabo.net/ Bookmark it!
正直「誰得?」ですが、本来の使い方をすれば、「愛をつぶやかれた方、得」です。
@hampomさんは使うといいですね!
他にも書きたいことはいろいろあり、半年後の自分へのメッセージとしてまとめたいところですので、いろいろと続きます。
とりあえず以上で。
得点入れていただいた方、ありがとうございましたー。
お祭りレポートの残りおよび周辺記事はまた明日。
PHP Matsuri 今年のお祭りはスケールアップ
こんにちは。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 へ。
はい。フレームワークが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食つき。
一緒にビールを飲みながら語らいませんか?
PS follow me @mon_sat
View Cache のキャッシュファイル名を変更する
久しぶりに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')) {
以上です。
もっとエレガントなやり方がありそうだけど、とりあえず。
CakePHP勉強会@東京(2010年5月)
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
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する前に
自分用にメモです。
さくらインターネットでCakePHPのbake等を利用する前に、bashへのパスを変更する必要があります。
CakePHPのバージョンアップの時に注意が必要です。
#!/usr/local/bin/bash
以上です。
CakePHPのdatetime validation
かゆいところに手が届く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のバージョンを取得する方法
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'; ?>
コメント除くとこれだけでした。
