[Symfony]テンプレートにアクションで定義した変数を埋め込む方法

テンプレートにアクションで定義した変数を渡すには

テンプレート(モジュールではなくアプリケーション)でアクションで定義した変数を呼び出したい場面が多々あるが、それにはslotを使う。

方法

大まか以下の流れで実現可能。
①アクションに変数を定義
②モジュールのテンプレートでslotを定義
③アプリケーションのテンプレートでslot呼び出し

順々に、
①アクションクラスにhogeという変数名のパラメータを定義します。

class XXXXaction extends sfActions {
	public function executeIndex() {
		$this->hoge = 'technology.';
	}
}

②モジュールのテンプレートでは(XXXSuccess.php等)では普通に呼べるのでここにslotをまず埋める。

<?php slot('sample', $hoge);

③アプリケーションのテンプレートでslotを呼び出します。

<?php if (has_slot('sample')): ?>
<?php include_slot('sample') ?>  // <- technology.が表示されます。
<?php else: ?>
スロットが定義されていません。
<?php endif; ?>

これで、actionで定義した変数をlayout.php等で利用可能です。もちろん、layout.phpが読み込んでるパーシャル等でも。

[Book]図解プロジェクトマネジメント

要約

すべてのビジネスパーソンに、いまプロジェクト実践力が求められている。戦略コンサルタントが書いた実践的プロジェクトマネジメント手引きの決定版。

Amazonより

目次

第1章 プロジェクトマネジメントの基礎
第2章 プロジェクトマネジメントの実践
第3章 組織的なプロジェクトマネジメントの実践
第4章 企業変革プログラムマネジメントへの発展

プロジェクトマネジメントの全体像

WEB上でも公開しています。プロジェクトマネジメントの全体像

感想

体系的にプロジェクトマネジメントを学ぶための良書。初級者向けの本であるが工程別にすっきり書かれているので早急にINPUTする必要がある場合は是非。

[Symfony]対顧機能と社内機能のアクセス制限機能について

概要

フロントエンド向けのアプリケーションとバックエンド向けのアプリケーションを作成した場合、フロントエンドはWANからのLB経由でのアクセスのみ、バックエンドのアプリケーションは社内からのイントラネットでのアクセスのみと制限したいケースがある。その方法について簡単に。

解決方法

結論から言うと、Symfonyのディレクトリ構成を変えてしまえばよい。Symfonyの通常のアプリケーションはすべて、web下にある{アプリケーション名}.phpでのアクセスとなってしまう。

例えば、バックエンド(backend.php)に対してアクセス制限したい場合は、webと同列のbackendディレクトリを作成して、その下にbackend.phpを移動させてしまおう。そうすることで、LBはweb下にあるindex.phpへのアクセスのみを許可し、backend/backend.phpにはアクセスできない。逆に社内イントラネットからはbackend/backend.phpのアクセスだけを許可してやればよい。

[Symfony]ログ出力をカテゴリ別に分ける

概要

機能毎にログ出力を別ファイルに行うこと(※)を実現したい場合に、Symfony標準機能ではアプリケーションレベルで機能を分ける必要があります。
同一アプリケーション内でログ出力を分けたい場合には、独自の実装が必要となります。その方法について解決策を紹介します。

※一例として、同一アプリケーションでHTTPリクエストのログと外接システムとのログを分けたい場合

実現方法

同一アプリケーションで複数ログファイルを作成したい場合はsfAggregateLoggerを利用しますが、このクラスでは複数ログは出力できるものの、出力内容はほとんど同じになってしまい、(ログレベルの違いしか変更できない)少しカスタマイズが必要となります。

SymfonyでのログクラスはすべてsfLoggerという抽象クラスを継承しており、ログ出力に関してはdoLog関数に集約されています。
下記は、sfAggregateLoggerのdoLog関数です。

  /**
   * Logs a message.
   *
   * @param string $message   Message
   * @param string $priority  Message priority
   */
  protected function doLog($message, $priority)
  {
    foreach ($this->loggers as $logger)
    {
      $logger->log($message, $priority);
    }
  }

ここで登録されているログクラスについてすべて出力してしまっているので、ここに条件を入れれば問題として解決しそうだということが分かります。
したがって、方針としてはsfAggregateLoggerを拡張したmyAggregateLoggerを作成し、またmyAggregateLoggerに登録するログクラスをsfFileLoggerを拡張したmyFileLoggerを登録することとします。そしてmyFileLoggerに同一インターフェースをもたせるためにmyInterfaceLoggerも作成します。

myInterfaceLoggerを作成する

<?php
/**
 * 本アプリケーションで利用するログが継承すべきインターフェース。
 *
 * @author Yusuke Iwasaki
 */
interface myInterfaceLogger {
    
    /**
     * 実装クラスのログのタイプを返す。
     * このログタイプにより、出力ログファイルを振り分けます。
     */
    public function getType();
}

myFileLoggerを作成する

<?php
/**
 * カスタムファイルロガー。
 *
 * @author Yusuke Iwasaki
 */
class myFileLogger extends sfFileLogger implements myInterfaceLogger {

    protected
        $log_type;

    public function initialize(sfEventDispatcher $dispatcher, $options = array()) {
        if (isset($options['log_type'])) {
            $this->log_type = $options['log_type'];
        }
        return parent::initialize($dispatcher, $options);
    }

    public function getType() {
        return $this->log_type;
    }
}

myAggregateLoggerを作成する

<?php
/**
 * カスタムアグレゲートロガー。
 *
 * @author Yusuke Iwasaki
 */
class myAggregateLogger extends sfAggregateLogger {
    /**
     * ログファイルの分散出力を行います。
     * @param type $message ロギング文言
     * @param type $type ログファイルタイプ(request, circumscription)
     * @param type $priority ログレベル
     */
     public function logging($message, $type, $priority = self::INFO) {
        foreach ($this->loggers as $logger) {
            if (method_exists($logger, 'getType')) {
                if ($logger->getType() === $type) {
                    $logger->log($message, $priority);
                }
            }
        }
    }
}

ここで注意すべきはinterfaceで定義したgetType関数を持つか持たないかを判別する条件式を入れることです。開発環境では、デバックをONにしている場合にsfWebDebugLoggerが入ってくるためです。

factories.ymlに作成したログを登録する

all:
  logger:
    class:   myAggregateLogger
    param:
      level:   info
      loggers: 
        api_logger:
          class: myFileLogger
          param:
            level: info
            file: %SF_LOG_DIR%/%SF_APP%_%SF_ENVIRONMENT%_request.log
            log_type: request
        request_logger:
          class: myFileLogger
          param:
            level: info
            file: %SF_LOG_DIR%/%SF_APP%_%SF_ENVIRONMENT%_circumscription.log
            log_type: circumscription

利用方法

通常のログクラスと同様に熱かってください。sfContextクラスからログ関数を取得して、通常ならば「info, …, log」関数を呼び出しましたが、独自で作成した「logging」を呼ぶように注意します。

[HA]MySQL5.1.52 + Heartbeat3.0.5でクラスタを組む

概要

Databaseの冗長化を考えます。一般的な冗長化の方法としては、FTサーバを導入することですが、コスト的な観点から
状況によりベストエフォートではない可能性があります。論理的な冗長化の方法としてHeartbeatを利用した方法幾つか紹介します。

HAを利用した冗長化のソリューション
概要 詳細
共有ディスク型 Headサーバのみ冗長化し、ディスクは共有するソリューション。
シェアードナッシング型 ディスク単位も冗長化する、稼働系と待機系のディスク同期はレプリケーションにより実現。

上記以外の方法としては、MySQL Clusterの利用も考えられます。

今回のソリューションはシェアードナッシングにフォーカスを当てます。シェアードナッシングのイメージ図は以下の通り。

本来であれば、監視セグメント・サービスセグメント、、、と分けて構成してあげるのがBETTERですが、ここでは省略して、各ホストひとつのNICで構築していきます。

バージョン

今回使用したミドルウェアのバージョンは以下の通り。
MySQL 5.1.52
Heartbeat 3.0.2

MySQLのレプリケーション設定

各サーバの役割は以下の通りとします。

ホスト名 IP 役割
sample.db1 10.10.0.10 マスターサーバ
sample.db2 10.10.0.20 スレーブサーバ

マスターサーバの設定

①レプリケーション用ユーザの作成
GRANT REPLICATION SLAVE ON *.* to ‘replication’@’10.10.0.20’ IDENTIFIED BY ‘replication’;
②my.cnfの設定
[mysqld]
# バイナリログを出力する
log-bin
# 識別用ID。
server-id=10

スレーブサーバの設定

①my.cnfの設定
server-id=20

データ移行 & レプリケーション設定

①マスターサーバに対して更新がかからないようにロックをかけます
mysql > FLUSH TABLES WITH READ LOCK;

②マスターサーバのデータをスレーブサーバにコピーする
・/var/lib/mysql配下のデータファイルをコピーします

③マスターサーバのバイナリログの状態を確認します
mysql > SHOW MASTER STATUS;

④レプリケーション設定をスレーブサーバに追加します
mysql > CHANGE MASTER TO MASTER_HOST=’10.10.0.10′, MASTER_USER=’replication’, MASTER_PASSSWORD=’replication’, MASTER_LOG_FILE=’※1′, MASTER_LOG_POS=’※2′;
※1 ③で確認したファイル名称
※2 ③で確認したポジション

⑤レプリケーションを開始します
mysql > START SLAVE;

⑥レプリケーションの稼動確認
mysql > SHOW SLAVE STATUS\G;
※ Slave_IO_Running, Slave_SQL_Runningがyesになっていることを確認

ハートビートの設定

大きく、以下の三つの設定ファイルが存在します。
①/etc/ha.d/ha.cf
②/etc/ha.d/authkeys
③/var/lib/heartbeat/crm/cib.xml

ha.cfの設定

logfacility local0
auto_failback on
pacemaker on
debug 0
# heartbeatに利用するポート番号
# 複数クラスタを同一LAN内で運用する場合はポート番号を変更すること
udpport 694
# heartbeatの監視間隔
keepalive 2
# クラスタを組んでいるホストがダウンしたときに警告を出すまでの時間
warntime 20
# クラスタを組んでいるホストがダウンしたと判断する時間
deadtime 24
# 起動から監視を解しするまでの時間
initdead 48
ucast eth0 10.10.0.20
logfile /var/log/ha-log

# クラスタに参加するサーバ
# uname -nで表示されるホスト名
node sample.db1
node sample.db2

watchdog /dev/watchdog
uuidfrom nodename

authkeysの設定

auth 1
# 任意の文字列を設定可能だがすべてのクラスタで同一にする必要がある
1 sha1 XXXXXXXXX

cib.xml

crmコンソールから実行します。

[root@sample.db1 ~]# crm
crm(live)# configure
crm(live)configure# primitive vip ocf:heartbeat:IPaddr2 params ip="10.10.0.1" nic="eth0" cidr_netmask="24" op monitor interval="10s"
crm(live)configure# property $id="cib-bootstrap-options" stonith-enabled="false" no-quorum-policy="ignore"
crm(live)configure# rsc_defaults resource-stickiness="INFINITY" migration-threshold="1"
crm(live)configure# primitive mysql ocf:heartbeat:mysql params binary="/usr/bin/mysqld_safe" max_slave_lag="15" evict_outdated_slaves="false" config="/etc/my.cnf" datadir="/var/lib/mysql" user="mysql" pid="/var/run/mysqld/mysqld.pid" socket="/var/lib/mysql/mysql.sock" test_user="root" test_passwd="mysql" replication_user="replication" replication_passwd="replication" op monitor interval="5s" role="Master" on_fail="fence"
crm(live)configure# ms ms_mysql mysql meta master-max="1" master-node-max="1" clone-max="2" clone-node-max="1" notify="true" globally-unique="false" target-role="Master" is-managed="true"
crm(live)configure# colocation vip_on_master inf: vip ms_mysql:Master
crm(live)configure# commit

稼動確認

[root@sample.db1 ~]# crm_mon
============
Last updated: Tue Aug 21 10:47:49 2012
Stack: Heartbeat
Current DC: sample.db1 (565e8e57-bbf8-2c7d-c2ad-f25e97a1e183) – partition with quorum
Version: 1.0.12-066152e
2 Nodes configured, unknown expected votes
2 Resources configured.
============

Online: [ sample.db1 sample.db2 ]

vip (ocf::heartbeat:IPaddr2): Started sample.db1
Master/Slave Set: ms_mysql
Masters: [ sample.db1 ]
Slaves: [ sample.db2 ]

フェイルオーバー確認

crm_monで状態を確認している状態で、Active側のMySQLプロセスを殺してください。
そうすると、フェイルオーバーの確認ができます。

最後に

MySQLとHeartbeatという無料のミドルウェアを使って冗長性のあるスケールアウト可能なシステムがこんなにも簡単に作れるなんてすばらしい。

関連図書