Tags: 技術日記

技術日記
Zend_Service_TwitterでBASIC認証からOAuthに切り替える方法Edit

古い(BASIC認証用の)ZendServiceTwitterを使ってTwitterのbotをいろいろ動かしていたんだけど、そのまま放置していたらBASIC認証が使えなくなって動かなくなっていた。

ZendServiceTwitterも今のバージョンはOAuthに対応しているんで、それに切り替えようと思ったんだけど、OAuthってことはアプリケーション登録してConsumerKeyとConsumerTokenを設定するだけじゃなく、自分のアカウントがそのアプリケーションにアクセスを許可(権限を移譲)するためのAccessTokenとかSecretTokenとかを発行してもらう必要があり、それってめんどくさ、と思ってそのまま放置していた。

けど、Twitterでめんどくさとつぶやいたら、bot用のキー入手方法が用意されてるよ、と教えてもらったので、その情報を利用してOAuth対応したついでに、そのやり方をオンラインにフィードバックしておく。

従来のBASIC認証対応のZendServiceTwitterだと、

$userName = ''; // twitterのアカウント
$password = ''; // twitterのパスワード
$twitter = new ZendServiceTwitter($userName, $password);
みたいな感じでZendServiceTwitterオブジェクトを生成していたけど、OAuth対応にする場合は、
  • まずbotアカウントでログインした状態で、http://twitter.com/appsにアクセスして、bot用のアプリケーション登録を行う。Default Access TypeはbotならばRead & Writeにすることを忘れない。→Consumer keyとConsumer secretが得られる。
  • 続いて、http://dev.twitter.com/apps/にアクセスして、該当アプリケーションのMy Access Tokenを見る。→Access Token(oauthtoken)とAccess Token Secret(oauthtokensecret)が得られる。
という下準備をしておいてから、
$accessToken = ''; // Access Token
$accessTokenSecret = ''; // Access Token Secret
$token = new ZendOauthTokenAccess();
$token->setToken($accessToken);
$token->setTokenSecret($accessTokenSecret);
$config = array(
  'consumerKey' => '', // Consumer key
  'consumerSecret' => '', // Consumer secret,
  'accessToken' => $token,
);
$twitter = new ZendServiceTwitter($config);
といった感じになる。

ZendServiceTwitterとかZend_Oauthとかのドキュメントを読んでも、OAuth対応のWebアプリケーションを作る場合の書き方(Twitterでの認証結果をコールバックURLで受け取って……みたいなの)はサンプルにあるんだけど、botみたいにあらかじめAccess TokenとかAccess Token Secretとかが分かっている場合の使い方が書いてなくて、結局ソース読んで試行錯誤する羽目になったんで、ここにやり方をまとめておく。

ひとまずこれで動いているけど、お作法が美しくないとか、無駄があるとかの問題はあるかもしれない。

Published At2010-09-17 18:53Updated At2019-12-30 15:31

技術日記
Amazon Reloaded Pluginで空白(半角スペース)を含む検索が成功しないバグEdit

なぜかAmazon Reloaded Pluginで半角スペースを含む検索を行っても結果が返ってこないんで、ソースを見てみたところ、

$url .= 'Keywords='.urlencode(str_replace(' ', '%20', $terms)).'&';
となっていたんで、
$url .= 'Keywords='.rawurlencode($terms).'&';
と修正したら半角スペースを含む検索も動くようになった。urlencodeで半角スペースが「%20」ではなく「+」に変換されてしまうのを避けたかったのかな?

でも、Amazon Product Advertising APIでこの処理の後に電子署名をつけたりしているんで、その辺対応に変更する際に、なんかおかしくなったのではないかと推測。

signUrl関数の中でもなんか一生懸命それ関係の置換処理とか行ってるけど、素直にrawurlencode関数の方を使っておくようにすれば、全体的にもっときれいに書けそうだ。

Published At2010-05-12 15:28Updated At2019-12-30 15:32

技術日記
Amazon Reloaded Plugin改造Edit

WordPressでAmazonから書誌情報を取得するプラグインは、ググったらWP-Amazonという名前をあちこちで見かけるんだけど、WordPressの管理ツールからプラグイン検索しても出てこない。よくわからないんで、Amazon Reloadedとかいうのをインストールしてみた。

投稿ページでAmazonを検索して、見つかった商品の画像やタイトルをリンク付きで投稿フォームに挿入する、という基本的な機能は十分なんだけど、これは著者情報とかはとってきてくれないらしい。著者情報は個人的には必須なんで改造して対応させた。

amazon-reloaded-for-wordpress.php

$imageUrls = array();
 $images = $item->getElementsByTagName('URL');
 for ($imageNumber = 0; $imageNumber < $images->length; $imageNumber++) {
 $imageUrls[] = $images->item($imageNumber)->nodeValue;
 }
// ↓ここから↓
 $authorNames = array();
 $authors = $item->getElementsByTagName('Author');
 for ($authorNumber = 0; $authorNumber < $authors->length; $authorNumber++) {
 $authorNames[] = $authors->item($authorNumber)->nodeValue;
 }

 $actorNames = array();
 $actors = $item->getElementsByTagName('Actor');
 for ($actorNumber = 0; $actorNumber < $actors->length; $actorNumber++) {
 $actorNames[] = $actors->item($actorNumber)->nodeValue;
 }

 //$item = array('asin'=>$asin, 'name'=>$name, 'detailPageURL'=>$detailPageUrl, 'imageURLs'=>$imageUrls);
 $item = array('asin'=>$asin, 'name'=>$name, 'detailPageURL'=>$detailPageUrl, 'imageURLs'=>$imageUrls, 'authors' => $authorNames, 'actors' => $actorNames);
// ↑ここまで↑

amazon-reloaded.js

var $newRow = jQuery('#arfw-result-template').clone().attr('id','arfw-result-' + this['asin']).addClass('arfw-result');
 $newRow.html($newRow.html().replace(/%ASIN%/g,this['asin']).replace(/%NAME%/g,this['name']).replace(/%DETAIL_PAGE_URL%/g,this['detailPageURL']).replace(/%IMG_SRC%/g,this['imageURLs'][0]).replace(/%IMG_SRC_MED%/g,this['imageURLs'][1]).replace(/%IMG_SRC_LRG%/g,this['imageURLs'][2]));
// ↓ここから↓
 if (this['authors']) {$newRow.html($newRow.html().replace(/%AUTHOR%/g,this['authors'].join(', ')));}
 if (this['actors']) {$newRow.html($newRow.html().replace(/%ACTOR%/g,this['actors'].join(', ')));}
// ↑ここまで↑

meta-box.php

<td>
 <p></p><a target="_blank" href="%DETAIL_PAGE_URL%">%AUTHOR%『%NAME%』%ACTOR%</a> <a href="%DETAIL_PAGE_URL%"><?php _e( 'Send Link to Editor' ); ?></a></p>
</td>

なんて感じで、%AUTHOR%と%ACTOR%で著者と出演者をテンプレートに埋め込めるようにした。

Published At2010-05-11 10:01Updated At2019-12-30 15:34

技術日記
tDiaryからMT形式で出力するスクリプトEdit

tDiaryからWordPressにデータをインポートしようとして、

とかを試してみたんだけど、うまくいかなかったんで適当にでっち上げた。コメントとかトラックバックには対応していないし、内部リンクとかの解決もしてない。なぜか画像へのリンクだけは適当に変換かけてある。

HTTPでがしがしアクセスしまくるんで、共有サーバーとかでそのまま実行しちゃだめ。自力で安全に使用できそうな人は参考にどうぞ。

#!/usr/bin/php
&lt;?php

define('DEBUG', false);
mb_internal_encoding('utf-8');

$author = 'ishinao';
$baseUrl = 'http://tdiary.ishinao.net/';
$dailyPattern = 'Ymd.\h\t\m\l';
$sourceImagePath = 'images/';
$destImagePath = '/wp-content/';

$startDate = '2009-10-29';
$nextDate = $startDate;

while ($nextDate = processDay($nextDate)) {
}

function processDay($date)
{
 global $baseUrl, $dailyPatten;
 $url = getDailyUrl($date);
 debug($url);
 $html = file_get_contents($url);

 $body = getBodyHtml($html);
 //debug($body);

 $sections = splitSections($body);
 $sectionInfo = array();
 foreach ($sections as $section) {
 //debug($section);

 $sectionInfo[] = array(
 'html' =&gt; $section,
 'body' =&gt; cleanupSection($section),
 'title' =&gt; getTitle($section),
 'category' =&gt; getCategory($section),
 );
 }
 foreach ($sectionInfo as $section) {
 printSection($date, $section);
 }

 $nextDate = getPrevLink($html);
 debug($nextDate);

 return $nextDate;
}

function printSection($date, $section)
{
 global $author;
 $title = $section['title'];
 $date = date('m/d/Y h:i:s A', strtotime($date));
 $body = $section['body'];
 $categories = $section['category'];
 echo &lt;&lt;&lt;EOD
AUTHOR: $author
TITLE: $title
STATUS: Draft
ALLOW COMMENTS: 0
CONVERT BREAKS: __default__
ALLOW PINGS: 0

EOD;
 foreach ($categories as $category) {
 echo 'CATEGORY: ' . $category . PHP_EOL;
 }
 if (!empty($categories)) {
 echo 'PRIMARY CATEGORY: ' . $categories[0] . PHP_EOL;
 }

 echo &lt;&lt;&lt;EOD
DATE: $date
-----
BODY:
$body

-----
EXTENDED BODY:

-----
EXCERPT:

-----
KEYWORDS:

-----

--------

EOD;
}

function cleanupSection($html)
{
 global $baseUrl, $sourceImagePath, $destImagePath;

 $result = trim($html);
 $result = preg_replace('#^&lt;div&gt;#', '', $result);
 $result = preg_replace('#&lt;/div&gt;$#', '', $result);
 $result = preg_replace('#&lt;h3&gt;.+?&lt;/h3&gt;#', '', $result);
 $result = preg_replace('#&lt;a name="p\d+".+?&lt;/a&gt;#', '', $result);
 $result = preg_replace('/(' . preg_quote($baseUrl, '/') . ')?' . preg_quote($sourceImagePath, '/') . '/', $destImagePath, $result);
 return trim($result);
}

function getCategory($html)
{
 $result = array();
 if (preg_match('#&lt;h3&gt;(.+?)&lt;/h3&gt;#', $html, $matches)) {
 $title = preg_replace('#&lt;span&gt;.+?&lt;/span&gt;#', '', $matches[1]);
 $title = trim(strip_tags($title));
 if (preg_match_all('#(\[[^\]]+\])#', $title, $matches)) {
 foreach ($matches[1] as $match) {
 $category = trim($match, '[] ');
 $result[] = $category;
 }
 }
 }
 array_unique($result);
 return $result;
}

function getTitle($html)
{
 if (preg_match('#&lt;h3&gt;(.+?)&lt;/h3&gt;#', $html, $matches)) {
 $title = preg_replace('#&lt;span&gt;.+?&lt;/span&gt;#', '', $matches[1]);
 $title = trim(strip_tags($title));
 $title = preg_replace('#^(\[[^\]]+\]\s*)+#', '', $title);
 return $title;
 } else if (preg_match('#title="(.+?)"#', $html, $matches)) {
 return $matches[1];
 }
}

function splitSections($html)
{
 $result = array();
 $splitter = '&lt;div&gt;';
 $splits = explode($splitter, $html);
 if (count($splits) &gt; 1) {
 array_shift($splits);
 $splits[count($splits) - 1] = preg_replace('#&lt;/div&gt;\s*$#', '', $splits[count($splits) - 1]);
 }
 foreach ($splits as $split) {
 $split = trim($split);
 if ($split == '') {continue;}
 if (trim(strip_tags($split)) == '') {continue;}
 $result[] = $splitter . $split;
 }
 return $result;
}

function getBodyHtml($html)
{
 if (preg_match('#(&lt;div&gt;.+&lt;/div&gt;\s*)&lt;div&gt;#ims', $html, $matches)) {
 return $matches[1];
 }
}

function getPrevLink($html)
{
 if (preg_match('#&lt;link rel="prev".+?href="(.+?)".*?&gt;#', $html, $matches)) {
 $link = $matches[1];
 if (preg_match('#(\d{4})(\d{2})(\d{2})#', $link, $matches)) {
 return $matches[1] . '-' . $matches[2] . '-' . $matches[3];
 }
 }
 return null;
}

function getDailyUrl($date)
{
 global $baseUrl, $dailyPattern;
 return $baseUrl . date($dailyPattern, strtotime($date));
}

function debug($string)
{
 if (DEBUG) {echo $string . PHP_EOL . str_repeat('-', 50) . PHP_EOL;}
}

Published At2010-05-07 15:15Updated At2019-12-30 15:35

技術日記
phpmyadmin 3系とmysql 5.0系 on windowsEdit

windows(xp or vista)で、xamppのphpmyadminとmysqlを使ってruby on rails(2.3.4)からmysqlを使おうとしたら、gemでインストールしたruby(1.8.7)のmysqlドライバがmysql 5.1で使おうとすると落ちる。どうやらmysql 5.0ならOKらしい。

しょうがないんで、xamppのmysql 5.1系は削除して、mysql 5.0系を入れることにしたんだけど、ちょうどtritonnのwindowsバイナリが5.0系だったんで、それを入れることにした。

すると、phpmyadminが「show pluginsがない」というエラーを出して動かないようになってしまった。

どうやら"show plugin"だか"show plugins"だかは、mysql 5.1以降で追加されたコマンドなんだけど、phpmyadmin 3系の新しいやつはそれを問答無用で使っちゃってるんでエラーが出るらしい。

phpmyadminのソースをgrepしてみたら、"show plugins"を使っているのはlibraries/blogstreaming.lib.phpしか見あたらなかった。

どう直すのが妥当なのかよくわからないけど、どうせサポートしてない機能の情報を得ようとしているんだから、"select null"にしておいた。

副作用があるかどうかは知らん。

Published At2009-10-29 00:00Updated At2019-12-30 23:54

技術日記
vistaでmysqldをサービス登録するEdit

tritonnのWindowsバイナリをd:\mysqlにコピーして、

d:\mysql\bin\mysqld-net --install mysql

とかしようと思ったら、

Install/Remove of the Service Denied!

とかでてサービス登録できない。なぜかと思ったらvistaのUACのせいらしい。コマンドプロンプト自体を明示的に「管理者として実行」して、「管理者:コマンドプロンプト」から実行したら、ふつうに登録できた。

vistaのUACは、自分が管理者アカウントでログインしているときでも、こうやって明示的に「管理者として実行」しないといけないときがあるけど、その仕組みがよくわからん。

で、サービスとして登録できたから、

net start mysql

とかしたらエラーが出て止まる。イベントログを見ると

Can't find messagefile 'C:\mysql\share\english\errmsg.sys'

とか書いてあって、どうやらデフォルトでc:\mysqlを見に行っているせいらしい。コンパネのサービスからmysqlの起動オプションに

--basedir=d:/mysql

とかつけたらようやく動きだした。

コンパネから起動オプションを追加しただけだと

再起動したら起動オプションを忘れてしまうらしい。いったんmysqlサービスを終了してから、

mysqld-nt --remove mysql

してサービスを削除し、

mysqld-nt --install MySQL --basedir=d:/mysql

みたいに起動オプション付きでサービス登録した方がよさそう。

Published At2009-10-29 00:00Updated At2019-12-30 23:59

技術日記
Zend_Db_Tableを使った複数DB接続Edit

Zend_Db_Tableを使ったモデルクラス周りは、Zend Frameworkクイックスタート モデルとデータベーステーブルの作成に書かれていたようにTable Data Gatewayパターンを使って書くとして、そういう場合にレプリケーション構成の複数DBを使い分ける(selectを複数のスレーブに分散させる)のはどうやって書けばいいのか考えてみた。 最近はミドルウェアレイヤーでその辺に対応するための情報も増えてきているんで、本格的に対応するならばそういうのを使った方がいいような気もするけど、PHPロジックレイヤーで対応する方法もあってもいいだろう。 実際にコードを動作させてみたわけじゃないんだけど、たぶんこんな感じで動くんじゃないかと思われる擬似コードを書いてみる。

application.ini(の一部)

# マスターDB設定
db.adapter = PDO_MySQL
db.params.host = ****
db.params.username = ****
db.params.password = ****
db.params.dbname = ****
# スレーブDB設定1
slaveDbs.slave1.adapter = PDO_MySQL
slaveDbs.slave1.params.host = ****
slaveDbs.slave1.params.username = ****
slaveDbs.slave1.params.password = ****
slaveDbs.slave1.params.dbname = ****
# スレーブDB設定2
slaveDbs.slave2.adapter = PDO_MySQL
slaveDbs.slave2.params.host = ****
slaveDbs.slave2.params.username = ****
slaveDbs.slave2.params.password = ****
slaveDbs.slave2.params.dbname = ****

Bootstrapクラス(の一部)

class Bootstrap extends Zend_Applicaiton_Bootstrap_Bootstrap
{
protected function _initDb()
{
$options = new Zend_Config($this->getOptions());
$masterConfig = $options->db;
$masterDb = Zend_Db::factory($masterConfig->adapter, $masterConfig);
Zend_Registry::set('db', $masterDb);
$slaveDbs = array();
$slaveConfigs = $options->slaveDbs;
foreach ($slaveConfigs as $configName => slaveConfig) {
$slaveDbs[$configName] = Zend_Db::factory($slaveConfig->adapter, $slaveConfig);
}
Zend_Db_Table::setDefaultAdapter($masterDb);
Zend_Registry::set('slaveDbConnections', $slaveDbs);
return $masterDb;
}
}

Zend_Db_Tableを使ったDBアクセスクラス

class My_DbTable_Foo extends Zend_Db_Table
{
protected $_name = 'foo';
}

マッパークラス

class My_Mapper_Foo
{
protected $_masterDbTable;
protected $_slaveDbTable;
// バックエンドにマスターDBを使ったend_Db_Tableオブジェクト
public function getMasterDbTable()
{
if (null == $this->_masterDbTable) {
$this->_masterDbTable = new My_DbTabel_Foo();
}
return $this->_masterDbTable;
}
// バックエンドにスレーブDBを使ったZend_Db_Tableオブジェクト
public function getSlaveDbTable()
{
if (null == $this->_slaveDbTable) {
// スレーブDBの中からランダムに一つのコネクションを選択する
$slaveDbs = Zend_Registry::get('slaveDbConnections');
$slaveDb = array_rand($slaveDbs);
$this->_slaveDbTable = new My_DbTable_Foo(array('db' => $slaveDb));
}
return $this->_slaveDbTable;
}
public function find($fooId, $foo)
{
// プライマリーキーからのデータ取得はマスターDBから
$row = $this->getMasterDbTable()->find($fooId)->current();
// 中略
return $foo;
}
public function save($foo)
{
$fooId = $foo->getFooId();
$data = array(
// $dataに$fooの各値をセット
);
if (is_null($fooId)) {
// 更新処理はマスターDBに
$this->getMasterDbTable()->insert($data);
} else {
// 更新処理はマスターDBに
$this->getMasterDbTable()->update($data, array('foo_id = ?', $fooId);
}
}
public function searchBySomeConditions($someConditions)
{
// 複雑な検索処理などはスレーブDBを使う
$rowset = $this->getSlaveDbTale()->fetchAll($someConditions);
// $rowsetを汎用形式$resultに変換
return $result;
}
}

モデルクラス

class My_Foo
{
protected $_mapper;
public funstion getMapper()
{
if (null == $this->_mapper) {
$this->_mapper = new My_Mapper_Foo();
}
return $this->_mapper;
}
public function save()
{
return $this->getMapper()->save($this);
}
public function find($fooId)
{r
return $this->getMapper()->find($fooId, $this);
}
public function searchBySomeConditions($someConditions)
{
return $this->getMapper()->searchBySomeConditions($someConditions);
}
}
標準のリソースと組み合わせて使うならもうちょい練った方がよさそうだ。とか。スレーブを選択するロジックをもうちょい融通を利かせられるようにした方がいいかも。とか、考え始めるといろいろ気になりはじめるんだけど、雰囲気としてはこんな感じの書き方で、Zend_Db_Table自体に手を加えずに、クエリーごとに接続するバックエンドDBを切り替えることができそうだよね。

Published At2009-08-05 09:00Updated At2019-12-30 23:54

技術日記
テストの書き方Edit

zfコマンドを使ってアプリケーションのスケルトンを生成すると、testsディレクトリが生成され、
tests
|-- application
|   `-- bootstrap.php
|-- library
|   `-- bootstrap.php
`-- phpunit.xml
みたいな配置になっているんだけど、そこにどんな感じで記述すればいいのかよくわからない。phpunit.xmlが1個しかないのに、applicationとlibraryにそれぞれbootstrap.phpがあるって、どういう感じで使うイメージなんだろう? 公式ドキュメントのテスト周りの部分とか、具体的な手順を説明している「Set up a Zend Framework application using Zend_Application (including PHPUnit setup) - mafflog」とかを参考に、コントローラのテストを追加してみた。applicationとlibraryの分かれている部分をどうやって活かすイメージなのかよくわからなかったんで、試行錯誤しつつ。ひとまずはこんな感じになった。 基本的には、「Set up a Zend Framework application using Zend_Application (including PHPUnit setup) - mafflog」で紹介されているやり方をほぼそのまま使いつつ、PHPUnitのブートストラップファイルは、tests/bootstrap.phpとして記述。その中から、tests/application/bootstrap.phpとtests/library/bootstrap.phpをそれぞれ読み込んでいる。つまり、共通のブートストラップはtests/bootstrap.phpに、application、libraryに特化したブートストラップは各ディレクトリ以下に書くイメージ。 主だった部分をコピペしておくと、

tests/phpunit.xml

<phpunit bootstrap="bootstrap.php" colors="true">
<testsuite name="NetJockey Application">
<directory>.</directory>
</testsuite>
</phpunit>

tests/bootstrap.php

<?php
define('BASE_PATH', realpath(dirname(__FILE__) . '/../'));
define('APPLICATION_PATH', BASE_PATH . '/application');
// Include path
set_include_path(
'.'
. PATH_SEPARATOR . BASE_PATH . '/library'
. PATH_SEPARATOR . get_include_path()
);
// Define application environment
define('APPLICATION_ENV', 'testing');
require_once 'application/bootstrap.php';
require_once 'library/bootstrap.php';

tests/application/bootstrap.php

<?php
require_once dirname(__FILE__) . '/controllers/ControllerTestCase.php';

tests/application/controllers/ControllerTestCase.php

<?php
require_once 'Zend/Application.php';
require_once 'Zend/Test/PHPUnit/ControllerTestCase.php';
abstract class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
public $application;
public function setUp()
{
$this->application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$this->bootstrap = array($this, 'appBootstrap');
parent::setUp();
}
public function appBootstrap()
{
$this->application->bootstrap();
}
}

tests/application/controllers/IndexControllerTest.php

<?php
class IndexControllerTest extends ControllerTestCase
{
public function testIndexAction()
{
$this->dispatch('/');
$this->assertRoute('top');
$this->assertController('index');
$this->assertAction('index');
$this->assertResponseCode('200');
$this->assertQueryContentContains('title', 'NetJockey');
}
public function testErrorURL()
{
$this->dispatch('/not/exists/path/');
$this->assertRoute('default');
$this->assertController('error');
$this->assertAction('error');
$this->assertResponseCode('404');
}
}
コントローラ用のテストケースのベースクラスを作ったりする部分は、「Set up a Zend Framework application using Zend_Application (including PHPUnit setup) - mafflog」の内容をそのまま借用。Zend Frameworkには専用のテストクラスとかも追加されていたんで。MVC周りのテストがナチュラルに書けて結構便利そう。コントローラ(MVC)周り以外のテストに関しては、ふつうにPHPUnitらしいテストを書いていけばいいんだろう。

Published At2009-07-24 09:00Updated At2019-12-30 23:54

技術日記
[開発] 基本的なブートストラップリソースの組み込みとレイアウトの適用Edit

別件でZend Framework 1.8をいろいろいじっているうちに、こっちをどういう順番でいじっていくか迷いが深くなってしまい、悪い方向に煮詰まってきてしまったので、頭を使わなくても済むレベルのところから手をつけることにした。 作業内容としては、更新履歴r3r15のあたり。
  • 今後のアップデートなどのことを考えて、application.iniをapplication.ini.orgに変更。動かしたい場合は、必要に応じてapplication.iniにリネームしつつ、内容を環境に合わせて修正を。
  • ZFが自動生成するerror.phtmlでショートタグを使っているので、それを除去しつつ日本語化。あとdoctypeとかをテンプレートに直書きしていたのを、ビューヘルパーを使うように修正。
  • ブートストラップ内の初期化コードとして、
    • Viewの初期化処理
    • クラスのオートローダーの初期化処理(NetJockey_というネームスペースのクラスもオートローダーの対象として追加)
    • アプリケーションローカルのモジュール(application/modelsとか)のオートローダー初期化処理。ローカルモジュールのネームスペースはMyにして、Zend_Db_TableによるORMはMy_DbTableに、マッパーはMy_Mapperに。
    • 設定ファイルをあちこちで使い回しやすくするために、配列からZend_Config形式に変換しつつ、$bootstrap->getResource('config')とかZend_Registry::get('config')とかで取得できるように初期化。
    • オートローダーの組み込みキャッシュ(要は読み込んだファイルをまとめてrequire_onceするphpファイルの生成)やZend_Db_Tableのスキーマキャッシュを行うためのキャッシュ関連初期化処理。キャッシュファイルの置き場も用意。キャッシュファイルの場所は設定ファイルに記述。
  • 標準のDBリソースを使ったDB設定の追加。
  • ビュー内でecho $this->escape()するのはいやなので、ちょっとでも短くするために$this->h()というビューヘルパーを追加。
  • Yahoo UI LibraryのGridsを使ったレイアウトファイルを用意し、Zend_Layoutを使ってレイアウト処理を行うように設定&ファイルを用意。ZFのサンプルとかだとレイアウトファイル置き場はapplication/layouts/scriptsとかになっているけど、気に入らないんでapplication/views/layoutsにしてみた。標準的なお作法と違うので注意。
実際の動作イメージは以下な感じ。 f:id:netjockey:20090717193725p:image DIV構造は以下な感じ。横幅ブラウザ依存でサイドバーが240pxのやつ。 f:id:netjockey:20090717194226p:image 今のところ、ヘッダ、サイドバー、フッターはそれぞれviews/scripts/partial/の中にパーツテンプレートして放り込んであるけれども、その内容にロジックが発生する場合はこういうやり方ではなく、ロジックをアクションスタックに詰んで、それぞれのレイアウト要素をプレースホルダーごとにレンダリングして……、ってやり方にする必要が出てくると思われる。が、本当にそれでいけるのか、いろいろ試してみないとわからない。 モデル周りの作り方もひとまずはドキュメント通りにやってみようと思っているんだけど、最初のうちはかなり実験的に試行錯誤してみるしかないかなー。単に動くものを作るだけだったら何とでもなるんだけど、ZFらしい標準的なやり方ってのはライブラリのソースとそういう実装に至る思想を一通りかみ砕きつつ、さらに実践で2、3回痛い目にあって学習しないと身につかなそうだ。ここ最近(1年くらい)のZF関連のドキュメントも全然見てなかったからなー。追いつくのがきつい。

Published At2009-07-17 09:00Updated At2019-12-30 23:54

技術日記
[開発] まずはプロジェクトスケルトンからEdit

Zend Framework 1.8を使ったアプリケーションの標準的な作り方がだいぶわかってきたんで、早速zfコマンドを使ってプロジェクトスケルトンを生成させてみた。んでもって、publicディレクトリをバーチャルホストとして、dev.netjockey.jpという名前*1でマウントして、こんな感じのデフォルト画面が出るところまで。 f:id:netjockey:20090703164728p:image さて、ここからどういう風に開発を進めていくべきか。だいたいの仕様ができているモデル周りは比較的手を付けやすいけれども、その辺は時間がかかる割には動かせるものが何もなくてつまらない*2。実際にいくつかの簡単なページから作ってみるかと思っても、まだデザインをどうするか決めていないんで手を出しにくい。 ひとまずは、最終的に作るアプリケーションの一部として使えるコードを書くんじゃなくて、Zend Framework 1.8に準拠したアプリケーションの練習的なコードをいくつか書いてみようかな。フォーム、モデル、フィルター、バリデータのコンビネーションあたりを、Zend_Applicationベースで書くとどういう感じになるのか早めに実践しておきたいし。 ユーザー登録とかログインあたりが、それっぽいコンポーネントを使えていいかな?

*1:hostsファイルに書いただけでlocalhost上にある

*2:モデルを作っただけで動かせるものと言ったらユニットテストくらいか

Published At2009-07-03 09:00Updated At2019-12-30 23:59