Zend Framework

Zend Framework での Amazon ItemSearch with PEAR Pager

最近個人ワークで Amazon を使ったモノを2つ程作っていますが、1つは Zend Framework を使用して開発中。 Services_Amazon の itemSearch を使用し商品検索を行っていましたが、Zend Framework 内で各ページ 10 件表示を PEAR Pager の GET の処理で行った際に少し悩んでしまったのでメモ。


Zend Framework の細かい仕様はここでは省きますが、Zend Framework は MVC パターンに基づいて Zend_Controller_Front が URL を分析しディスパッチ処理・ルーティング処理を行います。例えば今回の URL が http://www.test.com/amazon/search/ だとすると AmazonController class 内のsearchAction 関数内で処理を行うのですが、下記の様に Pager のオプションで append を false に、fileName に GET の変数値を表す "%d" を明確に「?pageID=%d」の様に追加しないとうまく動きませんでした。


(ソースは簡略化してあります。)
$amazon =& new Services_AmazonECS4(あなたの Subscription ID);
$amazon->setAssociateID(あなたの Associate ID);
$amazon->setLocale('JP');

$options = array();
$options['Keywords'] = 'flash';
$options['Sort'] = 'salesrank';
$options['ResponseGroup'] = 'Medium';
if(isset($_GET['pageID'])) {
$options['ItemPage'] = $_GET['pageID'];
} else {
$options['ItemPage'] = 1;
}

$rs =& $amazon->itemSearch('Books', $options);

$params = array(
'itemData' => itemData 用の配列,
'perPage' => 10,
'delta' => 5,
'urlVar' => 'pageID',
'path' => 'http://www.test.com/amazon/result/',
'fileName' => '?pageID=%d',
'append' => FALSE,
'httpMethod' => 'GET',
);

$o_pager =& Pager::factory($params);
$navi = $o_pager->getLinks();
$pageNavi = $navi['all'];

こうすることで、URL が 2 ページ目から http://www.test.com/amazon/result/?pageID=2(3,4,5・・・)の様になるので無事に Services_Amazon の $options['ItemPage'] の値に GET 変数を渡すことが可能です。

今回は append を TRUE 、fileName を空白にして、デフォルトではhttp://www.test.com/amazon/result/index.php?pageID=2 になったり、append を FALSE 、fileName を空白にして http://www.test.com/amazon/result/ で GET 変数が渡らなくなったりと Zend Framework 内で上手く挙動させれなかったのですが、検索して %d が 変数値だと分かり上記の設定で解決しました。

ページの一番上へ移動

Zend_Amf_Server

Zend Framework + AS3/Flex ユーザーの自分にとっては Zend と Adobe が提携して Zend_Amf_Server がリリースされたので、おっ!って感じで早速使ってみた。

これまでは AMF 通信の際は AMFPHP 1.9 を使っていて特にこれで問題は無いけれど、Zend Framework がインクルードされている環境では Zend_Amf_Server はすぐに使えるし、個別にパッケージをダウンロード出来るのでそれだけアップロードしても使用可能。Zend Framework ベースでデータベース連携とかするなら PHP 5 / MySQL 5 とか推奨なので、環境が無い場合はこれまで通り AMFPHP + PEAR 辺りで Flash と連携して特に問題ないと思う。一応、Cairngorm Framework とともに使ってみたけど使い勝手に特に問題ないし、外国人の開発陣も結構興奮している模様なんで、次のアプリケーション開発で環境が整う場合には、設定とか簡単だし Zend_Amf_Server を使ってみよう。

使い方。

(1)任意のサーバディレクトリに server.php(名前は何でも OK。)を作成し、下記の様に AMFServer を作成する。ブラウザ経由でこのページに直接アクセスしダウンロードが開始されるか、システムコマンドでアクセスして <p>Zend Amf Endpoint</p> と表示されたら設定完了。ただしこのままだと AMFServer が設定されただけなので、実際に Flash/Flex から呼び出す関数を持った PHP class(ここでは、Test.class.php)を setClass 関数で AMFServer インスタンスにアタッチする。また、名前空間を使用し複数のクラスをセットしたり、特定のディレクトリごとすべて含める事が出来る。これを設定する事で、Flex 側から RemoteObject の source プロパティで PHP 側のクラスファイルを記述する必要が無くなる。また、setClassMap 関数で明示的に DTO にも対応させることができるのは AMFPHP とは設定は多少違う。

AMFServer 用の php ファイル設定(http://あなたの URL/server.php)

<?php
require_once('Zend/Amf/Server.php');
require_once('class/Test.class.php');

// Zend_Amf_Server Object
$server =& new Zend_Amf_Server();
$server->setClass('Test');
//$server->setClassMap('TestVO', 'TestVO');
$response =& $server->handle();
echo $response;
?>

Test class(AMFPHP 用クラスファイル)
*今回は簡単な helloWorld 関数を持った Test クラスを作成する。

<?php
class Test {
    /**
    * Construct Function
    */
    public function __construct() {
    }
    /**
    * Function to helloWorld
    * @param  String name
    * @return String
    */
    public function helloWorld($name') {
        return 'Hello, ' . $name;
    }
}
?>

この記事の続きを読む »

ページの一番上へ移動

Zend_Amf_Server + AS3

個人的に勉強していた Zend_Amf_Server を実レベルの仕事で初めて Flash Remoting として使ってみた。プロジェクトは Flash ゲームで、もう 1 人の Flash デベロッパーと連携してゲーム以外の AS3 のバックエンド/モデルクラス/ビュークラス/コントローラ系のプログラム、Remoting 系、PHP/MySQL 開発、サーバ少々を担当。

Zend_Amf_Server は物凄くいいですね。何がいいかって Zend を普通のウェブアプリケーション感覚で開発できるので連携するバックエンド部分を MVC で組めるし、Zend_View_Smarty とか Zend_Db_Table とか Zend_Registry とか Flash と連携することを意識しないで使えるから超楽だ。Zend を使ってる Flash デベロッパーなら学習コストなし。PHP/MySQL 系のウェブアプリケーションに作っていた Zend のクラス群を Flash Remoting 用にそのまま引っ張ってきて再利用できたので、よっぽどシステムがでかくなければ簡単に作れちゃう感じですな。

で、Zend_Amf_Server で注意したい点を忘れないようにメモッておく。

この記事の続きを読む »

ページの一番上へ移動

そう言えば、base タグってありましたね。

ポーランド人のプログラマの同僚にぼそってささやかれて、先日はっとしました。
と言うのは、物凄く基本的な話ですが、下記の様なタグの存在をこの世からすっかり忘れていました。

<head>
	<base href="http://www.convexstyle.com/" />
</head>

これがあることで、Zend Framework 内で
www.convexstyle.com/controller
www.convexstyle.com/controller/
www.convexstyle.com/controller/action
www.convexstyle.com/controller/action/
を同じ処理出来るんですよね。

2 番目と 4 番目がディレトトリと見なされて、アセット関連のパスがおかしくなるのが嫌だったのでこれまでは Zend Framework 内で Smarty をテンプレートとして使う際に base タグを使わずに、例えば、<img src="<% $homeUrl %>images/sample.jpg" /> とか物凄い効率悪いやり方をしてました。

なのでこれからは

<head>
	<base href="<% $homeUrl %>" />
</head>

にして <img src="images/sample.jpg" /> でいいじゃんと思いました。
基本を忘れちゃいけないなあ。

一点問題は、<a href="#top">top</a> の様な内部リンクに関しては $homeUrl が http://www.convexstyle.com/ の場合、絶えずリンク先が http://www.convexstyle.com/#top になってしまうんですよね。この場合現状のページトップに行きたいのに IndexController にアクセスしてしまうので、これを避ける際は、やはりこういう場合は controller 側で対応して、view 側に 現状の controller や action やスラッシュとかを <a href="<% $currentUrl %>#top">top</a> の様な形で渡さないといけないですね。 もともと自分は javascript でページ top への移動は内部リンクと JQuery の animate で対応してたんですが、知らない間に scrollTo という JQuery のプラグインが存在していたので、これもまた <a href="Javascript:void(0);" onClick="goTop();">top</a> 的にすれば全く Zend の URL 構造は依存せず対応出来ますね。知らないで効率悪かったという話です。

・・・・・

参考サイト
base タグ
scrollTo (JQuery Plugin)

ページの一番上へ移動

RemoteProxy + Zend_Amf in AS3

Proxy class を extends した RemoteProxy class を作って Zend_Amf_Server にアクセス。
所謂、RemoteProxy Pattern を使用して RPC で Zend_Amf_Server 側の controller の関数をコール。

NetConnection オブジェクトで Zend_Amf_Server にアクセスする際に NetConnection.Connect.Success が NetStatusEvent で返ったのを確実に確認してから RPC しようと思ってたんだけど、NetConnection.Connect.Success って返らないんですね。NetConnection.Call.Failed などはきちんと返るんですが、接続成功時は NetStatusEvent の evt.info.code が何故か取得出来ない。

この記事の続きを読む »

ページの一番上へ移動

Zend_View の複製 - clone -

シドニーに来て一番忙しかった週を乗り越えて、ようやく少しだけゆったり出来る!
クリスマスに向けて海外は追い込みなんですよねー。

・・・・・

そんな感じで、PHP でオブジェクトを複製するのに clone ってのが使えるんですね。
例えばメールを送る時や API の Post データのレンダリングを動的に行う時など、Zend_View_Interface を implements した Zend_View_Smarty(名前は任意)を複製して通常の View の処理と明示的に分けたりする。ここら辺の修理は以前非効率なやり方してた。

例えばこんな感じか。

public function testAction()
{
    $_req       =& $this->getRequest();
    $_firstname = $_req->getPost('firstname');
    $_lastname  = $_req->getPost('lastname');
    $_email     = $_req->getPost('email');
    $_from      = $this->mailIni->from;
    $_subject   = $this->mailIni->subject;
    $_data = array(
    );
    $_mailData = array(
        'firstname' => $_firstname,
        'lastname'  => $_lastname
    );
    // View の複製
    $_view = clone $this->view;
    $_view->assign('_mailData', $_mailData);
    $_body = $_view->render('mail/_body.tpl');
    $_mail =& new Zend_Mail();
    $_mail->addTo($_email);
    $_mail->setFrom($_from);
    $_mail->serSubject($_subject);
    $_mail->setBodyText($_body);
    $_mail->send();
    // 通常の View
    $this->view->data = $_data;
}

・・・・・

[参考サイト]
オブジェクトのクローン作成

ページの一番上へ移動

fancybox + URL 書き換え + Zend Framework

Fancybox

Javascript と言えば、自前か JQuery 関連以外はあんまり使ったことが無いので library とか良く分からないですが、最近 thickbox 風の fancybox っていうのをプロジェクトで初めて使ってみた。使い方は物凄く簡単であえて説明をする必要もないので使用方法を見てもらうとして、こんな感じの見た目。

ここ

で、決まった URL 静的に html に記述、もしくは動的な URL を PHP 等でレンダリングして fancybox を割り当てるのは簡単なんですが、DOM 生成後に fancybox への URL をイベント毎に変更して割り当てるにはどうするんだと思って少しはまったのでメモ。

例えばユーザーの入力要素を付加した動的な URL(Zend Framework では Get 変数や UserParam 変数)を iframe 表示する場合。表示エリアが十分で、非同期に Ajax 処理できる場合は特に fancybox を選択する必要が無いので問題ないんだけど、表示スペースの問題でポップアップが必要な場合に当てはまるかも。ちなみに fancybox には thickbox の tb_show や tb_remove 等の様に関数からの処理が出来ない模様。

この記事の続きを読む »

ページの一番上へ移動

Zend_Amf + Win/Linux compatibility

最近 PHP よりのネタばかりだ。。。

Windows サーバでの開発とかした時が無かったし、Flash デベロッパーのつもりなんであんま気にしてなかったんだけど、Zend Amf Server を設置して Flash と連携しようと思ってはまった。まあ、バックスラッシュとかそういった問題なんだけど一瞬途方に暮れたんでメモ。PHP 専門の人には基本中の基本だろうけど。

Zend Amf Server 単体も個人的には通常の Zend Framework での開発と構成を同じにしたいので、ini_set でライブラリをいつもの様に下記の様に設定。でもこれだと Windows で動かず。

ini_set('include_path',ini_get('include_path').':../_application/libs:');

すべてを吸収するのは不可能だとか途方に暮れてたら、PATH_SEPARATOR とか定数があるんですね。知りませんでした。なので下記の様に変更する事で対応出来ました。これで windows(xampp など)のローカルでの簡単なテストも行けますね。

<?php
if(!defined('PATH_SEPARATOR')) {
    if(substr(strtoupper(PHP_OS), 0, 3) == 'WIN') {
        define('PATH_SEPARATOR', ';');
    } else {
        define('PATH_SEPARATOR', ':');
    }
}
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . '../_application/libs');
//. .(一部削除) . .
$_envIni = new Zend_Config_Ini(CONFIG_BASE . 'app.ini', 'env');
$_server   =& new Zend_Amf_Server();
$_server->setClass('UserController');
$_server->setClassMap('UserVO', 'UserVO');
if($_envIni->status == 'test') {
    $_server->setProduction(false);
}
$_response = $_server->handle();
echo $_response;

・・・・・

2011 年初め辺りに日本に復帰するだろう。まだ1年以上あるんだけど、その時期が自分的にはいい時期です。また日本で働ける様に、それまで海外でもっとウェブを鍛錬します。

ページの一番上へ移動

Zend_Paginator を使ってみる。

こんにちわ。
Sydney に渡って前半開発に頑張りすぎて肩こりが酷くなったので、たまには運動しないとと思い最近 15 年くらいやってて多少自信があったスケボーを購入してやり始めたんですが、2 週間目で疲労骨折気味になってしまい、全く動けなくなっている convexstyle です。意味ねえ…

・・・・・

最近のプロジェクトで Zend_Paginator を初めて使ってみました。
今までは PEAR::Pager を使っていたけど、Zend Framework に入ってなかなか便利だというのを聞いていたので使ったが便利だった。

Zend_Paginator_Adapter_DbSelect はかなり良いらしいが、今回は CMS の API 経由で XML をパースして表示する形式だったので、シンプルに Zend_Paginator_Adapter_Array をアダプタとしては使用した。

ページにどの様に表示するかは CSS/XHTML とテンプレートエンジン(Smarty など)の問題なので、色々やり方はあるしそこまで重要では無いが、重要な点はどの様に URI の syntax をテンプレートエンジンのレンダリング結果に受け渡すかだろう。

まずはどの様な値が Zend_Paginator から返されるか dump してみる。

$_paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Array($_data));
$_paginator->setCurrentPageNumber($_pageId);
$_paginator->setItemCountPerPage($this->_pagerIni->blog_per_page);
$_paginator->setPageRange($this->_pagerIni->page_range);
Zend_Debug::dump($_paginator->getPages('Sliding'));

結果は stdClass 形式で下記の様に返される。

object(stdClass)#69 (12) {
  ["pageCount"]=>
  int(1)
  ["itemCountPerPage"]=>
  int(3)
  ["first"]=>
  int(1)
  ["current"]=>
  int(1)
  ["last"]=>
  int(1)
  ["pagesInRange"]=>
  array(1) {
    [1]=>
    float(1)
  }
  ["firstPageInRange"]=>
  float(1)
  ["lastPageInRange"]=>
  float(1)
  ["currentItemCount"]=>
  int(2)
  ["totalItemCount"]=>
  int(2)
  ["firstItemNumber"]=>
  int(1)
  ["lastItemNumber"]=>
  int(2)
}

この記事の続きを読む »

ページの一番上へ移動

Zend_Service_ReCaptcha を使ってみる。

Optical Character Recognition 技術で有名な悪意ユーザーを退治する ReCaptcha が Zend_Service に知らないうちに組み込まれてたんでプロジェクトに使ってみる。かなりシンプルな API 対応なんで想像つくけど、あるもんは使う。

Recaptcha

reCAPTCHA でアカウントを作って、Public Key と Private Key を作成。
これを ini ファイルに設定して、こんな感じで設定。

$_params = array(
    'theme' => 'blackglass'
);
$this->_recaptcha = new Zend_Service_ReCaptcha($this->_recaptchaIni->public_key, $this->_recaptchaIni->private_key);
$this->_recaptcha->setOptions($_params);

View には

$this->view->_recaptcha = $this->_recaptcha->getHtml();

後は変数に post された際に下記な感じか。

if($_req->isPost()) {
    $_recaptcha_challenge_field = $_req->getPost('recaptcha_challenge_field');
    $_recaptcha_response_field  = $_req->getPost('recaptcha_response_field');
    $_recaptchaResult = $this->_recaptcha->verify(
        $_recaptcha_challenge_field,
        $_recaptcha_response_field
    );			
    if(!$_recaptchaResult->isValid()) {
        $_msg = 'reCaptcha field is needed to be filled correctly.';
        array_push($this->_errors, $_msg);
    }			
    if(!empty($this->_errors)) {					
        return;
    }
}

楽やな。

・・・・・

海外に来て、日本じゃあまり聞いたことの無いサービスの API に連携しないといけない事が多々ある。自分だけ知らないのかもしれないが。Silverpop がその1つ。メーリングリスト系のウェブサービスらしい。さすがにこれは Zend_Service に無かった。PHP 系のサイト制作には高頻度使用なので、自作しよう。

日本ではあり得ない仕様が海外だと結構あり得たりする。
ここら辺は本当にむずい。そのうちここら辺については書こう。

ページの一番上へ移動