アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

Symfonyの歩き方/5分でわかるSymfony Best Practices

この記事はSymfony Advent Calendar 2015 10日目の記事です。前日の記事はtsuboさんの「簡単デバッグ」でした。




こんにちは。宇都宮です。今日は、社内勉強会で使用した、Symfony入門者向けの資料を共有します。

Symfonyの歩き方





Symfonyの学習方法について、特に公式ドキュメントの読み方に焦点を当てています。

Symfonyの公式ドキュメントは、Symfonyの学習リソースとして第一級の資料ですが、量が多く、初心者のうちはどのドキュメントから読めばいいか分からないと思います。膨大な公式ドキュメントのうち、どの文書を優先的に読めばいいのかわかれば、効率的に学習していくことができます。

5分でわかるSymfony Best Practices





Symfony公式ドキュメントの中で、The Symfony Bookと並んで重要なのがSymfony Best Practicesです。Symfony Best Practicesは、Symfonyを使う上でのベストプラクティスが書かれた文書です。

Symfony にはレールが無い」といわれることがあります。Symfonyを使う開発者は、他のフレームワークを使う場合に比べて、設計を自分で考える必要のある場面が多くなります。

Symfony Best Practicesは、Symfony Wayを強制するものではありませんが、Symfonyらしい設計・実装方法のヒントになります。

このスライドでは、Symfony Best Practicesのポイントを抜き出しています。短時間で要点を押さえたい方におすすめです。

おわりに



Symfonyは覚えることが多いですが、Advent Calendarのような形で日本語の情報が共有されると非常に助かりますね!

明日は @qckanemoto さんです!

symfonyのDoubleList実装用ウィジェットを使ってみる

こんにちは。松田です。

今日はsymfonyで簡単にDoubleListのフォームを実装する、sfWidgetFormSelectDoubleListを使ってみます。
DoubleListとは、左右に表示されるセレクトボックス間で要素を移動させることで必要な要素だけを絞り込むための仕組みです。
symfonyにこれをまるごと作成できるプラグインが用意されているため、これを使用して作成します。

これを使えば下の画像のようなフォームを一瞬で作成してくれます。


DoubleListを作成するには、sfFormExtraPluginを導入する必要があります。
下記のURLからダウンロードし、プロジェクト直下のpluginsディレクトリに設置しておきます。
http://www.symfony-project.orstsg/plugins/sfFormExtraPlugin

次にプラグインを有効にするため、config/ProjectConfiguration.class.php を編集します。

// config/ProjectConfiguration.class.php


<?php

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    $this->enablePlugins('sfDoctrinePlugin');
    $this->enablePlugins('sfFormExtraPlugin'); // これを追加
  }

・・・

これで下準備は完了です。



次に、DoubleListを使用するためのFormを用意します。
DoubleListを使用するには、sfWidgetFormDoctrineChoiceの"renderer_class"オプションに sfWidgetFormSelectDoubleList を指定します。
modelには表示したいテーブルのモデル名を記述してください。

// TestClassForm.class.php


<?php 

class TestForm extends sfForm
{

  public function configure()
  {
    $this->widgetSchema['staff_list'] = new sfWidgetFormDoctrineChoice(array(
      'model'            => 'Staff', // リストに表示するモデルの名前
      'renderer_class'   => 'sfWidgetFormSelectDoubleList',
    )); 

  }
}



このFormをactionで呼び出し、テンプレートで描画します。
// actions.class.php


<?php
(略)
  public function executeDoubleList(sfWebRequest $request)
  {
    $this->form = new TestForm();
  }

// doubleListSuccess.php


<div style="margin: 100px 100px 100px 100px;">
  <form>
    <?php echo $form['staff_list'] ?>
  </form>
</div>


これで描画は出来るのですが、sfWidgetFormSelectDoubleListを使用するには専用のJavaScriptを読み込んでおく必要があります。
sfWidgetクラスを継承しているwidgetには、getJavascripts()メソッドが実装されており、これを使用するとそれぞれのwidgetFormで必要なjavascriptを知ることができるようになっています。

さらに、use_javascripts_for_form ヘルパーを使えば、formのgetJavascripts()で取得できるjsをすべて読み込んでくれるので、これを使ってしまいましょう。

// doubleListSuccess.php


<?php use_javascripts_for_form($form) ?>  

<div style="margin: 100px 100px 100px 100px;">
  <form>
    <?php echo $form['staff_list'] ?>
  </form>
</div>


sfWidgetFormSelectDoubleListでは、"/sfFormExtraPlugin/js/double_list.js" が必要となっています。
実際には /plugins/sfFormExtraPlugin/web/js に存在するファイルとなるはずなので、web以下にsfFormExtraPluginへのシンボリックリンクを貼るなどして対応してください。

これで設置完了です。
表示すると画像の状態になっているはずです。


セレクトボックス内の要素を選択し中央の矢印をクリックすると、選択した要素を左右に移動させることができるようになっています。




表示するだけであればこれだけで完成ですが、せっかくなので少しカスタマイズしてみます。
基本的にはsfWidgetFormDoctrineChoiceに渡すオプションを変更することでほとんどが対応可能です。

ぱっと見あまり変わってませんが、最終的に次の画像のような状態を目指します。



■候補リストに表示されるオブジェクトをクエリで選択する
今のままではStaffが全員表示されてしまうので、表示したい人だけ表示するように変更します。
具体的にはDoctrine_Queryを渡すことでクエリの結果のStaffだけを表示することができます。

例えば、下記のように記述すれば、TestFormのコンストラクタで渡したユーザーを除外してリストを表示できます。


<?php
(略)
  $query = StaffTable::getInstance()->createQuery();
  $default_id_list = $this->getDefaults();
  if (count($default_id_list)) {
    $query = $query->andWhereNotIn('id', $default_id_list);
  }

  $this->widgetSchema['staff_list'] = new sfWidgetFormDoctrineChoice(array(
    'model'            => 'Staff',
    'query'            => $query,  // これを追加
    'renderer_class'   => 'sfWidgetFormSelectDoubleList',
  ));




■リスト上部の文字列を変更する
renderer_optionsに "label_unassociated" "label_associated" を渡す。



<?php
(略)
$this->widgetSchema['staff_list'] = new sfWidgetFormDoctrineChoice(array(
  'model'            => 'Staff',
  'renderer_class'   => 'sfWidgetFormSelectDoubleList',
  'renderer_options' => array(
    'label_unassociated' => '【候補一覧】',              // これと
    'label_associated'   => '【選択しているスタッフ】',  // これ追加
  ),
));




■リストを左右入れ替える
初期状態では左が選択済みリスト、右が候補リストになっていますがこれを逆にします。
renderer_optionsの"associated_first"にfalseを渡すことで可能です。


<?php
(略)
$this->widgetSchema['staff_list'] = new sfWidgetFormDoctrineChoice(array(
  'model'            => 'Staff',
  'renderer_class'   => 'sfWidgetFormSelectDoubleList',
  'renderer_options' => array(
    'associated_first' => false,  // これ
    'label_unassociated' => '【候補一覧】',
    'label_associated'   => '【選択しているスタッフ】',
  ),
));




■中央の矢印ボタンをボタンタグに変える
renderer_optionsの associate, unassociateにタグを直接記入します。


<?php
(略)
$this->widgetSchema['staff_list'] = new sfWidgetFormDoctrineChoice(array(
  'model'            => 'Staff',
  'renderer_class'   => 'sfWidgetFormSelectDoubleList',
  'renderer_options' => array(
    'associated_first' => false,
    'label_unassociated' => '【候補一覧】',
    'label_associated'   => '【選択しているスタッフ】',
    'associate'   => '<input type="button" value="追加する → " />',  // これと
    'unassociate' => '<input type="button" value=" ← 外す "  />',  // これね
  ),
));




■リストをTableタグで描画する
このDoubleList、divタグとfloatを駆使してデザインされているため、IE6ではしっかりと崩れます。
どーーしてもIE6に対応したい!しなきゃいけないんだよちくしょう!という人は、tableタグで無理やり対応しちゃいましょう。
renderer_optionsの template オプションを使用して実装します。


<?php
(略)
$this->widgetSchema['staff_list'] = new sfWidgetFormDoctrineChoice(array(
  'model'            => 'Staff',
  'renderer_class'   => 'sfWidgetFormSelectDoubleList',
  'renderer_options' => array(
    'associated_first' => false, // template指定すると無意味。。
    'label_unassociated' => '【候補一覧】',
    'label_associated'   => '【選択しているスタッフ】',
    'associate'   => '<input type="button" value="追加する → " />',
    'unassociate' => '<input type="button" value=" ← 外す "  />',
// ↓ここから
    'template' => <<<STR
<table width="50%" border="0" cellpadding="0" cellspacing="0">
  <tr valign="top">
    <td width="200" align="center" style="white-space:nowrap;">%label_unassociated%</td>
    <td align="center"> &nbsp;</td>
    <td width="200" align="center" style="white-space:nowrap;">%label_associated%</td>
  </tr>
  <tr>
    <td align="center" valign="top">%unassociated%</td>
    <td align="center">
      %associate%
      
      
      
      %unassociate%
    </td>
    <td align="center" valign="top">
      %associated%
    </td>
  </tr>
</table>
<script type="text/javascript">
  sfDoubleList.init(document.getElementById('%id%'), '%class_select%');
</script>
STR
// ↑ここまで追加っ
  ),
));


%から%で囲まれた文字列が必要な要素に置き換えられます。
何が何に置き換えられるかはsfWidgetFormSelectDoubleListのソースを読んでみましょう。



■初期状態で選択されている要素を設定する
編集画面等、すでに設定されている要素を選択済リストに入れて表示させたいことがあります。
オプションで設定できてもいいような気もするのですが、やり方がわからなかったのでjsで作ってみました。
長くなってきたので省略しつつ書いて行きます。

まずはセレクトボックスにclassを指定し、jsから指定できるようにしておきます。
これで候補リストのセレクトボックスのclassが「staff-list」、選択済みリストのclassが「staff-list-selected」となります。

// TestForm.class.php


<?php
(略)
  public function configure()
  {
    $this->widgetSchema['staff_list'] = new sfWidgetFormDoctrineChoice(array(
      'model'            => 'Staff',
      'renderer_class'   => 'sfWidgetFormSelectDoubleList',
      'renderer_options' => array(
       'class_select' => 'staff-list', // jsから指定するためのclassを追加
(略)
  }

  // use_javascripts_for_formで読み込まれるjsファイルを追加
  public function getJavaScripts()
  {
    $javascripts = parent::getJavaScripts();
    $javascripts = array_merge($javascripts, array(
      'jquery.js',
      'show_default.js.php?id_list=' . $this->id_list, // ここはうまいこと実装してね
    ));  
    return $javascripts;
  }
}


次に、jqueryファイルと下記のjsファイルを読み込ませます。
上記のようにgetJavaScripts()を実装することで勝手に読み込まれるファイルが増えるはずです。
あとはshow_default.js.phpに初期表示のidリストを渡し、なんやかんやしてshow_default.js.phpのdefault_id_listを作成してください。

// show_default.js.php jsファイルをphpで出力するスクリプト


<?php
// $_GETでうけとったデータからIDの配列を作る
$default_id_list = 略
?>

$(function() {
  var default_id_list = <?php echo $default_id_list ?>;
  if (default_id_list.length) {
    $.getJSON(
      "/staff/get_default_json",  // 初期表示のスタッフをJSON形式で出力するURL
      { default_id_list: default_id_list },
      showSelectboxByJson
    );
  }
});

// 受け取ったjsonデータからoption要素を作成
// [{"id": "1", "name": "なまえ"}, ... ] ←こんなJSONデータを想定
function showSelectboxByJson(json)
{
  var options = '';
  var list = json.list ? json.list : [];
  for (var i = 0; i < list.length; i++) {
    options += '<option value="' + list[i].id + '">' + list[i].name + '</option>';
  }

  $(".staff-list-selected").html(options);
}

これで/staff/get_default_json部分もちゃんと作られていれば、初期選択されている画面が作成できるはず。




基本実装はとっても簡単なので、ぜひ機会があれば使ってみてください。

symfony + PHPスクリプト暗号化(ionCube)

こんにちは井川です。今回は、symfonyに組み込んだPHPスクリプトを暗号化した際に。アプリケーションを正常に動作させる簡単な方法をご紹介します。この方法を使うことで、実際にionCubeでPHPスクリプトを暗号化しても正常に動作します(その他のエンコーダでも同様だと思います)。

symfonyは、libディレクトリ、apps/アプリ/libディレクトリ、apps/アプリ/module/*/lib以下のPHPファイルとクラスの対応関係を自動的に生成し、cacheディレクトリ以下にキャッシュファイルとして保存します(cache/アプリ/環境/config_autoload.yml.php)。そして次回からは、そのキャッシュファイルを用いてautoloadを実行します。このキャッシュファイル生成方法はいたって簡単です。上記libディレクトリ内の全PHPスクリプト(lib/vendor/symfonyなどは除く)を文字列として読み込み、その文字列からクラス名を抽出します。デフォルトでは、sfAutoloadConfigHandlerクラスがこの役割を担っており、parse()メソッド内部で上記の処理を行います。

PHPスクリプトの暗号化を使うと、キャッシュファイル生成時に各PHPファイルを文字列として読み込んでも、クラス名を抽出できません。このため、symfony実行時にエラーが発生します。

そこで今回はアプリケーションが正常に動作するように、上記キャッシュファイルを生成するクラスを新たに作ります。そして、symfonyで使われるように設定します。方法はとても簡単です。

まず、PHPファイルへのクラス定義時に以下の2つの制約を設けます。

①1ファイルに1クラス
PHPファイル名は「クラス名.class.php

そして、新しいクラスでは、ファイル名からそのファイル内に記述されているクラス名を取得し、クラス名とPHPファイルの絶対パスの関係を取り出します(config_autoload.yml.phpに記述)。

実際に、デフォルトでクラス名とファイルパスの関係を抽出しているのは、sfAutoloadConfigHandlerクラスの113~115行目です。



<?php
  ...
  foreach ($finder->in($matches) as $file)
  {
    $mapping = array_merge($mapping, $this->parseFile($path, $file, isset($entry['prefix']) ? $entry['prefix'] : ''));
  }
  ...


そこで、sfAutoloadConfigHandlerクラスを継承したTestAutoloadConfigHandlerクラスを次のように実装します。



<?php
class TestAutoloadConfigHandler extends  sfAutoloadConfigHandler
{
  protected function parse(array $configFiles)
  {
    ...
      foreach ($finder->in($matches) as $file)
      {
        $file_name = basename($file);
        
        if (preg_match('/^(.+)\.class\.php$/i', $file_name, $m))
        {
          $mapping[strtolower($m[1])] = $file;
        }
      }
    ...
  }
}


このクラスを使用するために、configディレクトリにconfig_handlers.ymlを設置します。内容は以下の通りです。



config/autoload.yml:
  class:    TestAutoloadConfigHandler


さらに、config/ProjectConfiguration.class.phpの先頭部分で、先のハンドラクラスを読み込みます。



<?php
require_once dirname(__FILE__).'/../lib/vendor/symfony/lib/autoload/sfCoreAutoload.class.php';
sfCoreAutoload::register();
require_once dirname(__FILE__) . '/../lib/config/TestAutoloadConfigHandler.class.php';

....


後は、暗号化を行い、暗号化スクリプトを使用する環境でsymfony ccを実行します。これで暗号化スクリプトsymfonyを使用できます。

ここでご紹介した方法は簡易的なものです。他のライブラリで、1つのファイルに複数のクラスを記述しているものや、ファイル名とクラス名が一致しないものを使う場合には、TestAutoloadConfigHandlerクラスを適切に書き直さなければなりません。必要に応じて試してみて下さい。

symfonyとHTMLファイルを同居させた際のリンク切れ回避方法

井川です。こんにちは。

Webシステム構築をしていると、動的なシステムと静的なHTMLファイル(ドキュメントルート以下)を同時に配置することがあります。symfonyを使ってこのようなシステムやサイトを構築すると、HTMLファイルへの特定のリンクでは404エラーが発生してしまいます。今回はこれを回避する簡単な方法をご紹介します。

それらのファイルへアクセスでき、簡単に同居できます。しかしながら、リンクの書き方如何によっては、問題が発生します。例えば、/outline/index.htmlへリンクを貼りたい場合(webディレクトリ以下に配置)、次のようなリンクはsymfonyのモジュールとindex.htmlのどちらを意味するのでしょうか。



<a href="/outline">…</a>
<a href="/outline/">…</a>


リンク先でリクエストはsymfonyに渡され、symfonyではこれらのリンク先をモジュールと解釈します。そのため、仮に/outline/index.htmlが存在していても、404エラーをはいてしまいます。こういったリンクが膨大な場合や、新しいテンプレートファイルでリンク先を書き間違えた場合など、問題が発生する可能性は決して低くありません。

この問題を回避する方法の1つは、以下のようにフロントコントローラを作成することです(他の方法も多々あるとは思います)。このコントローラではsfFrontWebControllerを継承したクラスにcheckHtmlUriメソッドを加え、dispatchメソッドを上書きしています。

dispatchメソッドの中の変更点は、sfExceptionをキャッチしたブロック内部です。ここでは次のような処理を実行しています。

① 例外がsfError404Exceptionであるか確認する
② リクエストURLの最後がディレクトリである可能性を確認する
③ リクエストURL+index.htmlにファイルがあるか確認する
④ リクエストURL+index.htmlにリダイレクトする

checkHtmlUriメソッドでは②と③の処理を行い、リダイレクト先URLを設定します。dispatchメソッドでは、まず①の実行とcheckHtmlUriメソッドの呼び出しを行います。そして最終的に④のリダイレクトを行います。



<?php
/**
 * フロントウェブコントローラ
 * 
 * @package    blog
 * @subpackage controller
 * @author     Kazushi IGAWA <kazushi@asial.co.jp>
 * @date       2011.02.15
 */
class TestFrontWebController extends sfFrontWebController
{
  /**
   * インデクスファイル名
   */
  const INDEX_FILE_NAME = 'index.html';
  /**
   * リダイレクト先
   * @var string
   */
  private $redirectUrl = '';
  /**
   * リクエストを処理する
   *
   * @param  void
   * @return void
   */
  public function dispatch()
  {
    try {
      // reinitialize filters (needed for unit and functional tests)
      sfFilter::$filterCalled = array();
      
      // determine our module and action
      $request    = $this->context->getRequest();
      $moduleName = $request->getParameter('module');
      $actionName = $request->getParameter('action');
      
      if (empty($moduleName) || empty($actionName)) {
        throw new sfError404Exception(sprintf('Empty module and/or action after parsing the URL "%s" (%s/%s).', $request->getPathInfo(), $moduleName, $actionName));
      }
      
      // make the first request
      $this->forward($moduleName, $actionName);
    } catch (sfException $e) {
      if ($e instanceof sfError404Exception  & & $this->checkHtmlUri($request)) {
        $this->redirect($this->redirectUrl);
      }
      
      $e->printStackTrace();
    } catch (Exception $e) {
      sfException::createFromException($e)->printStackTrace();
    }
  }
  /**
   * HTMLファイルが存在するか確認する
   * 
   * @param  sfWebRequest $request
   * @return boolean true=静的ファイルあり, false=静的ファイルなし
   */
  protected function checkHtmlUri(sfWebRequest $request)
  {
    $uri  = $request->getUri();
    
    if ('/' !== substr($uri, -1)) {
      return false;
    }
    
    $filePath = sfConfig::get('sf_web_dir') . $request->getPathInfo() . self::INDEX_FILE_NAME;
    
    if (!file_exists($filePath)) {
      return false;
    }
    
    $this->redirectUrl = $uri . self::INDEX_FILE_NAME;
    return true;
  }
}


後は、factories.ymlでコントローラを設定し、上記コントローラを組み込めば、当初の問題であった"/outline/"のようなリンクでも"/outline/index.html"へ遷移します。もちろん、symfonyの従来の機能(404エラーなど)はそのまま使用可能です。



all:
  controller:
    class: TestFrontWebController


今回はコントローラのみを書き換える方針で作成しました。各クラスの役割を明確にするのであれば、checkHtmlUriメソッドやリダイレクト先はリクエストクラスに持たせた方がスッキリするかもしれません。

Doctrineのオブジェクト単位での悲観的ロックについて

皆さん、こんばんは。
笹亀です。

ついにiphone4の白を待ちきれずに黒を買いました。
いまさらながらiphoneの素晴らしさを体感しております。

さて本日はDoctrineのオブジェクト単位での悲観的ロックについて解説をさせていただきます。
Doctrineのトランザクション処理を行う際にConnection単位で行うのが一般的な方法ですが、あまり知られていませんが、Doctrineには通常のデータベースで行う方法とは別にオブジェクト単位でロックをかけることができます。
http://www.doctrine-project.org/documentation/manual/1_1/ja/component-overview#%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC%E3%82%92%E3%83%AD%E3%83%83%E3%82%AF%E3%81%99%E3%82%8B

今回は実際にコードを作成して試してみます。
テスト用に事前に作成したMemberオブジェクトに対してロックをかけます。

実装の確認をするためにActionは2つ用意します。
1.テーブルにロックをかける側のAction


  public function executeIndex(sfWebRequest $request)
  {
    echo 'Lock Start!';
    
    //Doctrineのコネクションを取得
    $conn = Doctrine_Manager::getInstance()->getCurrentConnection();

    //コネクションを渡し、ロックマネージャーを生成する
    $lockingManager = new Doctrine_Locking_Manager_Pessimistic($conn);

    //適当なMemberオブジェクトを取得
    $member = Doctrine::getTable('Member')->find(1);

    try {
      //ロックのタイムアウトを設定
      $lockingManager->releaseAgedLocks(300);

      //オブジェクトに対してロックする
      $getLock = $lockingManager->getLock($member, 'jwage');

      //ロックがかかったならばデータを更新
      if ($getLock) {
        echo 'Got Lock!!';
        $member->name  = 'testhoge';
        $member->email = 'sasa@asial.co.jp';
        $member->save();
        sleep(10);
      } else {
        echo 'Sorry, someone else is currently working on this record';
      }
      //ロックを解除する
      $lockingManager->releaseLock($member, 'jwage');

    //例外が発生
    } catch(Doctrine_Lock_Exception $e) {
      echo $e->getMessage();
      //ロックを解除する
      $lockingManager->releaseLock($member, 'jwage');
    }

    echo '';
    echo 'Lock Finished!';
    exit;
  }



2.テーブルロック中にアクセスする側のAction(Memberオブジェクトにアクセスしてデータを表示する


  public function executeTestLock()
  {
    echo 'Get Start';

    $member_obj = Doctrine::getTable('Member')->find(1);

    if ($member_obj) {
      print_r($member_obj->toArray());
    }
    echo '';
    echo 'Get Finished!';
    exit;
  }


実際にロックをかけてからオブジェクトに対してアクセスをして試してみます。

まずは、作成した1のロックする処理を実行します。


次にsleepしている間に2を実行します。



画像では伝えきれませんが、1の処理でロックが掛かっている間は2は実行されずに、1の処理が完了後に2が実行されデータが更新されます。



一般的な方法に比べてスクリプトのコストが低く利用しやすいのが特徴だと思います。オブジェクト単位でロックをかけて情報を操作したいときなどに利用してみてはいかがでしょうか。
※重要なデータを扱う場合はやはりちゃんとトランザクション処理をしてテーブルロックをかけて行う方が一般的ですので、オブジェクトでのロックを使用して実装する際には自己責任でお願い致します。

symfonyセミナー動画無料公開!

こんにちはアシアル・アピール担当の岡本雄樹です

最近、弊社の優秀なアルバイトが
私の席の後ろでsymfonyと格闘しているようです。

クラスの概念から勉強中なので苦労しているようですが、
親切な先輩社員が1から10まで教え込んでいるので、
将来が楽しみです。

symfonyと言えば、
先月開催した弊社テクニカルセミナー
PHP×symfony」編の動画を、
今回特別に公開する運びとなりました。

しかも、テクニカルセミナーにご参加いただいた
「ロジックデザイン」様からの御厚意により、
ただの動画ではなく、スライドと講師を同時に閲覧できる
Eラーニング形式で動画を公開することができました。

この場を借りて、お礼申し上げます。

動画は下記リンクを辿り、
「特別公開中」ボタンをクリックして下さい。
アシアル・テクニカルセミナー


尚、アシアルでは、現在、全5回にわけて
「開発者向け、一歩先を行くためのテクニカルセミナー」
を開催しています。

8月25日には「PHP×Flex(前編)」と銘打ちまして、
ActionScript3.0でインタラクティブなWeb開発を行う、
ノウハウをセミナーでご提供致します。
皆様お誘い合わせのうえ、ご参加下さいませ。

お申し込みページへは、以下のバナーよりお進み下さい。


また、講演者の松田 惇のブログでも、
Flexネタで記事を書かせて頂いております。
宜しければご参照ください。

【開催概要】

日程: 8月25日(水) 10:00 ~ 12:00(※ 開始30分前より入室可能)
会場: 山王健保会館2F 多目的ホール
交通: 地下鉄銀座線・南北線溜池山王駅 7出口 徒歩3分
    地下鉄千代田線:赤坂駅 1出口 徒歩5分
    地下鉄銀座線・丸ノ内線赤坂見附駅出口 下車徒歩7分
主催: アシアル株式会社
受講対象者: Webシステムにおいて1~3年の開発経験のある方
定員: 100名(※ 定員となり次第、受付を終了させていただきます 。)
参加費: 無料(事前登録制)


【第3回 PHP×Flex(前編)】ActionScript3.0でインタラクティブなWeb開発

ActionScript3.0でインタラクティブなWeb開発

近年、様々なFlashコンテンツがWebサイトの要となっています。これらのFlashコンテンツの元となるのがActionScriptであります。最近ではFlexAIRなどのRIAアプリケーションにも使用され、ActionScriptの重要性はますます高まってきています。


この講演では、Flexアプリケーションの基礎となるActionScript3.0の基本構文から簡単なFlashの作成まで実際のスクリプトを追いながら解説していきます。


PHP×Flex編は前後編の2回構成です。
前編ではActionScript3.0の基礎を学び、後編ではFlexPHPを連携したFlexアプリケーションの作成方法を解説します。まずはこの前編でActionScript3.0の基礎をしっかりと学びましょう。



講演者:アシアル株式会社 松田 惇(マツダ アツシ)

アシアル株式会社のPHPプログラマーFlexプログラマー

JavaScriptAjaxを生かした、動的にインターフェースが変化するシステムが好きなことからFlashアプリケーションに興味を持ちはじめ、趣味でFlexを勉強するようになる。そのことがきっかけで、現在ではFlexアプリケーションの構築も担当している。

AJAX内でのリダイレクト処理の罠

こんばんは。笹亀です。

iPhoneの白をず〜〜っと待っているのですが、
今年末ということになってとても残念でなりません。
ここまで待ったので気長に待つことにします。

さて今日はAJAX内でのリダイレクト処理をした際に、
起こったことをご紹介させていただきます。

結論から言うと、
AJAX内でリダイレクトするときはセッションは使うな!」

まずは遷移として下記のように試しました。
AJAXSymfonyをfile_get_contentsで呼び出すスクリプトを指定します。


info.php
 ↓AJAXで呼び出し
get.php(外部のSymfonyで作成されたページの呼び出し)
 ↓file_get_contents関数で呼び出し
http://sasa.local/top/sessionTest
 ↓(Symfony内でリダイレクト
http://sasa.local/top/sessionTest1

※Symfonyのリダイレクト(redirect)を使用してセッションのデータを書き込んで値を引き継ぐことができるのかを試しました。


まずは表示する側のページを作成します。

-- info.php


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<title>symfony &ajax session Test</title>
<script language="JavaScript" type="text/javascript" src="/jquery.js"></script>


<!-- EXPAND SKIPED -->
<script type="text/javascript">
<!--
function ajax_test() {
  $.ajax( {
    type : "POST",
    url : "get.php",
    data : {param : "sasagame hiroshi"},
    success : function(txt) {
      $('#view_area').html(txt);
    },
    error : function() {
      alert('error');
    }
  });
}
// -->
</script>
</head>
<body>
symfonyとセッションのテスト中
<input type="button" name="test" onClick="ajax_test();return false;" value="テスト開始">

<div id="view_area">

</div>
</body>
</html>


次にAJAXの呼び出し側のPHPスクリプトを作成します。
-- get.php


<?php
  $html = file_get_contents('http://sasa.local/top/sessionTest?param=sasagame+hiroshi');
  echo $html;
?>

※ここではSymfonyで作成されたページを指定しております


get.phpで呼び出しているSymfony側の処理を実装します。



・・・省略
  public function executeSessionTest(sfWebRequest $request)
  {
    $param = $request->getParameter('param');
    $this->getUser()->setAttribute('session_test', $param);
    $this->redirect('top/sessionTest1');
  }

  public function executeSessionTest1(sfWebRequest $request)
  {
    $param = $request->getParameter('param');
    $session_data = $this->getUser()->getAttribute('session_test', '取得できない');
    echo 'session_test_data:' . $session_data . "";
    echo print_r($_SESSION, true);
    exit;
  }
・・・省略


上記のように実装完了後に実際に動かしてみます。


テスト開始ボタンをクリック・・

うあ、やっぱり取得できません。

普通にSymfony側を実行すれば、セッションデータの取得はおこなえます。


普通にAJAXで処理するときには、ご紹介させていただいたようなリダレクトして処理を遷移させて使うみたいなことはそうそうないことだとおもいます。
今回はイレギュラー的にどうしてもセッションを使ってリダイレクトすることになってしまいました。なぜだといろいろと試行したため時間を使いました。先入観で出来るだろうと思って使うと痛い目にあいますというか痛い目にあいました。

教訓としてはこんな遠まわしな使い方をするなということと先入観で使用するなでした^^;

皆さんもAJAX使うときには気をつけてくださいませ。

「PHP×symfony」 開発者向け、一歩先をいくためのテクニカルセミナー、第二弾の開催

アシアルでは、現在、全5回にわけて「開発者向け、一歩先を行くためのテクニカルセミナー」を開催しています。前回6月16日に開催した「PHP×セキュリティ」では当初予定の100名を超える、120名様のお申込をいただき、途中で定員枠を拡大することになりました。

セミナーで実施したアンケートでも、以下のようなコメントをいただいております。

------------------------------------------------------
・理解し易かった(サンプルコード、プログラマ基準で具体的なコード、脆弱性の解説とPHPの具体的な対策、例をあげながらの説明、セキュリティのポイントを網羅、ほどよい内容)
・見過していた問題や更に考慮すべき部分が明確になった
・参考URLがよかった
・具体的な内容で良かった(対策、攻撃手法)
PHPに特化した内容だった
・システムを構築するにあたり、考慮すべきことが明確になった(php初心者)
・セキュリティに対しての意識が向上
・短い時間で良くまとまっていた、全体像を把握できた、要点を学べた
・セキュリティに興味がわいた
PHPのセキュリティについて幅広く知ることができた
・改めて知識を深めることができた
・最新の情報を交えて話してもらえた、最新のトレンドを知ることができた
------------------------------------------------------

現在、第二回目「PHP×symfony:symfonyアジャイルWeb アプリケーション開発」について、申込を絶賛募集しております!!

お申し込みは、以下URLよりお願いします。
http://www.asial.co.jp/seminar/




【開催概要】

日程: 7月12日(月) 10:00 ~ 12:00(※ 開始30分前より入室可能)
会場: 山王健保会館2F 多目的ホール
交通: 地下鉄銀座線・南北線:溜池山王駅 7出口 徒歩3分
    地下鉄千代田線:赤坂駅 1出口 徒歩5分
    地下鉄銀座線・丸ノ内線:赤坂見附駅出口 下車徒歩7分
主催: アシアル株式会社
受講対象者: Webシステムにおいて1~3年の開発経験のある方
定員: 100名(※ 定員となり次第、受付を終了させていただきます 。)
参加費: 無料(事前登録制)


【第2回 PHP×symfony】symfony でアジャイルWeb アプリケーション開発

Web に限らず、アプリケーション開発において開発スピードは非常に重要な要素の1 つです。symfony※1は数あるPHP フレームワークの中でも特に機能が豊富で、アプリケーションのひな型の自動生成などアジャイル開発に必要な機能を多く備えています。

本セミナーではsymfony を活用したアプリケーション開発の流れを、その豊富な機能の紹介と合わせて、講演いたします。

※1 symfony はPHP 5.2.4 以降に対応したオープンソースのフレームワークで、世界規模で注目されているPHP フレームワークの1 つです。開発当初からアップデートが積極的に行われており、symfony のリード開発者であるFabien Potencier 氏が共同設立したフランスのSensio 社が、スポンサーとして継続的に開発をサポートすることを表明されており、安定的なフレームワークの一つと言えるでしょう。

講演者:アシアル株式会社 小川 雄大(オガワ カツヒロ)アシアル株式会社のアーキテクトで、symfonyのエバンジェリスト。symfonyを使ったアプリケーションの開発を行うだけでなく、symfonyの導入コンサルティングも行っている。
日本Symfony ユーザー会の設立者の一人でもあり、「もっと知りたいsymfony(More with symfony)」の日本語翻訳や日経ソフトウエアにsymfonyの記事を寄稿するなど、個人としても様々な活動を行っている。

symfonyでMongoDBを使ってみました

こんにちは。笹亀です。

最近でも無いですが、なにかと注目を浴び続けているのがkey-valueストアです。TokyoTyrant, kumofs, ROMA, Flareなど種類も豊富で使用する用途によって使い分けするのがよいです。そんな中で自分がご紹介したいのはMongoDBです。

MongoDBはドキュメント指向データベースでNoSQLです。ご紹介理由としてはRDBMSから入るkey-valueストアとしてとてもわかりやすいからです。後はサポート言語が大量にあってWEBアプリケーションとして使用するのにとても相性がよいことです。
 ※PHPPythonRubyPerlなどなど

その他にたくさんの特徴があります。詳細については、下記のドキュメントにてご確認ください。
http://www.mongodb.org/display/DOCSJP/Home
 ※日本人なら日本語のドキュメントに限ります(一部英語のままらしい)

まずはインストールしてみます。下記から自分の環境にあったものをダウンロードします。
http://www.mongodb.org/display/DOCS/Downloads
自分の場合はMacの10.6なので下記のように取得します


wget http://downloads.mongodb.org/osx/mongodb-osx-x86_64-1.4.1.tgz


解凍してコマンドなどを設置(コピー)します。


tar zxvf mongodb-osx-x86_64-1.4.1.tgz
cd mongodb-osx-x86_64-1.4.1
sudo cp bin/* /usr/bin/
sudo cp -r include/* /usr/include/
sudo cp lib/libmongoclient.a /usr/lib/


mongoデーモンをデータの格納先を指定して起動します。


mongod --dbpath ~/mongodb  &


PHPからMongoDBを使えるようにします。


pecl install mongo
php.iniにextensionの箇所にmongo.soを追加する
apacheを再起動。


phpinfoでMongoが設定が反映されているか確認しましょう。



以上でPHPでMongoDBが使えるところまでいけます。

symfony(PHP)で使用する方法を記載します。まずは接続情報を記載します。MongoDBはスケーラブルに対応しておりますので今回はMaster-Slaveの情報の両方を記載します

config/mongo/databases.yml


all:
  master:
    param:
      dsn:     localhost:27017
      dbname:   test
  slave:
    param:
      dsn:     localhost:27017
      dbname:   test


次にsymfonyでMongoへ登録、取得(件数のみ)するためのクラスを作成します。

lib/MongoKeyValue.class.php


<?php
class MongoKeyValue 
{
  protected $connection_master = null;
  protected $connection_slave = null;
  protected $table_name = null;
  
  /**
   * コンストラクト
   * 
   * @param string $table_name
   * @return MongoKeyValue
   */
  public function _construct($table_name = '') {
    $this->table_name = $table_name;
    
    //コネクション情報をセット(Master&Slave
    $file = sfConfig::get('sf_config_dir').'/mongo/databases.yml';
    if (file_exists($file)) {
      $config = sfYaml::load($file);
    } else {
      throw new InvalidArgumentException('not mongo databases.yml');
    }

    $getDb = function ($name) use ($config)
    {
      $param = $config['all'][$name]['param'];
      $mongo = new Mongo($param['dsn']);
      return $mongo->selectDB($param['dbname']);
    };
    
    //コネクションをセット
    $this->connection_master = $getDb('master');
    $this->connection_slave  = $getDb('slave');
  }

  public function getMasterCollection()  
  {
    return $this->connection_master->selectCollection($this->table_name);
  }
  public function getSlaveCollection()  
  {
    return $this->connection_slave->selectCollection($this->table_name);
  }

  /**
   * データインサート処理
   * 
   * @param array $params
   * @return boolean
   */
  public function setKeyValue($params) {
    if (is_array($params)) {
      $result = $this->getMasterCollection()->insert($params);
      return $result;
    } else {
      return false;
    }
  }

  /**
   * 条件を設定してデータ件数を取得する
   * 
   * @param array $params 取得条件
   * @return integer
   */
  public function getKeyValue($params) {
    if (is_array($params)) {
      return $this->getSlaveCollection()->count($params);
    } else {
      return false;
    }
  }
  
}



準備ができたら実際にactionに記載して登録処理と件数を取得するものを作成して確認してみましょう。


  public function executeTest() {
    $mongo = new MongoKeyValue('test_table');
    $params['class_id'] = "1";
    $params['name']     = '笹亀弘';
    $mongo->setKeyValue($params);
    
    $params1['class_id'] = "1";
    $params1['name']     = 'ささがめひろし';
    $mongo->setKeyValue($params1);
    
    $params2['class_id'] = "2";
    $params2['name']     = 'ささがめひろし';
    $mongo->setKeyValue($params2);
    
    usleep(1000000);
    
    $params3['class_id'] = "1";
    print $mongo->getKeyValue($params3);
    exit;
  }



usleepしている箇所についてですが、Mongoで連続で登録してカウントの処理をするときに、Lock処理が入りませんので、登録処理を完了する前に件数を取得する部分が発行してしまい件数が合わない場合があります。そのためにsleepで処理を止めています。

カウント取得しているものには条件を指定します。ここではカウント取得条件はclass_idが1である件数を取得しています。また、MongoDBはPHPとは違い数値と文字列の数値を区別する必要がありますので、カウントを取得する際などには注意が必要です。


    $params3['class_id'] = "1";
    print $mongo->getKeyValue($params3);
  結果: 2
    $params4['class_id'] = 1;
    print $mongo->getKeyValue($params4);
  結果: 0



MySQLなどのようにMongoにもコマンドで実行して値を確認する方法もあります。使用するコマンドは「mongo」コマンドから値を操作することができます。


mongoの基本的なコマンドしか実行しておりませんので、その他にどういったものがあるかはコマンドのhelpで確認するか、ドキュメントよりご確認いただけたらと思います。


symfonyでMongoクラスを作成しましたが、こちらには登録処理とカウントする処理しかありません。その他にも削除する処理やデータを取得するものを拡張していただければ自由にMongoDBの値を扱うことが可能になります。

symfonyアプリケーションをデプロイするためのCapistranoレシピ

こんにちは、亀本です。

1週間ぶりです。というのも、今までご無沙汰過ぎて、ブログ当番に2週連続であたってしまいました。
やっぱり普段からコツコツやっとかないとだめですね。反省反省。

さて、今回はデプロイのお話です。
アプリケーションのデプロイといえばリリースの締めの一番ミスできないところです。
みなさん、これをどうやって作業していますか?

最近はバージョン管理システムが導入されるのが当たり前になっている場合が多く、リポジトリからのチェックアウトで済ませてしまうとか、rsyncするだけという方も多いんじゃないでしょうか。

もちろん、Webサーバ2~3台ぐらいだったらそれで事足りますし、適切なポリシーを持って臨めば、セキュリティ的に問題が発生するような事態にも陥らないでしょう。

しかし、バージョン管理システム自体やrsyncを使ったデプロイは手軽であるというメリットの裏で、以下のような不安材料も抱えています。

ロールバックの概念がない(障害発生時にすぐ戻せない)
・デプロイサーバからの指示だけで作業を完結できない(本番サーバに手を入れる必要がある)
・更新後にApacheの再起動など、複数の作業を並列実行できない
・複数台の場合、最初のデプロイと最後のデプロイで時間差が出る

これらの点は管理台数が増えれば増えるほど、大きな問題として頭をもたげてきます。
それこそ、2~3台レベルの時には手動で対応できた内容が、台数が増えるだけでとんでもない作業量になったりします。
こういった問題を解決するためには、何かしら作業を前サーバに対して適用してくれるシェルスクリプトなり、サポートツールが必要になってきます。

こういった問題に対して、自分も最初はシェルスクリプトを使って、rsyncをラッピングしてロールバック可能な構成でデプロイしてくれるような仕組みを作ったんですが、さらに一括ロールバックのギミックやメンテナンスモード切り替え、などといった機能をいくつも実装しなければならないのは結構骨だなぁと思っていました。
やっぱり、便利なデプロイツールはほしくなるものです。


そして、そんなツールとして特に注目度が高いのがCapistranoです。
CapistranoRubyで作られたRails向けのデプロイ管理ツールで、

・リリースのバージョン管理
・複数台デプロイ
・複数タスクの自動実行
ロールバック処理
・メンテナンスモード切替

などを備えています。
デプロイ機能がデフォルトでRails専用になっているとはいえ、タスクの追加も容易で、レシピと呼ばれるタスク定義・設定ファイルにちょっと手を加えれば、Rails以外のシステムにも簡単に応用が利く優れものです。


そして今回は、Capistranosymfony用レシピを作成し、symfonyのデプロイに対応させてみました。
# そんな話をIRCでしてたら、もうやってる人いたけど、そこは気にしたら負けということで(^^;

このレシピファイルはgithubにアップしておきましたので、ご自由にお使いください。
ほぼかき捨てですが、一応、MITライセンスという感じで。(ライセンスよくわかってないので)

http://github.com/yudoufu/symfony-capistrano-recipe

ちなみに人生で初めて.rbを編集しました。rubyとかよくわかりません。rubyわかる人誰かきれいにしてください。
って、rubyというほどruby書いてませんが。


基本的な使い方は、任意のディレクトリに上記のレシピをcloneしてきて、必要な設定を書いたら



% cap deploy:setup
% cap deploy:install


とすれば、プロジェクトの配置が完了します。
その後、プロジェクトのファイル群を更新するには、



% cap deploy


とすれば、symfony本体とpluginsを除く、プロジェクトのファイル群を更新してくれます。
普段のリリース作業は、ほぼこれだけになると思います。



ざっと基本的な使い方を説明したところで、Capistranoのインストールからの利用手順と、各タスクの紹介を順にしていきます。


なお、Capistorano自体の基本的な説明は
http://www.oiax.jp/rails/capistrano.html
http://builder.japan.zdnet.com/sp/open-source-software-moonlinx-2009/story/0,3800096543,20396188,00.htm
といったあたりをご覧ください。
一度どちらかでも目を通しておいて貰ったほうが、全体の理解が進むと思います。


では、ひとまずCapistrano自体の説明は上記のサイトを読んでもらうとして、準備手順だけかっ飛ばして説明していきます。

1. Capistranoのインストール

rubygemsは、必須です。
そのあたりはyumやaptなんかで入れておいてください。



% sudo gem install capistorano
% sudo gem install capistorano_colors


後者は任意で。
Capistranoをインストールすると、cap と capifyという2つのコマンドが使えるようになります。

2. レシピのclone

githubから上記のレシピファイルを取得してきます。



% git clone git://github.com/yudoufu/symfony-capistrano-recipe.git



とりあえずここで、ファイルの内訳を簡単に。



symfony-capistrano-recipe
  /Capfile
  /config
      /deploy.rb
      /symfony.rb
  /uploads/config/databases.yml


Capfileは言わずと知れた、といったところです。ちょっと標準のものから手を加えてありますが、アンタッチャブルです。
deploy.rbでは、諸々の設定を行います。基本的に、普段はこいつを編集してもらうことになります。
symfony.rbが、主に今回作成したレシピです。こいつを見てもらえれば、大体何をやっているかがわかります。通常はアンタッチャブルです。

そして、uploadsディレクトリですが、ここにはプロジェクトディレクトリにアップロードしたい設定ファイルなどを配置します。
普通、databases.yml等のファイルはバージョン管理システムリポジトリには含めず、各サーバごとに管理していると思います。
そういった、リポジトリに含まれていないけどデプロイ時に配置されてほしいファイル群を、このuploadsディレクトリの中に置いて、ファイル名をdeploy.rbで指定すれば、デプロイ実行時にそれらのファイルも合わせてアップロードされます。

ここでは、デフォルトでconfig/databases.ymlだけ置いてありますが、これを適宜編集したり、必要なファイルを追加で配置していってください。


3. deploy.rbの編集

あとは、必要な変数を自身の変数に変更すれば、デプロイの準備は完了です。
大体どこに何を。。。というのは、ファイルのコメントを読んでもらえればわかると思います。
以下にファイルを張りつけておきます。




4. タスクの実行

というわけで、後はタスクを実行するだけです。
基本的なタスクの説明はさっきしたので、ここでは、もう少し個別に色々掘り下げます。


4.1. setup



% cap deply:setup


これはCapistranoがもともと持っているタスクで、上記のURLなどで説明されているように、リリース準備としてデプロイ先のディレクトリを生成してくれます。
通常、まず最初はこれをやります。

ここで出来上がるディレクトリ構成は


{deploy_to}
    /release
    /shared
        /log
        /web/uploads
        /plugins
        /lib/vendor/symfony

といった構成で出来上がります。
詳細の説明は上記のCapistranoの記事に譲りますが、sharedの中が全リリース共通で使うもの、releaseのなかが毎回変わるプロジェクト系のファイルが格納される場所です。

なお、sharedの中に作られるディレクトリは、それぞれshared_children、plugins_dir、symfony_lib_dirの設定をsetすることで指定できます。
どれがどう設定されているかは、コード見てもらえればわかると思います。

4.2. install



% cap deploy:install


symfonyのプロジェクトを丸ごとチェックアウトしてきて、適切に配置してくれます。
一番最初に配置を行う際に利用されることを想定しているタスクです。
(setupも同時に実行するようにしてもよかったんですが、気分的に分けました)

このタスクは
symfonyのプロジェクト全体(externals指定のものもすべて)をチェックアウト
・sharedにsymfony本体を配置
・sharedにpluginsのファイル群を配置
・releaseにプロジェクトの各種ファイル群を設置
・デプロイ元のuploadsディレクトリに配置したファイル群を、releaseディレクトリに自動配置
symfony cc
・currentディレクトリに対して、シンボリックリンクを作成
・startタスクの実行(Apacheの起動を割り当てたりします)


というのを、一気にやってくれます。
Apacheの設定などが済んでいれば、これで一気にデプロイが完了できます。


4.3. default



% cap deploy


先ほども紹介したデフォルト実行タスクで、

・プロジェクトリポジトリのファイルのみをチェックアウト(--ignore-externals)
・releaseにプロジェクトの各種ファイル群を設置
・デプロイ元のuploadsディレクトリに配置したファイル群を、releaseディレクトリに自動配置
symfony cc
・currentディレクトリに対して、シンボリックリンクを作成
・restartタスクの実行(Apacheの起動を割り当てたりします)

というのを一通りやってくれます。

symfonyはそれ自体がかなりでかいフレームワークなので、チェックアウトにも時間がかかるし、tarで固めるにしても複数台にデプロイするにはかなりの転送量になります。
なので、symfonyはpluginsといった、基本的にsvn:externalsで扱われるようなものはshared扱いにし、普段はチェックアウトを行わずにプロジェクト単体だけの取得・転送にとどめることで、いろんな負荷を軽くしてます。
通常、リリース時にはsymfonyのバージョンはFIX、pluginsも同様に扱われることがほとんどだと思うので、これらをsharedで扱っても十分です。


4.4. upgrade

symfonyは更新しないけどpluginsに追加/更新があった、なんて場合には



% cap deploy:upgrade


とすると、defaultのタスクに加え、プロジェクトのファイルだけでなくpluginsの中身も最新化してくれます。

4.5. dist_upgrade

さらに、めったにないと思いますが、セキュリティFIXなどがあってsymfony自体も新しいバージョンに切り替えた!なんていう場合には



% cap deploy:dist_upgrade


とすると、symfony、plugins、プロジェクトファイル群のすべてを更新してくれます。
やっている事はほとんど4.2. installと同じですが、最後にrestartタスクが実行される点だけが違います。

4.6. uploads

設定ファイル群の更新のみを行いたい、という場合には、



% cap deploy:uplods


を使うと、uploadsディレクトリの中のファイルアップロード+symfony cc だけに特化して処理を行ってくれます。
大掛かりな作業にならずに済むので、結構色々使えると思います。


4.7. cc

いわゆるsymfony ccをかけたいときには



% cap deploy:cc</strong>


とします。
これで、各サーバのcurrentのディレクトリでsymfony ccが実行されます。


※なお、migrateも一応実装してありますが、railsのmigrateとsymfony/doctrineのmigrateではちょっと意味合いが違うため、これはあんまり適切なタスクになっていないかもしれません。
なので、説明は割愛しますので、使いたい人はコード読んでください。
(その関連で、coldタスクもあまり使い勝手の良いものにはなっていないと思います。)



以上が、一通りの機能説明です。

多分、複数台デプロイに必要なタスクはこれで一通りは使えるのではないかと思いますが、折があれば便利な機能を色々追加していきたいと思います。
要望などあれば、どしどしコメントください。
# もちろんコミットも大歓迎ですw


では、皆様にストレスフリーなデプロイ・ライフを。