Posts Tagged ‘cakephp’

Windows+Eclipseで(ゴテゴテした)快適なCake開発環境を作る(CakePHP Advent Calendar 2011 11日目)

日曜日, 12月 11th, 2011

@mon_satさんからバトンを受け取ってのAdvent Calendar2011 11日目担当EG(@EGMC)です。
初参加で地味ネタですが、開発環境の話ってあまり聞かないのでとりあえず自分なりに育てた開発環境の話をしてみます。

※注 この前置きは長いのでさっさと本題に入りたい方は「開発環境構築編(ここから本編)」へどうぞ

ネタはWindows+EclipsePDT+その他tools。
昨今、勉強会に行くと皆MacBook Airなどを片手に、華麗にターミナルを開いてVimやEmacsで優雅にコーディングしている光景を目に致します。
そんな光景を横目に見つつ、しかしThinkpadでWindowsを起動してEclipseでもっさり開発している人たちも一定数いるはずだ!と信じてこのエントリーを書く次第です。いるかなあ。いるといいなあ。
前提として環境は

・開発サーバー(物理でもVPSでも)を外部(VMとかではなく)立てていて、DBは同サーバー(または同一ネットワーク内)にある
・ローカル環境はWindows+EclipsePDTで構築しており、プロジェクトは作成済み
・ソース編集はローカルで行うが、DBの操作やデバッグ(ログのtailとか)はリモートで行う
・ローカルのWindows環境にもphpがインストールされてる(bake用)

とします。
それ以前の情報は検索すればすぐ当たると思うので他に譲ります。
なお、cake1.3系で書きますが2.0系でも基本的に同じような事が出来ます。
環境設定上examle.netとかにしている部分は適宜ご自分の環境に置き換えてくださいませ。

なぜWindowsなのか?

・プラットフォームに依存したエディタとか、使い慣れたツールがたくさんあるからです。例えば私はHTMLエディタにCrecsent EVEを使ってます。

なぜEclipseなのか?

・元々自分がJava開発をメインでやってたのでその流れて使い始め、慣れていたというのが大きい
・起動の重さとかは色々あるものの、強力な補完機能(コード、Docコメント)、コードへのジャンプ、バージョン管理連携、プラグイン拡張など現実的な機能が多い
・最悪外部エディタが起動出来たり結構なんとかなる
・他の環境(Andoroid開発環境とか)と同居出来る
・起動の遅さについてはRAMDISKなどを使ってある程度は改善出来る

なぜ外にサーバーを立てるのか?

・自分の場合、会社と家で使うPCが違ったりするのでローカルに環境作ると何かと面倒くさい
・外部から叩かれる(例えばFacebookのプラグイン入れたりとか)ような機能を追加したりする
・他の人と環境を共有したりする
・↑みたいなことが無ければ全然VMでいいかなーと思ってます

使うツール群

Eclipse PDT(IDE)
WinSCP(ファイル転送)
PortFowarder(ポート転送)

開発環境構築編(ここから本編)

1)ファイルの自動転送

今回の構成では開発環境はリモートにあるのですが、ソースの反映は書き換えたらリアルタイムで行いたいですよね。
というわけで変更を即座に反映するよう自動転送します。
WinSCPの同期機能を使います。

ローカルはEclipseのプロジェクトフォルダを指定してパスを合わせます。
これで監視下のフォルダで変更があると即座にリモートへ転送してくれます。
無視したいファイル(.svnなど)はこんな感じで除外リストに入れておきましょう。

この状態でセッション保存しておけば、起動→接続→CTRL+Uですぐ同期をスタート出来ます。

2)Eclipseの自動補完を効かせる

なぜEclipseなのか?の所で補完機能を使いたいからだよ!と書きましたが、フレームワーク故にというかCakeはあまりモデルそのものをnewしたりしないので、そのままではほとんど補完が効きません(モデル、コンポーネント、etc・・)。
というわけで補完を効かせるようにコードをいじります。
正直、IDEの補完機能のために不要なコードを足すのはどうなの?という思いもありますが開発効率や動作速度を天秤にかけてこの方法を採用しています。

app_controller.phpに

/**
* Userモデル
*
* @var User
*/
var $User;

/**
* セッションコンポーネント
*
* @var SessionComponent
*/
var $Session;

/**
* authコンポーネント
*
* @var AuthComponent
*/
var $Auth;

/**
* セキュリティコンポーネント
*
* @var AuthComponent
*/
var $Security;

のように書いてどのクラスかを教えてあげます。

これで補完が効くようになります。

3)ローカルからbakeを使う

cakeといえばbakeですよね。
モデルやコントローラの生成が楽に行えますが、リモートで生成したファイルをいちいち持ってくるのは面倒です。
ローカルで(かつIDE内で)作ります。

bakeは当然phpが必要なのでローカルのWindowsマシンにphpを入れます。
bake.batはphpにパスが通ってる前提なのでパスを通します。

さて、これでbakeは実行出来るのですが、モデルの情報を取得するのにDBに接続しなければなりません。
ローカルにDB作るという方法もありますが、いちいち二重化させるのは面倒なのでリモートのDBに繋ぎたいです。
ポート転送を使いましょう。
Teratermのポート転送機能などもありますが、今回はPortForwarderを使います。

config.txtはこんな感じで

Host dev.example.net
HostName dev.example.net
# hostname or IP address

User hoge
# username on server. NEEDED

LocalForward 13306 localhost:3306
# localforward info1

Compression yes
# if you need compression

IdentityFile dev.example.net-key.rsa

13306ポートをリモートサーバーの3306に転送してます。
これでPortForwarderを立ち上げてDBの接続先を13306にすれば繋がります。
開発環境と、ローカルで接続情報が異なる場合は実行パスの判定などを行って接続先を切り替えます。

core.phpで切り替える場合は例えばこんな感じで

if (__FILE__ === 'C:\path_to_workspace\project\app\core.php') {
// ローカルでbakeするための設定
    define( '_DB_HOST' , '127.0.0.1:13306' );
    define( '_DB_NAME' , 'projectname' );
    define( '_DB_USER' , 'user' );
    define( '_DB_PASS' , 'pass' );
} else {
    // サーバーで動かすための設定
    define( '_DB_HOST' , '127.0.0.1:3306' );
    define( '_DB_NAME' , 'projectname' );
    define( '_DB_USER' , 'user' );
    define( '_DB_PASS' , 'pass' );
}

※ちなみにCake2.x系の場合はPORTは別指定になりますね

bake用の実行構成を実行→外部ツールから作成します。

これでIDEからbakeが実行出来ます。

ファイルの生成を行った後、F5でファイルのリフレッシュを行うと反映されますね。

ここまでやると晴れて快適に補完も効き、bakeも使え、ソースも同期される開発環境が構築出来ます。

ちなみにここまでの設定をすべて行って必要な諸々を起動すると画面はこんな感じになります。

・・・・まあ非常にWindowsらしい感じですね、うんうん。
立ち上げているものも多く、そもそもサーバー側で作業すれば楽なことも多々ありますが、まあこれはこれで慣れればいいものですよー。
Windowsで開発をされている方のお役に立てれば幸いです。
あと、こんな面倒なことしなくてもこんなんありまっせ!みたいな情報もあれば是非twitterなどで突っ込んで頂ければと思います。

そんなこんなで初参加のCakePHP Advent Calendar 2011でした。明日は@slumbers99さんです!


CakePHPのShell内部でemailビューをレンダリングしてQdmailでメールを送る

木曜日, 11月 17th, 2011

タイトルまま。
我ながらこれは悪手だと思うがとりあえずこの方法で実現は出来ましたので公開。
こんなやり方もあるよ!とかあれば是非twitterなどでツッコミくださいませ。
ちなみにビューはSmartyプラグインを使っていますがこの場合基本ビューはなんでもよく、ヘルパーも普通に使えると思います(多分)_。

環境

PHP5.3.6
CakePHP1.3系
Qdmail1.2.6b

そもそもの問題点

cakeのというか、Qdmailの仕様で、ビューのemailエレメントを使ってメール本文を構築する際に、コントローラーに値をセットして渡す必要がある。
コントローラーからメール送信する場合はそれでよいが、シェルなどから(例えばユーザーに対しての一斉送信などで)メールを送る場合はコントローラーが存在しないため値の受け渡しが出来ないので考えた結果コントローラをでっちあげる事にしたという話。

サンプル

<?php

App::import('Core', 'Controller');
App::import('Component', 'Qdmail');
App::import('Core', 'Router');

class HogeShell extends Shell {

    var $uses = array('User');

    function startup() {

    }

    function main() {

        // qdmail
        $this->Qdmail = new QdmailComponent();
        // テンプレートに入れ込むためのデータ
        $userdata = $this->User->read(null, 100);

        // Qdmailコンポーネントに食わせるためのダミーコントローラーを生成
        App::import("Controller", "App");
        $dummycontroller = new AppController();
        // 値をセット
        $dummycontroller->set("user", $userdata);
        // startupを自分で呼んでダミーコントローラーを渡す
        $this->Qdmail->startup($dummycontroller);

        $this->Qdmail->to($userdata['User']['email']);
        $this->Qdmail->subject('シェルからのメール送信');
        $this->Qdmail->from('test@example.com');
        // emailエレメント使う
        $this->Qdmail->cakeText( null ,'users.notice');
        $this->Qdmail->send();
    }
}

これで生成したAppコントローラーを経由して値の受け渡しが出来、送信時にviews以下のテンプレートを使用することが出来る。
コントローラーはもちろんダミーで無駄な生成コストがかかってるがとりあえずやりたい内容は実現出来た。

そもそもの話というか不満点

emailの送信はコントローラーだけでなく色々なレイヤーで行われるので、コントローラーに依存したくない。画面出力に使わない値をメール送信のためだけのコントローラーにバインドするというのもちょっと気持ち悪い。
(まとめて配列でパラメータを渡すとかでいいと思う)

いずれにせよこれはあまり筋の良い解決法とは思われないので、何か良い方法を考えたい・・。


CakePHP2.0.2で意地でもTwigを使ってみる

火曜日, 11月 15th, 2011

CakePHPの2.0系が最近安定版リリースされましたね。
まだまだ情報は少ないですが、折角なので新規の開発は1.3系ではなく2系でやりたい!と思うこの頃です。

さて、CakeといえばテンプレートエンジンはSmartyを使う人が多い印象ですが私は圧倒的にTwigが好きです。然り、Twigが好きなわけです。

CakeでTwigを利用するプラグインはこんな感じでKjell Bublitz氏が作成されているわけですが、早くも2系に実験的ながら対応しました的なことが書いてあるのでこれは!ということで導入してみました。
2系の仕様がよくわかってなかったりドキュメントがまだアレだったりと色々ありましたがとりあえず動かせてます。

環境:
CakePHP2.0.2
Twig1.3.0(stable)
cakephp-twig-view(commit a5fc2e6291)

導入:
1)基本的にInstallationに書いてある通りのフォルダ階層で突っ込みます。

こんな感じで、Plugin直下にtwig_viewを配置。
Twig本体はvendorsの下に配置。

2)初期設定諸々

app/Config/bootstrap.phpあたりに下記コードを追加してプラグイン有効化

CakePlugin::load('TwigView');

app/Controller/AppController.phpにてviewClassの変更
(2系は$viewではないっぽい)

class AppController extends Controller {

    public $viewClass = "TwigView.Twig";
}

3)app/Plugin/views/twig.phpの編集
そのままだと動かなかったのでパスまわりの修正をする。
・Twigオートローダーの読み込み

// Load Twig Lib and start auto loader
App::import('Vendor', 'TwigView.TwigAutoloader', array(
'file' => 'Twig'.DS.'lib'.DS.'Twig'.DS.'Autoloader.php'
));
include_once __DIR__ . '/../vendors/' . 'Twig'.DS.'lib'.DS.'Twig'.DS.'Autoloader.php';

・・App::importがなぜかうまく読み込めてなかったので無理矢理include・・App::importの仕様がようわからんです。

・2系用にパス周りの修正とautoescape
まとめてこんな感じに書き換えちゃいました。
autoescapeは後述しますがちょっと問題があるのでお好みで。私はonにしました。

    function __construct(&$controller, $register = true) {

        parent::__construct($controller, $register);

        if($this->isCake2()) {

            // just collecting for str_replace
            $this->templatePaths = array(
                APP.'View',
                ROOT.DS.'cake'.DS.'Lib'.DS.'View'
            );
            $loader = new Twig_Loader_Filesystem(APP.'View');
        } else {
            $this->templatePaths = array(
                APP.'views',
                ROOT.DS.'cake'.DS.'libs'.DS.'view'
            );

            // we always look in APP, this includes error templates.
            $loader = new Twig_Loader_Filesystem(APP.'views');
        }
        // setup twig and go.
        $this->Twig = new Twig_Environment($loader, array(
            'cache' => TWIG_VIEW_CACHE,
            'charset' => strtolower(Configure::read('App.encoding')),
            'auto_reload' => (bool) Configure::read('debug'),
            //'autoescape' => false
            'autoescape' => true
        ));;

        // overwrite some stuff
        $this->Twig->addExtension(new CoreExtension);

        // activate |trans filter
        $this->Twig->addExtension(new Twig_Extension_I18n);

        // activate |ago filter
        $this->Twig->addExtension(new Twig_Extension_TimeAgo);

        // activate basic filter
        $this->Twig->addExtension(new Twig_Extension_Basic);

        // activate number filters
        $this->Twig->addExtension(new Twig_Extension_Number);

        if (isset($controller->theme))
            $this->theme =& $controller->theme;

        $this->ext = '.tpl';
    }

}

$this->isCake2()を使ってざっくりパスの切替をしています。
$this->isCake2は親で設定されるプロパティを使って判定しているので、親のコンストラクタの後に諸々の処理を移動しています。

4)使ってみる

コントローラーでこんな値を設定してみて


public function index() {

    $array_vars = array("foo" => "<bar>","hoge" => "<hogehoge>" );

    $this->set("hello", "<hello! It's a beautiful Twig World!!>");
    $this->set("array_vars", $array_vars);

}

出力してみます。ビューファイルの拡張子は.tplで(変更可能)

<div>
変数:
     {{hello}}
</div>
<div>
連想配列:
<ul id="sortable_list">
{% for k,v in array_vars %}
    <li> {{ k }} , {{ v }}</li>
{% endfor %}
</ul>
</div>
<div>
ヘルパー:
    {{form.create()|raw}}
    {{form.input('input')|raw}}
    {{
    form.input('email', {
        'label': 'Your E-Mail Address'| trans
        })
    |raw}}
    {{form.end()|raw}}
</div>

こんな感じに出力されます

<や’もエスケープされてますね。
オートエスケープをオンにするとヘルパーで生成したコードもエスケープされてしまうので|rawで出力しています。
ここらへんを考慮してデフォルトオフにしているんでしょうが、出来ればヘルパーで生成した場合は自動で生出力とかにしたい・・。

ざっくりまとめ

2.0対応の部分についてはまだドキュメントなども追いついてない部分がありますがとりあえず動かすことは出来ました。
ソースはすべてgithubで公開されているので自分もフィードバック送ったりしたいところですがgitの使い方がようわからん・・。使わないとなあ。

また、2系の仕様がイマイチよくわかってないのでTwigの読み込みがApp::importになってたりアヤシイ感じなのでここはこうじゃないだろ!というツッコミお待ちしております。


CakePHPで直前のクエリをチェック

日曜日, 11月 6th, 2011
App::import('Model', 'ConnectionManager');
$db = ConnectionManager::getDataSource('default');
$this->log($db->getLog(), LOG_DEBUG);

こんな感じで。

thanks →@deeeki