CakePHP クリスマス勉強会に参加してきました

久しぶりに勉強会に参加させて頂きました。
プレゼント交換あり、ケーキ&シャンパンのカフェタイムあり、もちろん濃い技術話もあり、ととても楽しい時間をありがとうございました。
聞いただけで終わりにならないよう、メモを公開します。


■cakephp3の話 [twitter:@yando]さん
・1.3.17 が6月に出てた
・2.3, 2,4系
modelの遅延読み込み
・3.0 来年にはリリースされそう
php5.4以降が対象 (phpの5以上は小数点以下が上がる毎にメモリ消費が少ない)
composer でインストール。追加、更新が自動
json ビューで楽
vagrant up だけでOK
app→App になった
tmp,vender がappと同じ階層になった(1つ上に移動した)
modelクラスが一新
→クエリの実行が遅延可能
→オブジェクトを触ったときに初めて実行される
過去と同じ使い方も可能だが、即実行で利点がなくなる
JSONビューはオブジェクトで渡せないのでこの方法
バリデーションはentityでやる
マイグレーションガイドは執筆中
Vagrantで作ってください!


■nanapiの話 [twitter:@wadap]さん
サービス:nanapi,アンサー
▼nanapi
開発5名,インフラ1名
githubに公式アカウントあり
cake1.3→2.4にバージョンup中
HWとOSまでは業者で、その後は自前
さくら、ライブドア、クララ、と引っ越ししてきた
毎月10パーセントくらいのアクセス増できている
VMでがっつり確保後、動的にリソース活用
chefにしておくことでサーバ構築の手間が減らせる
保存先はS3を利用している
AWSのルート53も利用

▼アンサーアプリの話
こっちは全てAWS
最初はミニマム1台、2週間たって現在6台運用。もうパンパンになりつつある
急激にアクセスが増える場合に対応
適切なサイジングが重要

▼開発環境
・外からアクセス可能にしたい
・さくらのVPS
・chefを使ってるのですぐに最新環境が作れる


■「Cakebookに翻訳貢献する方法とvagrant(仮)」 [twitter:@wa_terada]さん
翻訳結果をvagrant up で確認!


■「初PGで好きになったcakePHPの魅力(仮)」 [twitter:@hitomi_tw]さん
 マーケティング出身で開発者に!


■「レシピ本を書いて知った小ネタ3つ」 [twitter:@tomzoh]さん
デジタルサーカスの人
1.habtm 結合テーブルの命名はアルファベット順
2.組み込み定数
3.非正規フィールドの自動更新


■「Booklap運営の中で、役立ったこと良かったこと」[twitter:@Reo_Kasai]さん
Booklapから購入して下さい!


cakephpで二要素認証 [twitter:@cakephper]さん
scutum SaaS型のサービス 月3万より
twilio SMSはソフトバンク宛が一番安い(テストはソフトバンク宛がお勧め)
cakeのチュートリアルに二要素認証実装をデモ
150円でアメリカの電話番号を取得、そのあと2000円で正規ユーザーにアップグレード
github から twilio-phpライブラリを取得してインポート
SMSの場合、ウィルコムと固定電話はNG


■「ULURU製 A/Btest pluginの話」 [twitter:@hassy0607]さん
 司会お疲れ様です!


■「CakePHPとAzureの素敵な関係」 マイクロソフト武田さん
サーバサイドにlinuxもあるよ!
WebサイトからAzureにCakephpのセットあり。Github連携できる
東京リージョンが来年できる!
 会場提供ありがとうございます!


■「baserCMS」からの4つのご報告&お願い」 [twitter:@ecworks_masap]さん
12/2にVer3リリース
Cake1から2にするとコード変更なしで20%高速化
vagrant 対応
NPO設立!副理事長就任
テーマコンテスト参加してね!
テーマ・プラグイン販売サイト、来年オープン予定


■「最近つくってるもの」[twitter:@yando]さん
エンジンヤードの新サービス。構築時にAwsやAzureが選択できる
bitcoin危険。cpuめいっぱい使って採掘されてる。見つけてKill


■「テストデータの作り方 Fabricate」@sizuhikoさん
データジェネレータプラグイン
faker っていうライブラリもあるのでそれも使う


■「NC3」[twitter:@ecworks_masap]さん&桝川さん
net Commons 独立行政法人でで作ってる
3系をCakephp2で開発中
OSCのスポンサーになってるので会場で聞いて


■「なんかの話?」[twitter:@shin1x1]さん
servershell.php
ビルトインサーバー
技評PHPプログラミング診断室連載中
若気の至りで生み出した秘蔵のお宝コード募集中!




間違えてる箇所がありましたら教えて頂けると嬉しいです。

「体系的に学ぶ 安全なWebアプリケーションの作り方」

徳丸さんが書かれた「体系的に学ぶ 安全なWebアプリケーションの作り方」が3月に発売されます。
「体系的に学ぶ 安全なWebアプリケーションの作り方」3月1日発売です - ockeghem's blog

レビュアーの募集に応募してみたところ幸運にもレビュアーとして参加することができました。
初稿を読むだけでなく、技術力の高いレビュアーさん達の意見交換を読めたことは貴重な経験となり非常に勉強になりました。


書店に並ぶ前ですが、手元に本が届きましたので、ご紹介します。


写真を見て分かる通り、DVDが付録でついています。
このDVDには、サンプルのソースコードだけでなく、各種攻撃手法の検証環境構築用にVMも収録されています。


攻撃手法の名前を知っていても、その再現方法や対策まで覚えている方はそれほど多くないでしょう。
ネットで検索すれば、確かに見つかりますが各攻撃手法ごとに検索しなければならないし、それが信頼できるかどうか確認する手間を考えたら、手元にこの本一冊あれば…というのはかなり心強いのではないでしょうか。
また、プログラミングに直接かかわる部分だけでなく、ネットワーク経路に関する部分やマネジメントまで、非常にボリュームがあります。(その分、全て読み終えるには時間がかかるかもしれませんが)
私以外の優秀なレビュアーさん達のおかげで内容の濃い1冊です。初心者だけでなく、ある程度開発に慣れた方にも参考になる本だと思います。





最後に…
レビュアーとして原稿を読んでいましたが、本になって手元に届くとまた感慨深いものがありますね。
紙の方が読みやすいと思ってしまう私は既に古い人間なのかもしれません。。。

複数ファイルアップロード

一部のブラウザで可能な複数ファイルアップロードをした場合の処理をやってみた。

view側の記述

<?php echo $this->Form->input('Model.filename', array('type' => 'file','multiple'=>'multiple')); ?>

出力されるタグ

<input type="file" name="data[Model][filename]" multiple="multiple" id="ModelFilename" />

controllerでいつもの$this->data[Model]で受け取れるのを期待していたのだが、複数ファイルのはずが1ファイル分しかデータがとれない。

ちなみに期待した形式は

Array
(
    [Model] => Array
        (
            [filename] => Array
                (
                    [0] => Array
                        (
                            [name] => 
                            [type] => 
                            [tmp_name] => /tmp/xxxxxx
                            [error] => 0
                            [size] => 0
                        )

                    [1] => Array
                        (
                            [name] => 
                            [type] => 
                            [tmp_name] => /tmp/zzzzzz
                            [error] => 0
                            [size] => 0
                        )
                )

        )
)

なのだが、debug($this->data)の出力結果はこうだった。

Array
(
    [Model] => Array
        (
            [filename] => Array
                (
                    [name] => 
                    [type] => 
                    [tmp_name] => 
                    [error] => 4
                    [size] => 0
                )

        )
)

念のため debug($this->params)を見てみたところ

    [form] => Array
        (
            [files] => Array
                (
                    [name] => Array
                        (
                            [0] => file1.png
                            [1] => file2.png
                        )

                    [type] => Array
                        (
                            [0] => image/png
                            [1] => image/png
                        )

                    [tmp_name] => Array
                        (
                            [0] => /tmp/xxxxxxxxx
                            [1] => /tmp/zzzzzzzzz
                        )

                    [error] => Array
                        (
                            [0] => 0
                            [1] => 0
                        )

                    [size] => Array
                        (
                            [0] => 1234
                            [1] => 5678
                        )

                )

        )

結局、ファイルを一つずつチェックしたり処理をするのに不便なので、最初に期待した形式になるように詰め替えを行った。

$files = array();
foreach($this->params['form']['filename'] as $key=>$value){
    foreach($value as $indx=>$data)
        $files[$indx][$key]=$data;
}

Ajaxを使って1ファイルずつアップロードさせるパターンが多いと思うが、複数ファイルの扱いって他に方法はないのだろうか?

EmailComponentを使ってメール送信

cake1.1の頃にあったEmailComponentはまともに使えなかったけれど、1.2からはマシになったと話を聞いていたので、1.3の環境で試してみた。

参考サイトはこちら
http://koshikawa.net/tp/promenade.cgi?id=24
EmailComponentの使い方。htmlメール&テンプレート。CakePHPメール - CPA-LABテクニカル



まずはメール送信を行いたいcontrollerにEmailComponentを追加

class HogeController extends AppController {
    var $components = array('Email');

action側でメール送信に必要なプロパティをセット

$this->Email->reset();
$this->Email->to       = 'example@example.com';
$this->Email->from     = 'example@example.com';
$this->Email->return   = 'example@example.com';
$this->Email->replyTo  = 'example@example.com';
$this->Email->xMailer  = 'exampleMailer';
$this->Email->language = 'Japanese';
$this->Email->charset  = 'ISO-2022-JP';
$this->Email->subject  = 'メール件名';
$this->Email->send('メール本文');

このままでもメール送信そのものはできますが、文字化けするので自前でエンコード

$this->Email->subject  = mb_convert_encoding('メール件名', $this->Email->charset, Configure::read('App.encoding'));
$body = 'メールの本文';
$this->Email->send(mb_convert_encoding($body,$this->Email->charset, Configure::read('App.encoding')));

また、本文が70文字を超えると自動改行されて、そこで文字化けが発生してしまうので、一行の文字数を大きくしておく。(長いURLを送りたいときの改行も防げる)

$this->Email->lineLength = 1024;


というわけで、結局はこんな感じで書いてます。

function send($to, $subject, $body){
    $this->Email->to       = $to;
    $this->Email->from     = 'example@example.com';
    $this->Email->return   = 'example@example.com';;
    $this->Email->replyTo  = 'example@example.com';;
    $this->Email->xMailer  = 'exampleMailer';
    $this->Email->language = 'Japanese';
    $this->Email->charset  = 'ISO-2022-JP';
    $this->Email->lineLength = 1024;
    $this->Email->subject  = $this->_convEnc($subject);
    $this->Email->send($this->_convEnc($body));
}

function _convEnc($value){
    return mb_convert_encoding($value, $this->Email->charset, Configure::read('App.encoding'));
}

今回↓もかなり参考になりました。

Pocket詳解 CakePHP辞典

Pocket詳解 CakePHP辞典

hasAndBelongsToManyなテーブルでpaginate

cakephpのアソシエーションは便利ですが、マニュアルには詳しく書いてなかったので解決に時間がかかりました。


hasAndBelongsToManyな関係のテーブルがあった場合
書籍とタグの関係で、書籍名とタグで検索したい、という場合。

books
 id
 name

books_tags
 id
 book_id
 tag_id

tags
 id
 name

モデルでの記述

class Book extends AppModel {
    $hasAndBelongsToMany = array(
        'Tag' =>array(
            'className' => 'Tag',
            'joinTable' => 'books_tags',
            'with' => 'BooksTag',
            'foreignKey' => 'book_id',
            'associationForeignKey' => 'tag_id'
        )
    );
class Tag extends AppModel {
    $hasAndBelongsToMany = array(
        'Book' =>array(
            'className' => 'Book',
            'joinTable' => 'books_tags',
            'with' => 'BooksTag',
            'foreignKey' => 'tag_id',
            'associationForeignKey' => 'book_id'
        )
    );


書籍名で検索するなら

$this->paginate('Book',array('Book.name LIKE'=>$this->data['Book']['name'].'%'));

と簡単にできるのだが、タグで検索しようとして

$this->paginate('Book',array('Tag.id'=>$this->data['Tag']['id']));

なんてやっても

$this->paginate('Book',array('BooksTag.tag_id'=>$this->data['Tag']['id']));

なんてやっても、そんなカラムはないとエラーになってしまう。

デバッグ出力のクエリを見てみると、TagテーブルもBooksTagもJoinされていないので、エラーになるのは当たり前。


検索してみるといくつかヒットしたので、参考にして書き直してみる。(参考URL控えるの忘れてた(汗))

$this->paginate('BooksTag',array('BooksTag.tag_id'=>$this->data['Tag']['id']));

つまり「中間テーブルを基本にして検索する」という方法。
検索結果は意図通りに動いた。。。が、しかし、タグが必ず紐づいてる場合にしかヒットしなくなってしまうため、再び困る。


bindModelを使えば動的にModelのアソシエーションを変更できるのだから、検索時だけBooksTagをLEFT JOINする方法があるはず…と思い、キーワードを変えて再検索してみると、1.2の情報だけれど見つかったので実装してみる。

$conditions = array(
    'Book.name LIKE'=>$this->data['Book']['name'].'%',
    'BooksTag.tag_id'=>$this->data['Tag']['id']
);
$this->paginate['joins']=array(
    array(
        'type' => 'LEFT',
        'table' => 'books_tags',
        'alias'=>'BooksTag',
        'conditions' => 'Book.id=BooksTag.book_id'
    )
);
$this->paginate('Book',$conditions);

とりあえず解決。
paginateでのJOIN方法にたどり着くまでに時間がかかりすぎ(汗)

ただし、これは検索でもタグ指定が1つの場合のみ。複数指定するとカウントがおかしくなるらしいので注意が必要とのこと。(同一書籍に対して2タグヒット→2冊になる?)


この件に関して参考になりそうなサイトはこちら。
http://d.hatena.ne.jp/aroundthedistance/20090728/1248784179

PHPカンファレンス2010

亀本さん挨拶

名前ではなくハンドルを名乗った!

GREE青柳さん

3000万人利用を目指す
目標はNintendoDS?
30代以上で40%くらい<-TVCMの効果
オープン化
ソーシャルゲーム市場は数千億円規模に。。。?
ソーシャルゲームはスモールスタートが可能->運用は重要度高い
ゲームを作るというよりソーシャルという意識
GREEプラットフォーム
パートナーさんのゲームのCMをGREE負担でやった
トップの変更・動線変更でアプリ利用&コイン利用が大幅増
コンサルティングチームの立ち上げ(データ提供も)
パートナータイトルのCMが今後も続く
スマートフォン展開今年中に公表
海外展開… アジア・北米展開を計画中

CakePHPで作るニフティWebサービスのレシピ ニフティ 小田さん

ニフティxPHP
シュフモ、 @niftyストア、 トピックイット、コネタマなど
cakePHPの紹介
冗長化
NASにアプリケーションを配置▶レスポンス悪化
ファイルベースのセッション管理▶一定確率でレスポンスが帰ってこない▶DBで管理
マスターとスレーブ▶AppModelを書き換えて動的に接続先の変更
デプロイ: Webistrano の導入(RonR,デブロイ用サーバ、subversionかgit)
ニフティクラウドについて
コントロールパネルから5分以内でできる
ニフティクラウドの今後 SOAP,REST
新機能:サーバコピー、オートスケール、基本監視、稼働状況レポート

OpenPNE 手嶋さん

2002年創業手嶋屋 社内開発▶公開
公開から五年。少人数〜多人数、多ジャンル(エンタメ、企業、地域など)で構築されている
設置ペース: 週に100〜150サイト(ダウンロード数ではない)
OpenPNE「情報ネットワーク社会を作る」手嶋屋「組織の進化に貢献する」
デモ OpenPNE3.6
ユーザ画面:アクティビティ(つぶやき)、いろいろな機能の取り外しが可能
管理画面:ブラックリスト管理、プラグイン設定(Symfony)、ガジェット単位
モバイルオープンソーシャル対応(一部個別対応必要)
100万人オーバーのパフォーマンス
SNS=ネットワーク上の組織を作るサービス
ソーシャルCRM
シングルサインオン、外部サービスとの連携
開発者募集!!

新しいPHPアプリケーションのテスト手法 大垣さん

バージョンアップ効率化。真面目にやろうと思うと結構大変
PCIDSS
重要なセキュリティパッチは一ヶ月以内
優先順位の高いシステム及びデバイスは一ヶ月以内
make test では不十分
php本体の動作確認難。アプリの動作確認はもっと難。(マルチリンガルサイトやケータイ向けは特に難題)
PROVE for PHP : アプリレベルのテストスイート。バージョンアップ時に強い。テストケース書かなくていい
Zend Engineモジュールとして動作
追加予定機能 : ソースカバレッジ分析、オーバーライド関数追加、PROVE for ANY
SQLクエリとphp変数を一緒に記録可能になる▶セキュリティ向け
どうしてもPHP4を使いたい方〜
1リクエスト1ログファイル
PHP認定機構の紹介〜これからWeb業界に入る人向け〜

Zynga

規模感が全然違う!
ネット人口の十パーセントがプレイ
Facebook内のトップゲームのほとんどがZynga
サーバ追加一週間で1000!
書き込み多、リリース後数日で大量の負荷
クラウド:水平方向でスケール。全てが非同期
NoSQL(KVS)
MySQL(非同期)
Membase
Mcmux
pecl-memcache
FontLabel
キャラクターは日本のアニメに影響を受けている?という質問に対して「いい質問ですねw」「ユーザーテストで反応が良かったものを採用している」
日本だけを対象に開発する予定はない
東京オフィスの目的は?従業員募集中ですw
東京60人(ウノウ)くらい。海外では規模が多すぎて分からないw
MySQLを使う理由は?▶信頼性が高い、LAMPスタッフだったから
MySQL書き込みの工夫▶頻度調整で対応できた。水平に分割しているので困ってない

cakePHP1.3 管理者アクション

久々の新規案件のためやっとcakePHP1.3を触れるようになりました。
実は1.2も実務では使っていなかったため、いろいろと戸惑うことばかりです(汗)


まずは、管理者アクションからと思い、確かadmin_routingとかがあったな、と検索。
CakePHP 管理者用アクション - Shin x blog
を参考に設定していく。

しかし、1.2の情報のため、ソースを見てみるとどうやら違うらしい。

/app/config/core.php

// 1.2の場合
define('CAKE_ADMIN', 'admin');

// 1.3の場合
Configure::write('Routing.prefixes', array('admin'));

とはいえ、アンコメントするだけなので、やることは同じ。
1.2の場合は、rout.phpの修正も必要だったらしいが、1.3はこれだけらしい。


そして、controller側で、管理者アクションかどうかを判断する。

if (!empty($this->params['prefix'])
    && $this->params['prefix']=='admin'
    && !empty($this->params['admin'])) {
    $this->layout='admin';
} else {
    $this->layout='user';
}

という感じになるらしい。
もし間違ってたら指摘して下さい。

これからAuthComponentも組み込んで画面を振り分けしたりする予定です。


追記

Routing.prefixesにadminしか指定していないなら、管理者アクションかどうかの判別は、こちらの方がすっきりすると思います。

if (!empty($this->params['prefix'])
    && !empty($this->params[$this->params['prefix']])) {
    $this->layout='admin';
} else {
    $this->layout='user';
}