2010年2月15日月曜日

Zend_Registryの使い方

Zend_Registryの使い方をとりあえずメモ

グローバルな値を格納するときに利用できるZend_Registryの利用方法をなんとなくメモ

利用は簡単で、Zend_Registry::set("変数名","値");でセットした値をZend_Registry::get("変数名");で取得する。

Zend_Registry::get('変数名');を実行する前に、Zend_Registry::set("変数名","値");を実行しておかないと、Zend_Exceptionがスローされる。

変数に設定しているクラス名からクラスを生成

Zend_Applicationでは、Bootstrapクラスを外部ソースで指定できるので、クラス名の文字列からクラスが生成できるだろうと思って調べてみたのでなんとなくメモ。


Zend_ApplicationのsetBootstrap()を見ていると、以下の記述があった。
コード例)
 $this->_bootstrap = new $class($this);
 どうやら、クラス名を文字列として指定して、生成できるよう。
ただ、
コード例)
 new 'クラス名'():

ではエラーになる。
一度変数に格納しないとだめなようです。


メソッドも同じように変数に一度格納することによって以下のような書き方できる。

コード例)
$class="aaa";
$method="bbb";
//aaaクラスを生成
$aaa=new $class();

//aaaのbbbメソッドを実行
$aaa->$method();

面白い。

BootstrapでのZend_Db_Tableにプロファイラの登録方法

Zend_Application_Resource_Dbを利用して、プロファイラを設定することはできなさそうなので、Bootstrapで設定する方法を、なんとなくメモ

Bootstrapから、'db'リソースを取得して、プロファイラを設定するだけ。

コード例)

function _initDbProfile(){
  $this->bootstrap('db');
  $db=$this->getResource('db');
  $profiler=new Zend_Db_Profiler_Firebug("All Querys");
  $profiler->setEnabled(true);
  $db->setProfiler($profiler);
}

ついでにメタデータをキャッシュする方法も追加すると以下のようになる。


コード例)メタデータをキャッシュする場合
function _initDbProfile(){
  $this->bootstrap('db');
  $db=$this->getResource('db');
  $profiler=new Zend_Db_Profiler_Firebug("All Querys");
  $profiler->setEnabled(true);
  $db->setProfiler($profiler);
  // フロントのオプション設定
  $frontendOptions = array(
    'automatic_serialization' => true
  );
  //バックエンドのオプション(ファイルへ吐き出すので、
  //キャッシュファイルを配置するディレクトリを指定
  $backendOptions  = array(
    'cache_dir'                => APPLICATION_PATH . '/data/cache'
  );
  //キャッシュオブジェクトを作成。キャッシュはファイルへ
  $cache = Zend_Cache::factory(
    'Core',
    'File',
    $frontendOptions,
    $backendOptions
  );
  // テーブルオブジェクトで使用するように設定します
  Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);
}

Zend Framework1.10からはZend_Application_Resource_Cachemanagerが利用できるので、もう少し記述が簡単になるのと、定義を外部に出せるのでメンテナンスはしやすくなるはず。
Zend_Application_Resource_Cachemanagerはおいおい調査

PHPでの処理実行時間取得

PHPで処理時間を知りたくなったので、なんとなくメモ

処理前後の時間をmicrotime()で取得して、差分を取得する。
microtime()は戻りが小数部、半角スペース、整数部の文字列になっているので、explodeで分割して、配列に格納してから、floatに変換しておく。

コード例)
$start=explode(' ',microtime());
sleep(5);
$end=explode(' ',microtime());
$diff=((float)$end[0]+(float)$end[1])-((float)$start[0]+(float)$start[1]);

2010年2月13日土曜日

Zend Framework 1.10.1リリース

Zend Framework 1.10.1リリースがリリースされてました。

変更点はよくわかりませんが、バグフィックスが中心かな?

2010年2月9日火曜日

Zend_View_Helperを自作する

Zend_View_Helperを自作する必要があったので、とりあえずメモ。

ヘルパーはZend_View_Helper_InterfaceかZend_View_Helper_Abstractを継承して作成する。

クラス名には、View_Helperを入れておいたほうがよいと、リファレンスに書いてあった。
クラスのメソッドとして、必ずクラス名からプレフィックスを除いた名称と同じ名前のメソッドを用意する必要がある。
※クラス名ははじめが大文字、メソッド名ははじめは小文字で。

コード例) 引数を大文字にするだけの簡単なHelperクラス
class My_View_Helper_StrUpper extends Zend_View_Helper_Abstract {
    public function strUpper($val){
        return strtoupper($val);
    }
}

配置先は、とりあえず、views/helpers配下あたりに置いておく。
 上のクラスの場合は、以下のようになる。
配置例)
views/helpers/StrUpper.php



このままではZend_Viewはヘルパークラスを認識できず、使えないので、独自のヘルパークラスを探してもらうようにZend_Viewに探し先を登録する必要がある。
パスを追加するには、Zend_ViewのaddHelperPath()を使う。
第一引数に、パス名(絶対パス)、第二引数にクラス名のプレフィックスを指定する。
コード例) コントローラ内でパスを通すときの書き方
$this->view->addHelperPath(APPLICATION_PATH.'/views/helpers','My_View_Helper');

※ヘルパーのクラス名をZend_View_Helper_XXXXとしておくと、ヘルパーのパス追加は必要がない。ただ、Zend_xxxとつけられるのはZendだけなので、めんどくさがらずに設定をしておくこと。

上記を行うことで、テンプレートにおいて、独自ヘルパーを利用できるようになる。

 コード例)テンプレート内での記述
<div>
<?php echo $this->strUpper("aa"); ?>
</div>

ViewHelperは以外に便利。

Zend_Layoutの制御

Zend_Layoutの制御の仕方をなんとくなくメモ

  ・Zend_Layoutオブジェクトを取得
        i)どこでも
            $layout = Zend_Layout::getMvcInstance();
        ii)ActionControllerから(その1)
            $layout=$this->_helper->layout;  
        iii)ActionControllerから(その2)
            $layout=$this->_helper->getHelper('layout');

    ・Zend_Layoutを無効にする
        $layout->disableLayout();
    ・Zend_Layoutを変更
        $layout->setLayout('nomal');

    ViewHelperの中には、LayoutやViewを自動的に無効にしてくれるものもある。
    Zend_View_Helper_Jsonなど



Viewにヘルパーを追加するようなときに必要になると思うので、ついでにZend_Viewの取得方法もついでにメモ。

Zend_Viewを使用しないと明示しない限り、自動的にZend_Viewは生成されているので、生成されているインスタンスを取得する方法をなんとなく列挙。

i)コントローラから
 $this->view
ii)どこでも
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer')->setNoRender(true);

iii)Bootstrap内で
$this->bootstrap('view');//一度初期化しておいたほうが間違いがない。
$this->getResource('view');    


他にもあるかも知れないけど、まぁこんなもんで。

Zend_View_Helper_Partial/Zend_View_Helper_PartialLoop/Zend_View_Helper_Action

Zend_Viewヘルパーで使えそうなものがあったので、なんとなくメモ。


(1)Zend_View_Helper_Action
他Actionの戻り値を取得することができる。
フォワードやリダイレクトをするようなものは、空の文字列が変える。
ウィジットのようなものもを作るのに便利。
引数として、アクション名、コントローラ名、モジュール名、引数(array)を渡す。

コード例)VIEWの中で、indexコントローラのsample2アクションを呼び出している
<?php echo $this->action("sample2","index",null,array("count"=>100));?>


(2)Zend_View_Helper_Partial
VIEWの中で他のテンプレートをレンダリングすることができる。
名前空間を気にせずにコーディングできるのと、複雑なレイアウトを使いまわしたいようなときに便利。
通常のVIEWファイルがview/scripts/コントローラ名/ディレクトリの配下を前提としているのに対し、

呼び出すテンプレートファイルは、views/scripts直下を探しにいくので、階層を間違えないように指定する必要がある。コントローラの範囲を超えて使いまわすのを想定しているのかも。
一応他のモジュールのVIEW配下のファイルも指定することができる。
拡張子はVIEWと同じもの。

コード例)
a) 元のビュー内の記述
<?php echo $this->partial("hogechild",array("aa"=>1,"bb"=>2)); ?>

b)呼び出されるテンプレート(hogechild.phtml)内の記述
<?php echo $this->aa ."/".$this->bb;?>


(3)Zend_View_Helper_PartialLoop
Partialヘルパーのループ版で、渡した引数の配列分だけ繰り返す。
テンプレート内では、 partialCounter変数で、繰り返している回数を取得することができるので、テーブルの行で、一行おきに背景色を変えるなども比較的に容易に実現できる。


コード例)
a) 元のビュー内の記述
 <?php echo $this->partialLoop("hogechild.phtml",
         array(
             array("a1"=>"1","a2"=>"2"),
             array("a1"=>"1","a2"=>"2")             
        ));?>

b)呼び出されるテンプレート(hogechild.phtml)内の記述 
(<?php echo $this->partialCounter?>)a1.":".$this->a2?>

2010年2月6日土曜日

ErrorController指定とFrontControllerの取得の仕方

 Zend Frameworkでは、ActionControllerでExceptionが発生した場合、Zend_Controller_Plugin_ErrorHandlerが例外をキャッチし、処理をErrorControllerのerrorメソッドに移動する。

モジュール構成にしている場合でも、何もしないと、必ずDefaultモジュールのErrorContorllerに処理が移るので、モジュール別にエラーページを切り分ける必要がある場合は、移動先を変更する必要がある。

やり方がわかったので、とりあえずメモ。

Zend_Controller_Plugin_ErrorHandlerには、以下の3つのメソッドがあるので、エラーが発生する前に移動先を決めることができる。

  • setErrorHandlerModule() //モジュールを変更する
  • setErrorHandlerController() //コントローラを変更する
  • setErrorHandlerAction() //アクションを変更する

設定の仕方は以下のとおり
  1. FrontControllerのオブジェクトを取得
  2. FrontControllerに設定されているZend_Controller_Plugin_ErrorHandlerをgetPlugin()で取得
  3. 取得したErrorHandlerのオブジェクトに対して、移動先を設定。

(コード例)
$front=Zend_Controller_Front::getInstance();
$plugin=$front->getPlugin('Zend_Controller_Plugin_ErrorHandler');
$plugin->setErrorHandlerAction('errorhoge');


モジュール別や特定の条件で分けることが決まっているならば、BootStrap中か、自前でプラグインを用意し、dispatchLoopStartup($request)で、指定しておけばよい。



ついでに、FrontControllerの取得方法がよくわからなかったのでとりあえずメモ

[FrontControllerの取得方法]
(1)どこからでも
$front=Zend_Controller_Front::getInstance();


(2)ActionControllerの中で
(2-1)ActionControllerのgetFrontController()を利用して
$front=$this->getFrontController();

(2-2)Bootstrapのリソース経由で

$bootstrap=$this->getInvokeArg('bootstrap');
$front=$bootstrap->getResource('frontController');
(2-3)Bootstrapnoコンテナ経由で
$bootstrap=$this->getInvokeArg('bootstrap');
$container=$bootstrap->getContainer();
if(isset($container->frontcontroller)){
$front=$container->frontcontroller;
$plugin=$front->getPlugin('Zend_Controller_Plugin_ErrorHandler');
$plugin->setErrorHandlerAction('errorhoge');
}
(3)Bootstarpの処理の中で
$this->bootstrap('FrontController');
$this->getResource('FrontController');


Zend_Controller_Plugin_ErrorHandlerはFrontControllerでdispatchされたときに自動的に設定されるようになっている。
BootStrapはフロントコントローラのdispatch前なので、Bootstrap中の処理ではフロントコントローラーに何もPluginに設定されていない。
この為、BootStrap中にErrorHandlerの設定を変更したい場合は、自分でErrorHandlerのオブジェクトを生成して、設定する必要がある。

(コード例)
$this->bootstrap('FrontController');
$front=$this->getResource('FrontController');
if($front->hasPlugin('Zend_Controller_Plugin_ErrorHandler')){
$plugin =$front->getPlugin('Zend_Controller_Plugin_ErrorHandler');
}else{
$plugin=new Zend_Controller_Plugin_ErrorHandler();
$front->registerPlugin($plugin, 100);
}
$plugin->setErrorHandlerAction('errorhoge');

2010年2月4日木曜日

Zend_Db_Table(その6) 追加、更新、削除、トランザクション

テーブルへのデータ追加、更新、削除とトランザクションについて、とりあえずメモ

(1)データ追加
データの追加は、行を新しく作ってから、保存するやりかたと、直接データを追加する2つの方法がある。

a)空行を作ってから保存
テーブルクラスのcreateRowメソッドを使って、新しく行オブジェクトを作成して、データを設定し、Rowオブジェクトのsave()メソッドを使って保存をする。

コード例)
$table=new Hoge();
$row=$table->createRow();         
$row->name="sample";
$newid=$row->save();


b)直接データ追加
テーブルクラスのinsert()メソッドを使って、データを直接テーブルに追加する。

コード例)
$table=new Hoge();
$newid=.$table->insert(
  array("name"=>"sample")
);


createRow()メソッドで行オブジェクトを作った場合、値を設定した以外の項目はnullが入るため、テーブル定義でデフォルト値を設定するようにしていても意味がなくなる。
SQL発行をProfilerで見ると、createRow()=>save()で追加を行った場合、新しく割り当てられた主キーの値を取得するためにselect文を実行しているので、insert()を使うよりもSQLが1つ多く発行されていることになる。

createRow()メソッドを使う方法の方が、更新とほぼ同じ手順データ操作が行えるので、いい感じだけど、insert()を使ったほうが、デフォルト値などの設定の手間を考えると、問題が少ないかも。



(2)データ更新
データの更新は、更新行を入手してから、値を変更し、テーブルへ更新する方法と、直接条件を指定してデータを更新する2つの方法がある。

a)対象行を入手してから更新
テーブルクラスから、find(),fetchRow(),fetchAll()などのメソッドを使って、更新対象となる行オブジェクト(Zend_Db_Table_Row)を取得し、項目の値を変更したとRowオブジェクトのsave()メソッドを 使って保存をする。

コード例)
$table=new Hoge();

$row=$table->fetchRow($table->select()->where("id=?",1);         
$row->name="sample";
$row->save();

b)条件を指定して直接データ更新
テーブルクラスのupdate()メソッド を使用し、更新データと検索条件を指定し、テーブルのデータを更新する。

コード例)
$table=new Hoge();

$count=.$table->update(
  array("name"=>"sample"),
  $table->getAdapter()->quoteInto("id=?",1)
);
update()の2つ目の引数には、SQLの条件を記入する。
単に文字列を渡すことになるため、クォート処理などは行ってくれない。
このため、テーブルアダプタのquoteInto()やquoute()メソッドなどを利用して、クォート処理をしておくこと。

updateは条件の指定次第で複数行にを一度に更新ができる。全レコード対象とする場合は、update()の2番目の引数にnullを設定する。

update()は更新内容、条件が同じ場合に利用し、Rowオブジェクトのsave()メソッドを利用する場合は、更新内容や、レコードの特定項目の条件あわせて変更するような場合に利用する。

(3)データ削除
データの削除は、更新の場合と同様に、対象行を入手してから、削除する方法と、直接条件を指定して削除する2つの方法がある。

a)対象行を入手してから削除
テーブルクラスから、find(),fetchRow(),fetchAll()などのメソッドを使って、更新対象となる行オブジェクト (Zend_Db_Table_Row)を取得し、項目の値を変更したとRowオブジェクトのdelete()メソッドを 使って削除する。

コード例)
$table=new Hoge();

$row=$table->fetchRow($table->select()->where("id=?",1);
$row->delete();

b)条件を指定してデータ削除
テーブルクラスのdelete()メソッド を使用し、検索条件を指定してデータを削除する。

コード例)
$table=new Hoge();

$count=.$table->delete(
  $table->getAdapter()->quoteInto("id=?",1)
);

delete()の2つ目の引数には、SQLの条件を記入する。
クォート処理などは行わないのは更新の場合と同様

(4)トランザクション
 通常Zend_Db_Tableなどへの更新操作は、自動的に操作単位でコミットされる。
複数の操作に対して、トランザクション制御を行いたい場合は、アダプタークラスのbeginTransaction()、commit()、rollback()メソッドを利用する。

コード例)
$table=new Hoge();
$table->getAdapter()->beginTransaction();
try{
   //テーブルへの操作など
   $table->getAdapter()->commit();
}catch (Exception $e){
  $table->getAdapter()->rollback();
}

2010年2月3日水曜日

Zend_Db_Table(その5) 検索その2

find()メソッドでは、主キーのみにしか条件に設定できないため、他の条件を指定する場合は、fetchAll()メソッドやfetchRow()メソッドで、Zend_Db_Table_Selectクラスを指定する。

Zend_Db_Table_Selectクラスは、テーブルクラスのselect()メソッドを使うと取得できる。

(コード例)
$table=new Hoge();

$select=$table->select();

Zend_Db_Table_Selectクラスに対して、条件を設定する。
SQLで記述するところを
 from() : テーブル名や取得する項目を指定する。
 where():検索条件をANDで指定する。
 orwhere() :検索条件をORで指定する。
 order() :ソート順を指定する
 limit():取得件数を制限する


(コード例)
$table=new Hoge();
$select=$table->select();
//テーブルからidとnameを取得対象とする
%select->from($table->name(),array("id","name");
//id項目に制限を追加
$select->where("id>?", 100);
//name項目に制限を追加(OR)
$select->orwhere("name like ?","%hoge%");
//sort順をidの降順に設定
$select->order("id DESC");
//取得するレコードを11番目から100行取得
$select->limit(100,10);
//検索実行
$rows=$table->fetchAll($select);

Zend_Db_Table_Selectへの条件を指定する順番は意識しなくてもいい。
where()などの戻り値はZend_Db_Table_Selectオブジェクトになるので、以下のような記述もできる。

(コード例)
$table=new Hoge();
$table->fetchAll($select=$table->select()->where("id>?",100)->orwhere("name like"."%hoge%")->limit(100,10)->order("id DESC"));

※2行目は区切りなしで記述している。


他のテーブルとの結合(join)もできる。

(コード例)
$table=new Hoge();
$select=$table->select()->from()->setIntegrityCheck(false)->join("テーブル名","テーブル名.id=hoge.id",array())->where("id>?",100);

leftjoin()メソッドやrightjoin()メソッドを使うと、外部結合の指定も可能。
他のテーブルを結合する場合、取得する項目に他のテーブルの項目を混ぜることができ、joinメソッドの3つ目の引数で指定する。
その場合、setIntegrityCheck(false)としておかないと、例外がスローされる。

他のテーブルの項目を混在して取得したZend_Db_Table_Rowに対してはupdateメソッドを使った更新を行うことができずに例外がスローされる。

2010年2月1日月曜日

Zend_Db_Table利用時のメタデータのキャッシュ設定

Zend_Db_Tableでは、実行するたびにテーブルのメタ情報を作成している。
テーブルメタデータをキャッシュすることで、パフォーマンスをあげることができる。
やり方はマニュアルに書いてあるけど、なんとなくメモ。
 
キャッシュはZend_Cacheを利用。

ファイルへメタデータをキャッシュする場合は、以下のように設定する。

// (1)キャッシュを作成
$cache = Zend_Cache::factory('Core', 'File',
    array('automatic_serialization' => true),
    array('cache_dir' => APPLICATION_PATH . '/data/cache')
);
//(2) すべてのテーブルオブジェクトで上記のキャッシュ設定Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);


メタデータをテーブルクラスに直接記述してさらにパフォーマンスをあげることができるそうです。
テストまでは、キャッシュを使って、運用時は直接メタデータを書いたほうがいいかのな。