Hatena::ブログ(Diary)

130単位

2011-04-12

CakePHP CSVでインポート/エクスポートするビヘイビア

deeeki/cakephp-csvio - GitHub
https://github.com/deeeki/cakephp-csvio

つくってみました。

これは何

使いかた

準備
ビヘイビアのセット
<?php
//なんらかのModel内
public $actsAs = array('CsvIo');
インポート/エクスポート
<?php
//なんらかのController内
$this->Model->importCsv();
$this->Model->exportCsv();

同梱コントローラーでの作業例

  • 対象テーブル
CREATE TABLE IF NOT EXISTS `items` (
	`id` int(11) NOT NULL AUTO_INCREMENT,
	`name` varchar(255) NOT NULL,
	`price` int(11) NOT NULL,
	`created` datetime NOT NULL,
	`modified` datetime NOT NULL,
	PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
  • app/controllers/admin/csv_controller.phpに配置
  • bootstrap.php(core.php)に追記
Configure::write('Routing.prefixes', array('admin'));
  • http://your.cake.app/admin/csv/にアクセス

f:id:deeeki:20110413010750j:image

  • export_allクリック

f:id:deeeki:20110413010749j:image

id,name,price,created,modified
1,"レーザービーム/微かなカオリ [初回限定盤]",1500,"2011-04-12 23:09:47","2011-04-12 23:09:47"
2,"レーザービーム/微かなカオリ [通常盤]",1000,"2011-04-12 23:10:00","2011-04-12 23:10:00"
  • config/csv/items.csvへコピー
  • http://your.cake.app/admin/csv/にアクセス

f:id:deeeki:20110413010748j:image

  • import_allまたはitemsクリック

f:id:deeeki:20110413010746j:image

f:id:deeeki:20110413010744j:image


Windowsのローカルな開発環境で作業してたり、非エンジニアな他のメンバーにデータ入力を行ってもらうとかの場合にお役に立てるかと思います。よかったら使ってみてください。フィードバックいただけるとありがたいです!



Pocket詳解 CakePHP辞典

関連記事

*1:appディレクトリ丸ごとがお手軽

2011-03-29

CakePHP 日付別パーティショニングするビヘイビア

MySQL 日付別パーティショニングの運用 - 130単位

前回MySQLのパーティショニングを簡単にまとめてみました。実験はCakePHPアプリでしていて、ついでにつくったビヘイビアを晒してみます。GitHubにもあげてます。

no title

ビヘイビア

app/models/behaviors/以下にdate_partitionable.phpとして保存します。中身はSQLをつくって実行しているだけの単純なものです。

<?php
class DatePartitionableBehavior extends ModelBehavior {
    public function createPartition(&$Model, $field, $date) {
        $ts = strtotime($date);
        if ($ts === false) { return false; }

        $partition_name = $this->_getPartitionName($ts);
        $partition_date = $this->_getPartitionDate($ts);

        $sql = "ALTER TABLE {$Model->table} PARTITION BY RANGE (TO_DAYS({$field})) ";
        $sql .= "(PARTITION {$partition_name} VALUES LESS THAN (TO_DAYS('{$partition_date}')) COMMENT = '{$partition_date}')";

        return $Model->query($sql);
    }

    public function removePartition(&$Model) {
        $sql = "ALTER TABLE {$Model->table} REMOVE PARTITIONING";

        return $Model->query($sql);
    }

    public function addPartition(&$Model, $date) {
        $ts = strtotime($date);
        if ($ts === false) { return false; }

        $partition_name = $this->_getPartitionName($ts);
        $partition_date = $this->_getPartitionDate($ts);

        $sql = "ALTER TABLE {$Model->table} ADD PARTITION ";
        $sql .= "(PARTITION {$partition_name} VALUES LESS THAN (TO_DAYS('{$partition_date}')) COMMENT = '{$partition_date}')";

        return $Model->query($sql);
    }

    public function dropPartition(&$Model, $date) {
        $ts = strtotime($date);
        if ($ts === false) { return false; }

        $partition_name = $this->_getPartitionName($ts);

        $sql = "ALTER TABLE {$Model->table} DROP PARTITION {$partition_name}";

        return $Model->query($sql);
    }

    private function _getPartitionName($ts) {
        return 'p' . date('Ymd', $ts);
    }

    private function _getPartitionDate($ts) {
        return date('Y-m-d', $ts + 86400) . ' 00:00:00';
    }
}

Cakeシェル

Cakeシェルのサンプルです。app/vendors/shells/以下にpartition.phpとして保存します。usesプロパティの先頭要素のモデルがパーティショニングの対象になります。main()以外のメソッドをつくることで、Cakeタスクの代わりになります。引数でモデルを指定してもよかったのですが、面倒なのでやめました。

<?php
class PartitionShell extends Shell {
    public $uses = array('GameHistory'); //input partitionable target model name on first.
    public $tasks = array();

    public function startup() {
        $this->{$this->modelClass}->Behaviors->attach('DatePartitionable');
    }

    public function create() {
        $count = (isset($this->args[0])) ? intval($this->args[0]) : 1;

        $this->{$this->modelClass}->createPartition('created', date('Ymd'));
        for ($i = 1; $i < $count; $i++) {
            $this->{$this->modelClass}->addPartition(date('Ymd', strtotime('+' . $i . ' days')));
        }
    }

    public function remove() {
        $this->{$this->modelClass}->removePartition();
    }

    public function add() {
        if (!isset($this->args[0])) {
            $this->err("[ERROR] require date parameter. ex: 'add 20110101'");
            return;
        }
        $this->{$this->modelClass}->addPartition($this->args[0]);
    }

    public function drop() {
        if (!isset($this->args[0])) {
            $this->err("[ERROR] require date parameter. ex: 'drop 20110101'");
            return;
        }
        $this->{$this->modelClass}->dropPartition($this->args[0]);
    }
}

Cakeシェル実行例

cake/console/cake partition create
#-> ALTER TABLE game_histories PARTITION BY RANGE (TO_DAYS(created)) (PARTITION p20110329 VALUES LESS THAN (TO_DAYS('2011-03-30 00:00:00')) COMMENT = '2011-03-30 00:00:00')
cake/console/cake partition add 20110330
#-> ALTER TABLE game_histories ADD PARTITION (PARTITION p20110330 VALUES LESS THAN (TO_DAYS('2011-03-31 00:00:00')) COMMENT = '2011-03-31 00:00:00')
cake/console/cake partition drop 20110330
#-> ALTER TABLE game_histories DROP PARTITION p20110330
cake/console/cake partition remove
#-> ALTER TABLE game_histories REMOVE PARTITIONING

直打ちで試してみて問題なさそうであれば、cronジョブに登録して自動化するといいんじゃないかと思います。不具合などありましたらご報告いたけますと助かります!


4798027456
Pocket詳解 CakePHP辞典

4774142948
エキスパートのためのMySQL[運用+管理]トラブルシューティングガイド

関連記事

2011-02-15

CakePHP env()で環境判定してシェル実行

開発や本番など、環境によって設定を切り替えるには大きく分けて2つの方法があるかと思います。

  • ファイルやディレクトリの名前や中身で判定
  • (サーバー)環境変数で判定

今回は後者の判定方法についてです。

env()

CakePHPにはenv()という便利な関数があり、$_SERVERもしくは$_ENVで定義されている変数をうまい具合に取得することができます。

env(string $key)

有効なソースから環境変数を取得します。$_SERVER あるいは $_ENV が無効な場合、代替として使用されます。

この関数はサポートしていないサーバでも PHP_SELF や DOCUMENT_ROOT をエミュレートします。実際に完全なエミュレーションラッパーですので、 $_SERVER や getenv() の代わりに常に env() を使用することは(特にコードを配布する計画がある場合には)よい考えです。

no title

例として、環境の判定のために「CAKE_ENV」というキーを用いることにします。env('CAKE_ENV')の返り値で判定できます。

Webからアクセス

#開発環境
SetEnv CAKE_ENV development
#本番環境
#SetEnv CAKE_ENV production

とApacheの設定に環境に応じて記述することで判定できます。

シェル

ようやく本題です。

コマンドライン
export CAKE_ENV="development"

としてからシェルを実行すればokです。サーバーで1つの環境しか使わないのであれば、.bashrcなどに記述してもいいと思います。

CAKE_ENV="development" /full/path/to/cakeshell myshell

としてワンライナーで実行することもできます。

cron

cronで自動実行する場合、crontab -e で以下のように書くことができました。

CAKE_ENV="development"
0 * * * * /full/path/to/cakeshell myshell1
0 * * * * /full/path/to/cakeshell myshell2
CAKE_ENV="production"
0 * * * * /full/path/to/cakeshell myshell1
0 * * * * /full/path/to/cakeshell myshell2

先の2つのジョブにはdevelopmentが適用され、後の2つはproductionとして実行されます。MAILTOなどと同様に、自由に変数を定義できるようです。ただし深く調べたわけではないため、もしかしたら不具合が起きるかもしれません。冗長にはなってしまいますが、各ジョブを先程のワンライナーの記述にしておいたほうが無難というか安全です。

ちなみにファイル/ディレクトリによる判定は、Webかコマンドかにとらわれずに処理できるのがメリットといえます。

関連リンク


Pocket詳解 CakePHP辞典
Pocket詳解 CakePHP辞典


【関連記事】
CakePHP cronでシェル実行 - 130単位

2011-01-18

CakePHP新春勉強会に参加した #cakephpstudy

CakePHP新春勉強会 東京 : ATND
http://atnd.org/events/11810

LTした

たいした話もできないので、皆さんに聞いてみたいことをネタにしてみました。前のエントリはその草稿的な内容でもありました。

スライド
404 Not Found
URL設計
  • Model単位
    • 規約通りのModel=Controllerな形式
    • 複数形ControllerのURL
  • ユースケース単位
    • 自由で柔軟なURL
    • Controller#loadModel()とかして対応

皆さんに聞いてみたところ、どちらも4割程度の方が手をあげてくださいました。まあ半々といったところでしょうか。単純なCRUD中心の業務アプリであればModel単位が適しているでしょうし、アプリの性質にもよるかと思います。

複数Modelのやりとり
  • Modelから別のModelを呼ぶ
  • ClassResistroy#init()を使う
  • 場合によってはControllerとModelの中継用クラスを作る
  • $useTable = false なModelとか

テーブルのない中継用のModelを作ったりするかどうか聞いてみたところ、3割程度の方が手をあげてくださいました。おそらく使い込んでいるような方々にとっては、割と一般的な手法なのだということがわかりました。

パラメータの扱い
  • カスタムroute / Passedパラメータ
    • controller/action/1
  • Namedパラメータ
    • controller/action/key:1
  • クエリストリング
    • controller/action/?key=1

クエリストリングを使うかどうか聞いてみたところ、手をあげてくださったのは1割もいませんでした。クエリストリングを使うようにした自分は少数派のようです。ソーシャルアプリなら特に問題ないかと思うのですが、SEO的には微妙だったりするので、一般的にはNamedパラメータを利用したほうがよさそうな感じです。

感想とか

  • CakePHP2.0は待ち遠しいけど投入できるまではまだしばらくかかりそう
  • Livlisの開発事例は興味深かった
    • もとはmixiアプリだった
    • リリースまでの開発期間は4ヶ月程度
    • AWSのプロダクトを積極的に採用
    • Nginxを採用
    • Lithium移行を考えてる
  • BaserCMS使ってみたい
    • 比較表みるとCroogoよりも使いやすそう
  • CakePHPオンライン勉強会(3月予定)に期待
  • @さんのLithiumコードあとで読みたい
  • Mini DisplayPort-DVIケーブルは持ってても使えない
    • VGAの貸して下さった @さんありがとうございました><
  • CakePHPでソーシャルアプリの事例は心強かった
    • とはいえインフラの知識は相当必要そう

内容はともかく、初LTが経験できて良かったです。会場提供のジンガジャパン様、運営の皆様、参加された皆様、どうもありがとうございました!


Pocket詳解 CakePHP辞典
Pocket詳解 CakePHP辞典


【関連記事】
CakePHP開発振り返りメモ - 130単位

2011-01-16

CakePHP開発振り返りメモ

モバイルソーシャルアプリでのものです。アプリのパフォーマンスなど運用面はふくんでません。

各モジュール概要

Config
  • 種類別にファイル作成して管理
    • 環境依存設定
    • 定数定義
    • エラー文言定義
  • bootstrap
    • core.phpの修正
    • 上記ファイルの読み込み
  • routes
    • ユーザーページのURLを定義
Controller
  • AppController
    • OpenSocial対応
    • 絵文字変換
    • ユーザー読み込み
  • Component
    • OpenSocial用URLに一括置換
Model
  • AppModel
    • スレーブ対応
    • ページネーション修正
  • Behavior
    • Containable
      • アソシエーション補助
    • SoftDeletable
      • 論理削除
      • 削除フラグ判定のWHERE句生成部分を修正
View
  • Helper
    • アプリ用ヘルパー
      • アイテム画像URLの生成とか
Plugin
  • DebugKit(開発用)
Vendor
  • OAuth
  • HTML:Emoji
Shell
  • 集計用バッチ処理+メール送信
その他
  • flash動的生成
  • OpenSocial APIラッパー

すこしやりづらかったこと

  • 多次元配列のタイピング
    • $user['User']['id'] のuserの二度手間さ*1
  • bakeで生成されるPHP4対応のコード
  • Viewへの値の受け渡し
    • $this->set(compact('var_a', 'var_b')); ってやってるけど
    • Controllerのプロパティをそのまま展開してほしい気もする

なんとかしたいこと

  • URL設計
    • Model単位かユースケース単位か
  • Fat Controller
    • 複数のModelとやりとりしながら進めるロジックとか
    • ModelとControllerをつなぐためのクラスの切り出し方
  • パラメータの取捨選択
    • カスタムrouteパラメータ/passパラメータ/Namedパラメータ/クエリストリング
    • Namedパラメータは使わずにクエリストリングにした
      • PaginatorHelperを修正する必要があった

よかったこと

  • ルールに乗っかってコードに統一感ができた
  • 強力なO/Rマッピングで楽できた
  • 豊富なドキュメントに助けられた
    • 公式マニュアル/ブログ/SlideShare/CakePHP辞典

なんとかしたいことでいい案あったりする方は教えていただけますと助かります><


Pocket詳解 CakePHP辞典
Pocket詳解 CakePHP辞典


【関連記事】
ソーシャルアプリ CakePHP調査メモ - 130単位
mixiアプリ リリースまでの開発振り返り - 130単位

*1:アソシエーションあるから仕方ないかもですが