tag:blogger.com,1999:blog-78070659040825829272024-02-07T12:06:55.277+09:00ユニークビジョン技術ブログaoyagikouheihttp://www.blogger.com/profile/02332627244925169468noreply@blogger.comBlogger12125tag:blogger.com,1999:blog-7807065904082582927.post-74277375854345330782013-06-14T19:01:00.002+09:002013-06-14T19:01:36.680+09:00VirtualBox で「なんちゃってステージング環境」のススメ山本です。<br />
<br />
負荷分散のある環境で試したいけれど、開発環境はマシン1台しかない!<br />
でもステージング環境で試すのはまだ怖い…<br />
そんなときは、 VirtualBox を使って手元にステージングもどき環境を作ってしまうといいかも知れません。<br />
<h2>
ポイント</h2>
<ul>
<li>本番環境と似たサーバネットワークを、社内環境から隔離したかたちで手元に用意するのが目標<br />↑気軽に負荷分散環境での動作をシミュレート</li>
<li>簡単にバーチャルマシンのコピーが作れるから、OSインストールなどの共通作業だけ先に済ませておける</li>
</ul>
<h2>
事前設定</h2>
サーバネットワークに使うホストオンリーネットワークを作っておく。<br />
Oracle VM VirtualBox マネージャー(システムツール/Oracle VM VirtualBox)から<br />
「ファイル」→「環境設定」→「ネットワーク」タブ<br />
→「+」(追加)アイコン<br />
→「(ドライバーの絵)」(編集)アイコン<br />
→IPアドレスとネットワークマスクを入力して「OK」<br />
→「OK」<br />
<br />
<h2>
共通作業</h2>
<br />
<h3>
OSインストール</h3>
(省略;ISOイメージを光学メディアとして直接接続できるので活用!)<br />
<br />
<h3>
事前ダウンロード</h3>
aptitude install -d パッケージ名… で、インストールする予定のあるパッケージをあらかじめダウンロードだけしておけるので、これも活用すると便利。<br />
<h3>
準備できたら</h3>
一旦仮想マシンをシャットダウンします。<br />
<h2>
個別マシン作成</h2>
元ネタの仮想マシンを右クリック→「clone」<br />
<ul>
<li>「Reinitialize the MAC address of all network cards」のチェックを忘れずに!</li>
<li>「Full Clone」を選択←元ネタにクローン側で行った変更が伝染しないようにするため</li>
</ul>
クローン作成にはホストのディスクI/O性能によって相応の時間がかかります。<br />
(山本のマシンだと、8Gのイメージをクローンするのに3分弱かかりました。) <br />
<h3>
起動する前にやること</h3>
<h4>
ネットワーク設定(ハード?側) </h4>
できたクローンを右クリック→「設定」→「ネットワーク」タブ<br />
<br />
<ul>
<li>「アダプタ1」タブを選択 (こいつが仮想マシンの eth0; apt-get 用+ホストからのアクセス用)</li>
<ul>
<li>「割り当て」→「NAT」←ブリッジだと無線接続時うまく動かない</li>
</ul>
</ul>
<ul>
<li>「アダプタ2」タブを選択 (これは仮想マシンの eth1; 模擬サーバネットワークにつなげるため)</li>
<ul>
<li>ネットワークアダプタを有効化</li>
<li>「割り当て」→「ホストオンリー アダプタ」</li>
<li>「名前」→事前準備で作ったホストオンリーネットワークを選択<br />↑これで模擬サーバネットワークにつなげる</li>
<li>「高度」→「アダプタタイプ」→「準仮想化ネットワーク (virtio-net)」<br />↑パフォーマンス(10倍くらい違うらしいです)</li>
</ul>
</ul>
<br />
<h2>
いざ起動!</h2>
初回はネットワーク設定がされていないので、DHCPのタイムアウトで待たされる。<br />
辛抱強く待ちましょう。<br />
<h3>
ネットワーク設定(ゲストOS側; Ubuntuゲストの例)</h3>
Ubuntu、というかいまどきのLinuxは、MACアドレスでインタフェース名を固定する機能があるが、クローンを作る場合はそれが障害になる。<br />
初回起動時に待たされる原因のほとんどはこれが原因です。 <br />
<ul>
<li>/etc/udev/rules.d/70-persistent-net.rules 書き換え<br />NAME="eth0" の行を削除<br />NAME="eth1" の行を書き換え→ NAME="eth0" に<br />NAME="eth2" の行を書き換え→ NAME="eth1" に</li>
<li>/etc/network/interfaces 追記<br />auto eth1<br />iface eth1 inet static<br /> address 10.0.0.11<br /> netmask 255.255.0.0<br />↑IPアドレスとネットワークマスクは適切なものに読み替えて</li>
<li>再起動!</li>
</ul>
<ul>
</ul>
<h3>
ミドルウェアその他のインストール&設定</h3>
個別にインストール and/or 設定が必要なものは各プロジェクトのインフラ情報を参考に。<br />
<h2>
その他</h2>
<h3>
起動したままの状態で一時停止</h3>
仮想マシンのウィンドウを「閉じる」→「仮想マシンの状態を保存」を選択<br />
→ハイバネーションに似た状態になります。<br />
<h3>
スナップショット</h3>
「在りし日の仮想マシン 」として、大規模変更前などに仮想マシンの状態を記録しておくことができます。<br />
「Oracle VM VirtualBox マネージャー 」ウィンドウで、仮想マシンのアイコンを選択した状態で「スナップショット」タブを表示、カメラのようなアイコンを叩くとスナップショットが作成できます。<br />
作ったスナップショットに仮想マシンの状態を巻き戻したり、クローンの仮想マシンを新たに起こしたりできます。<br />
<h3>
サーバネットワークのネットワーク経路が入らない?!</h3>
原因・再現性とも不明ながら、 eth1 のネットワークのための経路が入らない、もしくは何らかの理由で消えてしまうという現象がおこることがあるようです。<br />
「ゲストOSからホストOSへの ping は通るのに、ゲストOS同士や、ホストからゲストOSへの ping が通らない」のような奇妙な現象として観測されることも。<br />
手動でネットワーク経路を追加してやることで、とりあえず修復できます。<br />
<ul>
<li>sudo route add -net 10.0.0.0/16 gw 10.0.0.11 </li>
</ul>
Anonymoushttp://www.blogger.com/profile/00618575252116270595noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-22680476713314918152013-06-05T19:21:00.002+09:002013-06-05T19:21:52.580+09:00Zend Framework 2とDependency Injection高瀬です。<br />
<br />
PHP歴約1年、まだYii Frameworkでしか開発出来ないので、少しは他の開発手法もやってみようと思う。そこで、ユニークビジョンでは馴染みがあるようなないようなZend Frameworkに注目。<br />
<br />
<h3>
Zend Frameworkについて</h3>
<br />
<a href="http://sourceforge.jp/magazine/12/09/06/2359209" target="_blank">米Zend、PHPアプリフレームワーク「Zend Framework 2.0」をリリース</a> (2012年9月7日)<br />
<br />
バージョン1.0のリリースは2007年、2012年9月にバージョン2.0がリリースとのこと。2013年6月時点のバージョンは2.2。<br />
<br />
数あるPHPフレームワークの中で、Zemd Frameworkの人気はどうだろうか。<br />
<br />
<a href="http://ameblo.jp/rb26/entry-11453375241.html" target="_blank">PHPの4大フレームワークの人気を比較</a> (2013年1月21日)<br />
<br />
4大フレームワークと呼ばれるものの中では、Zend Frameworkはいまひとつの様子。<br />
<br />
さて、バージョンアップでどのように変わったのか。まずはZend Framework開発メンバーの方が作成されたこちらを参照。<br />
<br />
<a href="http://www.mwop.net/slides/2012-06-07-Zf2-Module-Workshop/Zf2ModuleWorkshop.html#slide1" target="_blank">Creating Re-usable Modules for Zend Framework 2</a> (2012年6月?)<br />
<br />
これによると、3つのコンセプトがあるという。<br />
<br />
Three core concepts<br />
<ul>
<li>Decoupled and DI-driven (ServiceManager, Di)</li>
<li>Event-driven (EventManager)</li>
<li>HTTP handlers (DispatchableInterface)</li>
</ul>
<br />
<div>
英語で書かれているからということを差し引いても、よくわからない。では、少し観点が違うけれども、日本の方が書かれたこちらも参照。</div>
<div>
<br /></div>
<div>
<a href="http://www.slideshare.net/nully/zend-framework2zf1zf2" target="_blank">Zend Framework2について〜ZF1とZF2の違いについて〜</a> (2013年2月23日)</div>
<div>
<br /></div>
<div>
結局よくわからないが、先ほどの資料と共通しているのは依存性の扱いが変わったことと、イベントという機能が追加されたことか。これは本家のFAQにも書かれている。</div>
<div>
<br /></div>
<div>
<a href="http://framework.zend.com/about/faq/" target="_blank">Frequently Asked Questions About ZF2</a></div>
<div>
<br /></div>
<div>
ここでは依存性の扱いについて見ていくことにする。<br />
<br />
<h3>
チュートリアルの実施</h3>
<br />
では、Zend Framework 2を使ってみよう。インストールは、スケルトンアプリケーションをダウンロードし、composerで関連モジュールを取り込む、という手順になる。チュートリアルのページを参照。<br />
<br />
<a href="http://framework.zend.com/manual/2.2/en/user-guide/skeleton-application.html" target="_blank">Getting started: A skeleton application</a><br />
<br />
上記のページに、php composer.phar create-project --repository-url=... のコマンドでインストールするとあるが、LinuxとWindowsのどちらで試してもエラーが発生し、インストールができなかった。<br />
<br />
しかたがないので、GithubからZendSkeletonApplication-master.zipをダウンロードし、解凍する。</div>
<div>
<br /></div>
<div>
解凍したら、composerで関連モジュールを取り込む。これにより、Zend Framework 2本体もインストールされる。<br />
<br />
php composer.phar self-update<br />
php composer.phar install<br />
<br />
nginxの設定は以下のとおり。<br />
<br />
server {<br />
root /usr/share/nginx/www/workspace/zend/public;<br />
index index.html index.htm index.php;<br />
server_name localhost;<br />
<br />
location / {<br />
try_files $uri $uri/ /index.php$is_args$args;<br />
}<br />
<br />
location ~ \.(php|phtml)?$ {<br />
fastcgi_param PATH_INFO $fastcgi_path_info;<br />
fastcgi_pass 127.0.0.1:9000;<br />
fastcgi_index index.php;<br />
include fastcgi_params;<br />
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;<br />
fastcgi_param APPLICATION_ENV development;<br />
}<br />
}<br />
<div>
<br /></div>
<div>
設定できたら、http://lcoalhost/ でアクセスする。スケルトンアプリケーションの画面が表示されたら成功。なお、nginxの設定の問題ではないかと思うが、http://localhost/index.php でアクセスすると、「Page not found.」のエラーとなった。<br />
<br />
チュートリアル自体は、ソースコードをコピー&ペーストしながら読み進めていくと、データベースへのデータの登録、更新、取得、削除が一通りできる内容になっている。<br />
<br />
「Using ServiceManager to configure the table gateway and inject into the AlbumTable」の手順で、データベースへのアクセスを行うTableGatewayやAdapterのインスタンスを用意しているあたりがDependency Injectionの書き方になっている。<br />
<br />
チュートリアルで作成したプログラムが動作したら、Dependency Injectionについて調べてみよう。<br />
<br />
<br />
<h3>
Dependency Injection</h3>
<br />
自分がDependency Injection(以下、DI)という言葉を知ったのは最近だが、これは別に新しい技術ではなく、検索すると2005年ころの記事が多く見つかる。<br />
<br />
<a href="http://itpro.nikkeibp.co.jp/free/ITPro/OPINION/20050216/156274/" target="_blank">Java開発を変える最新の設計思想「Dependency Injection(DI)」とは</a> (2005年2月18日)<br />
<br />
上記のページから一部抜粋。<br />
----------<br />
DIを実現するメカニズムの概要は、オブジェクト相互の依存性をプログラム外部に記述して、実行時に結合することである。これにより、オブジェクト間の独立性が高まる。<br />
<br />
----------<br />
<div>
<br /></div>
<br />
さて、何のことだかわからないが、自分の認識では以下のような内容。<br />
<br />
<br />
<div>
「ソースコードの中にクラス名を直接書いていると、そのクラスを拡張した派生クラスに差し替えようと思うとあちこち修正しなければならなかったり、開発環境と本番環境でクラスを切り替えたり、というようなことがやりにくい。そこで、ソースコード中には仮のクラス名を書いておき、実際にインスタンスを作るクラスの名前は設定ファイルに書くことにする。」</div>
<br />
<br />
<br />
<div>
Zend Framework 2ではこれを以下のページの説明のように実現している。</div>
</div>
</div>
<div>
<br /></div>
<div>
<a href="http://framework.zend.com/manual/2.2/en/tutorials/quickstart.di.html" target="_blank">Learning Dependency Injection</a></div>
<div>
<br /></div>
<div>
簡単な例が最初に示されている。A、Bという2つのクラスがあったとして、Bはコンストラクタの引数にAのインスタンスを受け取る。すなわち、Bのインスタンスを生成する構文は以下のようになる。</div>
<div>
<br /></div>
<div>
$b = new B(new A());</div>
<div>
<br /></div>
<div>
newが2回も登場するので、Aを差し替えるときも、Bを差し替えるときもソースコードの修正が面倒そうだ。そこで、newを使わずにそれぞれのインスタンスを生成させてみよう。</div>
<div>
<br /></div>
<div>
クラスBのコンストラクタの定義部分で、引数に型を書いておく。</div>
<div>
<br /></div>
<div>
class B {</div>
<div>
<div>
public function __construct(<span style="color: #274e13;">A</span> $a) { ...</div>
</div>
<div>
<br /></div>
<div>
そのうえで、以下のように記述する。</div>
<div>
<br /></div>
<div>
<div>
$di = new Zend\Di\Di;</div>
<div>
$b = $di->get('B');</div>
</div>
<div>
<br /></div>
<div>
こうすると、$bにはBのインスタンスが代入されるが、その際に自動的にAのインスタンスも生成される。Zend\Di\Diクラスが必要なインスタンスの準備を肩代わりしてくれるのだ。少なくともこれで、クラスAの登場場面が減ったので、Aの差し替えが楽になる。</div>
<div>
<br /></div>
<div>
newが消えたとはいえ、Bの方は相変わらず直接名前が指定されている。$bに代入されるインスタンスの型を設定ファイルへ追い出すことができるかどうかはまだ勉強中。</div>
<div>
<br /></div>
<div>
<br /></div>
<h3>
実行環境による型の切り替え</h3>
<div>
<br /></div>
<div>
前述の例では設定ファイルが登場しなかったが、生成されるインスタンスの型を設定ファイルに追い出してみよう。</div>
<div>
<br /></div>
<div>
BookStoreというモジュールに、StoreControllerというコントローラがあるとする。本屋さんが仕入先の出版社から本を入荷する、と考えてみる。</div>
<div>
<br /></div>
<div>
<div>
<?php</div>
<div>
namespace BookStore\Controller;</div>
<div>
<br /></div>
<div>
use Zend\Mvc\Controller\AbstractActionController;</div>
<div>
use Zend\View\Model\ViewModel;</div>
<div>
use Zend\Di\Di;</div>
<div>
use Zend\Di\Config;</div>
<div>
<br /></div>
<div>
class StoreController extends AbstractActionController</div>
<div>
{</div>
<div>
public function indexAction()</div>
<div>
{</div>
<div>
$di = $this->getDi();</div>
<div>
$purchase = $di->get('BookStore\Model\Purchase');</div>
<div>
<br /></div>
<div>
return new ViewModel(array(</div>
<div>
'purchase' => $purchase,</div>
<div>
));</div>
<div>
}</div>
<div>
<br /></div>
<div>
protected function getDi()</div>
<div>
{</div>
<div>
// http://stackoverflow.com/questions/8957274/access-to-module-config-in-zend-framework-2</div>
<div>
$config = $this->getServiceLocator()->get('Config');</div>
<div>
<br /></div>
<div>
// http://www.eschrade.com/page/zf2-dependency-injection-managing-configuration/</div>
<div>
$diConfig = new Config($config['di']);</div>
<div>
<br /></div>
<div>
$di = new Di;</div>
<div>
$di->configure($diConfig);</div>
<div>
return $di;</div>
<div>
}</div>
<div>
}</div>
</div>
<div>
<br /></div>
<div>
Purchaseクラスは仕入先を表し、以下のように定義する。</div>
<div>
<br /></div>
<div>
<div>
<?php</div>
<div>
namespace BookStore\Model;</div>
<div>
<br /></div>
<div>
class Purchase</div>
<div>
{</div>
<div>
protected $publisher;</div>
<div>
<br /></div>
<div>
public function setPublisher($publisher)</div>
<div>
{</div>
<div>
$this->publisher = $publisher;</div>
<div>
}</div>
<div>
<br /></div>
<div>
public function getPublisher()</div>
<div>
{</div>
<div>
return $this->publisher->getName();</div>
<div>
}</div>
<div>
}</div>
</div>
<div>
<br /></div>
<div>
コンストラクタの引数にPublisherクラスのインスタンスを受け取る。Publisherクラスは以下のとおり。ついでにほぼ同様の内容でPublisherMockクラスも作っておく。</div>
<div>
<br /></div>
<div>
<div>
<?php</div>
<div>
namespace BookStore\Model;</div>
<div>
<br /></div>
<div>
class Publisher</div>
<div>
{</div>
<div>
protected $name;</div>
<div>
<br /></div>
<div>
public function __construct($name)</div>
<div>
{</div>
<div>
$this->name = $name;</div>
<div>
}</div>
<div>
<br /></div>
<div>
public function getName()</div>
<div>
{</div>
<div>
return $this->name;</div>
<div>
}</div>
<div>
}</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
ここで、このプログラムを本番環境で動作させる時はPublisherクラスを使用し、開発環境の場合はPublisherMockクラスを使用したいとする。nginxから受け取っている環境変数APPLICATION_ENVで実行環境は判定。設定は config/module.config.php と config/module.config.develop.php に書くことにする。</div>
<div>
<br /></div>
<div>
まず環境変数APPLICATION_ENVの値により読み取る設定ファイルを切り替えるようにしてみる。というか、マージさせてみる。</div>
<div>
<br /></div>
<div>
Module.phpに、設定ファイルのパスを記述している部分がある。</div>
<div>
<br /></div>
<div>
<div>
class Module</div>
<div>
{</div>
<div>
public function getAutoloaderConfig()</div>
<div>
{</div>
<div>
省略</div>
<div>
}</div>
<div>
<br /></div>
<div>
public function getConfig()</div>
<div>
{</div>
<div>
<span style="color: #274e13;">return include __DIR__ . '/config/module.config.php';</span></div>
<div>
}</div>
</div>
<div>
<br /></div>
<div>
これを、以下のように変更。config/module.config.phpを読み取ったうえで、環境変数の値に対応するファイルがあったらそれのファイルの内容をマージする。</div>
<div>
<br /></div>
<div>
<div>
public function getConfig()</div>
<div>
{</div>
<div>
$basePath = __DIR__ . '/config/';</div>
<div>
$config = include $basePath . '/module.config.php';</div>
<div>
<span class="Apple-tab-span" style="white-space: pre;"> </span> $mode = $_ENV['APPLICATION_ENV'];</div>
<div>
<br /></div>
<div>
if (!empty($mode)) {</div>
<div>
$filePath = $basePath . "/module.config.{$mode}.php";</div>
<div>
<br /></div>
<div>
if (file_exists($filePath)) {</div>
<div>
$overwrite = include $filePath;</div>
<div>
$config = array_merge($config, $overwrite);</div>
<div>
}</div>
<div>
}</div>
<div>
return $config;</div>
<div>
}</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
それから、config/module.config.phpに以下の内容を追加する。</div>
<div>
<br /></div>
<div>
<div>
<?php</div>
<div>
return array(</div>
</div>
<div>
<div>
'di' => array(</div>
<div>
'instance' => array(</div>
<div>
'BookStore\Model\Purchase' => array(</div>
<div>
'parameters' => array(</div>
<div>
'<span style="color: blue;">publisher</span>' => '<span style="color: #274e13;">BookStore\Model\Publisher</span>',</div>
<div>
),</div>
<div>
),</div>
<div>
'BookStore\Model\Publisher' => array(</div>
<div>
'parameters' => array(</div>
<div>
'name' => 'Amazon',</div>
<div>
),</div>
<div>
),</div>
<div>
),</div>
<div>
),</div>
</div>
<div>
:</div>
<div>
<br /></div>
<div>
さらに、config/module.config.development.php には以下の内容を書いておく。</div>
<div>
<br /></div>
<div>
<div>
<?php</div>
<div>
return array(</div>
<div>
'di' => array(</div>
<div>
'instance' => array(</div>
<div>
'BookStore\Model\Purchase' => array(</div>
<div>
'parameters' => array(</div>
<div>
'<span style="color: blue;">publisher</span>' => '<span style="color: #274e13;">BookStore\Model\PublisherMock</span>',</div>
<div>
),</div>
<div>
),</div>
<div>
'BookStore\Model\PublisherMock' => array(</div>
<div>
'parameters' => array(</div>
<div>
'name' => 'Amazon',</div>
<div>
),</div>
<div>
),</div>
<div>
),</div>
<div>
),</div>
<div>
);</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
以上のコードで、Purchaseクラスのインスタンスが作成されるとき、環境変数APPLICATION_ENVの値がdevelopmentでないときはコンストラクタの引数$publisherにPublisherクラスのインスタンスが注入され、developmentの場合はPublisherMockクラスのインスタンスが注入される。</div>
<div>
<br /></div>
<div>
ソースコードを1行も書き換えずに、環境変数の値だけで使用されるクラスを切り替えることができるようになった。</div>
<div>
<br /></div>
<div>
Zend Frameworkのサイトの説明だけでは分かりにくかったが、以下のページが参考になった。</div>
<div>
<br /></div>
<div>
<a href="http://www.eschrade.com/page/zf2-dependency-injection-managing-configuration/" target="_blank">ZF2 Dependency Injection: Managing Configuration – Part 1</a></div>
<div>
<br /></div>
<div>
<br /></div>
<h3>
終わり</h3>
<div>
<br /></div>
<div>
正直なところ、DIがどのような場面で活躍するのか今ひとつ想像できていない。逆にソースコードの流れが読み取りにくくなりそうで少々心配だが、それは自分が大規模なシステム開発をしたことがないためか。<br />
<br />
Zend Frameworkはまだチュートリアルくらいしか触っていないのでなんとも言えないが、DIやREST用コントローラ、コントローラの単体テストなどYiiでは提供されていなかったり、標準機能ではなかったりするものがあって、いろいろ勉強できそう。<br />
<br />
またPHPの4大フレームワークと呼ばれるものを一つも知らないし、最近はFuelPHPやLaravelなどの方が有名らしいので、順番に見ていき、比較ができるようになれればよいと思う。<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/14099319546219098184noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-65920202561826518582013-05-01T19:00:00.000+09:002013-05-01T19:00:18.923+09:00Slim Frameworkについて青柳です。<br />
Slim Frameworkについて紹介します。<br />
<br />
○機能<br />
<br />
ホームページで紹介されている機能は以下の通りです。<br />
■強力なルーター<br />
・標準と拡張できるHTTPメソッド<br />
・ワイルドカードや条件と一緒に使えるルートパラメーター<br />
・リダイレクト、パス、停止<br />
・ルートミドルウェア<br />
■拡張できるビューとテンプレートの表示<br />
■フラッシュメッセージ<br />
■AES256暗号化した安全なクッキー<br />
■HTTPキャッシュ<br />
■拡張できるログライター<br />
■エラーハンドリングとデバッグ<br />
■ミドルウェアとフックアーキテクチャ<br />
■簡単な設定<br />
<br />
○インストール<br />
・フォルダー構成<br />
app<br />
- composer.json<br />
- composer.lock<br />
- lib<br />
- Controller<br />
- Model<br />
- public<br />
- index.php<br />
- templates<br />
- vendor<br />
<br />
・composer<br />
vi composer.json<br />
<blockquote class="tr_bq">
{<br />
"require": {<br />
"slim/slim": "2.*"<br />
}<br />
}</blockquote>
php composer.phar install<br />
<br />
・index.php<br />
mkdir public<br />
cd public<br />
vi index.php<br />
<blockquote class="tr_bq">
<?php<br />
require 'vender/autoload.php';<br />
$app = new \Slim\Slim();<br />
$app->get('/hello/:name', function ($name) {<br />
echo "Hello, $name";<br />
});<br />
$app->run()</blockquote>
・nginx<br />
<br />
<blockquote class="tr_bq">
server {<br />
server_name slim.example.com;<br />
listen 80;<br />
root /var/www/app/slim/public;<br />
index index.php index.html index.htm;<br />
location / {<br />
try_files $uri $uri/ /index.php?$args;<br />
}<br />
location ~ \.php$ {<br />
try_files $uri =404;<br />
fastcgi_split_path_info ^(.+\.php)(/.+)$;<br />
include fastcgi_params;<br />
fastcgi_param PATH_INFO $fastcgi_path_info;<br />
fastcgi_index index.php;<br />
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;<br />
fastcgi_pass 127.0.0.1:9000;<br />
}<br />
}</blockquote>
<br />
○拡張<br />
素のSlimにはあまり機能がありません。<br />
目的に応じて機能を追加します。<br />
<br />
・ビュー<br />
HTMLを構築することが多いようならSmartyがおすすめ。<br />
以下のパッケージを追加します。<br />
<br />
"slim/extras": "2.0.*",<br />
"smarty/smarty": "3.1.*",<br />
<br />
<br />
vi index.php<br />
<br />
<blockquote class="tr_bq">
\Slim\Extras\Views\Smarty::$smartyDirectory = '../vendor/smarty/smarty/distribution/libs';<br />
\Slim\Extras\Views\Smarty::$smartyCompileDirectory = '../templates_c';<br />
\Slim\Extras\Views\Smarty::$smartyTemplatesDirectory = '../templates';<br />
\Slim\Extras\Views\Smarty::getInstance()->escape_html = true;<br />
$app = new \Slim\Slim([<br />
"templates.path" => "../templates",<br />
'view' => new \Slim\Extras\Views\Smarty,<br />
]);</blockquote>
<br />
・ログ<br />
fluentdにログを書くにはこれ。<br />
"aoyagikouhei/slim-fluent-logwriter": "0.0.*",<br />
<br />
vi index.php<br />
<br />
<blockquote class="tr_bq">
$writer = new \Slim\FluentLogwriter(['tag' => 'mongodb.system_log', 'level' => \Slim\Log::INFO]);<br />
$writer->addFluent(['tag' => 'mail.system_log', 'level' => \Slim\Log::WARN]);<br />
$app = new \Slim\Slim([<br />
'log.writer' => $writer,<br />
]);</blockquote>
<br />
・セッション<br />
MongoDBにセッションを保存します。<br />
"aoyagikouhei/mongo-session-handler": "0.0.*",<br />
<br />
vi index.php<br />
<br />
<blockquote class="tr_bq">
\MongoSession\Handler::initSession([<br />
'server' => 'mongodb://127.0.0.1:27017'<br />
,'db_name' => 'test'<br />
,'write_options' => ['w' => 1] <br />
]);</blockquote>
<br />
○コントローラー<br />
規模が大きくなってくると、index.phpに全てのコードを書くと量が多くなり読みにくいコードになります。共通化するのも関数でしかできなくて不便です。<br />
そこでコントローラークラスを導入することで、ベースクラスやポリモルフィズムやtraitなどの共通化行いやすくなります。<br />
<br />
まずindex.phpの先頭でコントローラークラスを自動で読み込んでもらえるようにAutoloaderに登録します。<br />
<br />
<blockquote class="tr_bq">
<?php<br />
$loader = require "../vendor/autoload.php";<br />
$loader->add('Controller', '../lib');<br />
$loader->register();</blockquote>
<br />
次にBaseControllerです。<br />
<blockquote class="tr_bq">
<?php<br />
namespace Controller;<br />
class BaseController<br />
{<br />
protected $app;<br />
public function __construct($app)<br />
{<br />
$this->app = $app;<br />
}<br />
}</blockquote>
<br />
各コントローラーを定義します。<br />
<blockquote class="tr_bq">
<?php<br />
namespace Controller;<br />
class Member extends \Controller\BaseController<br />
{<br />
public function __construct($app)<br />
{<br />
parent::_construct($app);<br />
$this->app->get('/member/login'), function () {<br />
$this->login();<br />
});<br />
}<br />
private function login() {<br />
echo "hi";<br />
}<br />
}</blockquote>
ルート設定はコンストラクターで行います。無名関数では宣言無しで$thisが使えるのが便利です。<br />
<br />
○モデル<br />
モデルもあると便利です。<br />
自前で容易するのもいいですがMongoDBを使うのならmandangoがおすすめです。<br />
<br />
"mandango/mondator": "dev-master",<br />
"mandango/mandango": "dev-master",<br />
<br />
ジェネレーションギャップパターンで空の派生クラスが生成されるので、追加のコードはそこに書けば再生成し直しても問題ありません。<br />
<br />
<br />
<br />aoyagikouheihttp://www.blogger.com/profile/02332627244925169468noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-39215337605339916042013-05-01T07:19:00.000+09:002013-05-01T07:19:51.844+09:00ØMQ(zeromq)について<div class="tr_bq">
青柳です。</div>
<div class="tr_bq">
メッセージキューを使おうかと思って調べていたら面白そうなキューのライブラリがあったので試してみました。</div>
<div class="tr_bq">
<br /></div>
<div class="tr_bq">
○メッセージキューとは</div>
<a href="https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%AD%E3%83%A5%E3%83%BC" target="_blank">メッセージキュー</a><br />
メッセージキュー(英: Message queue)は、プロセス間通信や同一プロセス内のスレッド間通信に使われるソフトウェアコンポーネントである。制御やデータを伝達するメッセージのキューである。<br />
[Wikipeida]<br />
<br />
<a href="http://e-words.jp/w/MQ.html" target="_blank">MQ【メッセージキューイング】</a><br />
アプリケーションソフト間でデータを交換して連携動作させる際に、送信するデータをいったん保管しておき、相手の処理の完了を待つことなく次の処理を行う方式。<br />
[e-Word]<br />
<br />
<a href="http://docs.oracle.com/cd/E19528-01/820-0531/aeraq/index.html" target="_blank">メッセージ指向ミドルウェア (MOM)</a><br />
イラスト参考<br />
[Oracle]<br />
<br />
<a href="http://www.sophia-it.com/content/MOM" target="_blank">MOMとは</a><br />
MOMとは、異なるプラットフォーム間でアプリケーション同士が双方向に情報をやり取りするためのソフトウェアのことである。<br />
[Binary]<br />
<br />
<a href="http://togetter.com/li/131622" target="_blank">MOM(メッセージ指向ミドルウェア)の存在意義って何?</a><br />
日本ではMOMがあまり活用されていないようですが、MOMを使う理由はどこにあって、どうしてあまりはやらないのでしょうか。<br />
[togetter]<br />
<br />
○ØMQとは<br />
<a href="http://www.zeromq.org/" target="_blank">本家</a><br />
The Intelligent Transport Layer<br />
[ØMQ]<br />
<br />
<a href="http://www.infoq.com/jp/news/2010/09/introduction-zero-mq" target="_blank">ØMQ (ZeroMQ) 序論</a><br />
異なったソケットタイプ,接続処理,フレーミング,さらにはルーティングといった低レベルな詳細事項を,いくらかでも抽象化できたら素晴らしいとは思わないでしょうか? ZeroMQ (ØMQ/ZMQ) ネットワークライブラリは,まさにそのためのものです。"このライブラリはメッセージ全体をインプロセスや IPC,TCP,マルチキャストなど,さまざまなトランスポートを越えて送信できるソケットを提供します。ファンアウト,PubSub,タスク分散,要求/応答などのパターンによる N 対 N の接続が,ソケットを使って可能になるのです。"<br />
[InfoQ]<br />
<br />
<a href="http://blog.wktk.co.jp/archives/71" target="_blank">ØMQ(zeromq)について調査する。</a><br />
<br />
N-N通信を実現する、socket API風軽量メッセージングライブラリ。<br />
自動的な再接続や、メッセージのキューイングを行ってくれる。<br />
複数のメッセージングパターンと呼ばれるものを組み合わせることによって、柔軟なメッセージ配信を行うことができる。<br />
[グニャラくんのwktk運営日記]<br />
<br />
<br />
<a href="http://d.hatena.ne.jp/wivlog/20101103/1288775099" target="_blank">ActiveMQ or RabbitMQ or ZeroMQ or ...</a><br />
どうやら ZeroMQ はシンプルで高速、RabbitMQ は割と高速でスケーラビリティが高い、ActiveMQ は遅いけど機能豊富といった感じらしい。ま、ActiveMQ は JMS 実装だしね……ESB 向きなんだろうなぁ。<br />
[wivlog]<br />
<br />
<a href="http://tech-sketch.jp/2011/12/-zeromq.html" target="_blank">システム間連携 その4:ZeroMQ</a><br />
ZeroMQを用いる事により、Berkeley socketsと同様のコーディングでありながら、Berkeley socketsで提供されずユーザーが実装しなければならなかった障害対応等の実アプリケーションで必須な機能が使用でき、簡単にシステム間連携が実現できます。<br />
[TeckSketch]<br />
<br />
○Rubyでの実装<br />
■request/response<br />
response.rb<br />
<blockquote>
#coding: utf-8<br />
require 'ffi-rzmq'<br />
context = ZMQ::Context.new(1)<br />
# Socket to talk to server<br />
socket = context.socket(ZMQ::REP)<br />
socket.bind("tcp://127.0.0.1:5555")<br />
while true<br />
msg = ''<br />
res = socket.recv_string(msg)<br />
puts "recive message " + msg<br />
socket.send_string(msg + " World")<br />
end</blockquote>
request.rb<br />
<blockquote>
#coding: utf-8<br />
require 'rubygems'<br />
require 'ffi-rzmq'<br />
context = ZMQ::Context.new(1)<br />
# Socket to talk to server<br />
puts "Connecting to hello world server…"<br />
requester = context.socket(ZMQ::REQ)<br />
requester.connect("tcp://127.0.0.1:5555")<br />
0.upto(9) do |request_nbr|<br />
puts "Sending request #{request_nbr}…"<br />
requester.send_string ARGV[0]<br />
reply = ''<br />
rc = requester.recv_string(reply)<br />
<br />
puts "Received reply #{request_nbr}: [#{reply}]"<br />
end </blockquote>
■publish/subscribe<br />
publish.rb<br />
<blockquote>
#coding: utf-8<br />
require 'ffi-rzmq'<br />
context = ZMQ::Context.new(1)<br />
pub = context.socket(ZMQ::PUB)<br />
pub.bind("tcp://127.0.0.1:5555")<br />
0.upto(9) do |i|<br />
puts "Sending #{i}…"<br />
pub.send_string i.to_s<br />
sleep(1)<br />
end<br />
pub.close<br />
context.terminate </blockquote>
susbcribe.rb<br />
<blockquote>
#coding: utf-8<br />
require 'ffi-rzmq'<br />
context = ZMQ::Context.new(1)<br />
sub = context.socket(ZMQ::SUB)<br />
sub.setsockopt(ZMQ::SUBSCRIBE, '')<br />
sub.connect("tcp://127.0.0.1:5555")<br />
while true<br />
msg = ''<br />
res = sub.recv_string(msg)<br />
puts ARGV[0] + " " + msg<br />
end</blockquote>
■push/pull<br />
push.rb<br />
<blockquote class="tr_bq">
#coding: utf-8<br />
require 'ffi-rzmq'<br />
context = ZMQ::Context.new(1)<br />
push = context.socket(ZMQ::PUSH)<br />
push.bind("tcp://127.0.0.1:5555")<br />
0.upto(9) do |i|<br />
puts "Sending #{i}…"<br />
push.send_string i.to_s<br />
sleep(1)<br />
end<br />
push.close<br />
context.terminate</blockquote>
pull.rb<br />
<blockquote class="tr_bq">
#coding: utf-8<br />
require 'ffi-rzmq'<br />
context = ZMQ::Context.new(1)<br />
pull = context.socket(ZMQ::PULL)<br />
pull.connect("tcp://127.0.0.1:5555")<br />
while true<br />
msg = ''<br />
res = pull.recv_string(msg)<br />
puts ARGV[0] + " " + msg<br />
end</blockquote>
■pipeline<br />
task.rb<br />
<blockquote class="tr_bq">
#coding: utf-8<br />
require 'ffi-rzmq'<br />
context = ZMQ::Context.new(1)<br />
push = context.socket(ZMQ::PUSH)<br />
push.bind("tcp://127.0.0.1:5555")<br />
pull = context.socket(ZMQ::PULL)<br />
pull.bind("tcp://127.0.0.1:5556")<br />
th_push = Thread.new do<br />
0.upto(9) do |i|<br />
puts "Sending #{i}…"<br />
push.send_string i.to_s<br />
sleep(1)<br />
end<br />
push.close<br />
end<br />
th_pull = Thread.new do<br />
while true<br />
msg = ''<br />
res = pull.recv_string(msg)<br />
puts "Recv " + msg<br />
end<br />
end<br />
th_push.join<br />
th_pull.join<br />
context.terminate</blockquote>
worker.rb<br />
<blockquote class="tr_bq">
#coding: utf-8<br />
require 'ffi-rzmq'<br />
context = ZMQ::Context.new(1)<br />
pull = context.socket(ZMQ::PULL)<br />
pull.connect("tcp://127.0.0.1:5555")<br />
push = context.socket(ZMQ::PUSH)<br />
push.connect("tcp://127.0.0.1:5556")<br />
while true<br />
msg = ''<br />
res = pull.recv_string(msg)<br />
back = ARGV[0] + " " + msg<br />
puts back<br />
push.send_string(back)<br />
end</blockquote>
■まとめ<br />
非同期の仕組みが驚くほど簡単に実装できます。<br />
大量のクロールを走らせるような仕組みを構築するのに使えそうです。<br />
ただし、キューはメモリ上で行われるためメッセージが確実に処理されたかどうかの保証がありません。<br />
クロールを作ろうとすると実際にクロールされたかどうか、現在クロール中なのかどうかをDBなどで管理する必要がありそうです。<br />
<br />
<br />aoyagikouheihttp://www.blogger.com/profile/02332627244925169468noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-41150197124683259312013-04-24T18:53:00.000+09:002013-04-24T18:53:33.942+09:00AWS whitepaper “Using AWS for Disaster Recovery” を読み解く山本です。<br />
AWS のホワイトペーパー <a href="http://d36cz9buwru1tt.cloudfront.net/AWS_Disaster_Recovery.pdf" rel="nofollow" target="_blank">Using AWS for Disaster Recovery</a> を読み解いてみます。<br />
(注:このホワイトペーパーは既存のクラウド上でないシステムの存在を前提にしているフシがある)<br />
<h2>
キーワード</h2>
<h3>
一般用語</h3>
<dl>
<dt><a href="http://en.wikipedia.org/wiki/Recovery_time_objective" target="_blank">Recovery Time Objective, RTO</a></dt>
<dd>災害から回復までの所要時間とサービスレベルの目標。
objective(目標)であって mandate(指令)ではないところがポイント。
RTOを満足しない作戦をとることもありうるし、その場合でもRTOは目標として堅持すべき。</dd>
<dt><a href="http://en.wikipedia.org/wiki/Recovery_point_objective" target="_blank">Recovery point objective, RPO</a></dt>
<dd>許容可能なデータロス期間の上限目標。
例えば、RPOとして4時間を設定したとすると、日次バックアップは適切な手段ではないことになる。</dd>
</dl>
<h3>
AWS用語</h3>
<dl>
<dt>リージョン</dt>
<dd>国くらいの粒度の地域単位。AWSでは往々にしてリージョンをまたぐような操作に壁がある。</dd>
<dt>アベイラビリティ・ゾーン, AZ</dt>
<dd>リージョンの中に複数個存在。地理的に離れていて独立性が高い、ということになっている。
普段目にするのは論理AZであって物理AZではないため、アカウントをまたぐとゾーン名が一緒でも物理的には違うなどということがあるらしい。
(<a href="http://www.scutum.jp/information/waf_tech_blog/2011/08/waf-blog-005.html" rel="nofollow" target="_blank">ソース</a>)</dd>
<dt>CloudFormation</dt>
<dd>インスタンスの作成などをテンプレート化して自動化するための仕掛け。(今のところ、UVでこれを活用したことはない)</dd>
</dl>
<h2>
災害復旧シナリオ例</h2>
<ul>
</ul>
<h3>
Backup and Restore</h3>
昔なら定期的にテープにフルダンプをとってオフサイト送り、となるところ。<br />
S3がテープメディアの代わりに。EBSのスナップショットも有効。<br />
<h4>
バックアップで話は終わりじゃない!</h4>
リストアまでがシナリオです。<br />
<ul>
<li>バックアップに使うツールは適切か?</li>
<li>データ保持(期間)のポリシーは適切か?</li>
<li>セキュリティ対策は?</li>
<li>作ったバックアップ からの復元のテストを<b>定期的</b>に!</li>
</ul>
<ul>
</ul>
<h3>
Pilot Light(種火)</h3>
最重要なコア要素をAWSで動かすようにする。<br />
災害時にはこのコア要素に取り巻きを立て直すことで復旧とする。<br />
<h4>
準備</h4>
データはパイロットライト系統にレプリケーション。<br />
OSのようなあまり更新頻度の高くないようなものは定期的にAMI(マシンイメージ)を更新しておく。<br />
<h4>
復旧</h4>
水平展開によるスケーリングの方がオススメ。インスタンスタイプを上位に変更してのスケールアップ手法もとれないことはない。<br />
復旧が一段落したら、冗長性を速やかに取り戻すべし。<br />
<h4>
キーポイント</h4>
<ul>
<li>アプリケーションサーバのAMIを作って、そこからすぐ立ち上げられるように</li>
<li>必要に応じてスケールアップ </li>
<li>災害時はDNSをいじってAWS側を向くように</li>
<li>AMIベースでない要素の構成を忘れずに、理想的には自動化</li>
</ul>
<ul>
</ul>
<h3>
Warm Standby</h3>
Pilot Light 方式の拡張。 インスタンスタイプその他を必要最小限に抑えた一式をAWSに用意する方式。<br />
災害時にはこちらをスケールアップして負荷を捌く。 <br />
<ul>
</ul>
<h3>
Multi-Site</h3>
平時はオンプレミスとAWS上のシステムを同時に動かしておく方式。<br />
<ul>
</ul>
<h2>
データレプリケーションの方式</h2>
<ul>
<li>同期(Multi-AZな AWS RDB はこの構成になる)</li>
<li>非同期(バックアップ用や参照系のユースケースならこれで充分なことが多い)</li>
</ul>
レプリケーション方式について理解しておくことが推奨されている。 <br />
<h2>
災害復旧プランを向上するには</h2>
<h3>
テスト!</h3>
いざ障害という時に復旧プロセスが可能な限りシンプルにできるように、復旧マニュアルが不足ないかを見る。<br />
<h3>
モニタリングとアラート(監視系)</h3>
<h3>
バックアップは継続的に</h3>
<h3>
アクセス制御</h3>
<h3>
自動化 </h3>
Anonymoushttp://www.blogger.com/profile/00618575252116270595noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-3362613260033162462013-04-06T16:06:00.001+09:002013-04-06T16:06:46.738+09:00AndroidでのRESTクライアント(GETのみ) <style type="text/css">
table.tbl {
border-collapse: collapse;
}
table.tbl td {
border-style: solid;
border-width: 1px;
border-color: #666666;
}
td.src {
overflow: scroll;
white-space: nowrap;
width: 500px;
line-height: 0.8em;
}
</style>
<br />
高瀬です。<br />
<br />
今回はAndroidアプリを作ってみる。目指すものは、Webサーバからデータを取得し、それを画面に表示する、という機能の実現とする。<br />
<br />
アプリとWebサーバとの通信はRESTの形で行う。アプリ側でのRESTの実装には<a href="http://www.springsource.org/spring-android" target="_blank">Spring for Android</a>を利用。このSpring for Androidを試してみることが本題なのだが、RESTのサーバを準備したり、JUnitでAndroidアプリのテストなどもやってみる。<br />
<br />
ソースコードはGitHubの<a href="https://github.com/ktakase00/andrest" target="_blank">andrest</a>リポジトリを参照。<br />
<div>
<br /></div>
<br />
<h3>
RESTについて</h3>
<br />
自分はRESTが何なのか分かっていないので、まず用語辞典を読んでみる。<br />
<br />
RESTとは<br />
<a href="http://e-words.jp/w/REST.html">http://e-words.jp/w/REST.html</a><br />
<br />
やっぱり何だかよく分からないが、自分の認識を書くと以下のとおり。<br />
<br />
<ul>
<li>URLが一つのリソースを表す。</li>
<li>リソースへの操作の種類をHTTPメソッド(GET=取得、POST=新規追加、PUT=更新、DELETE=削除)で表す。</li>
<li>レスポンスはJSON形式で送信する。</li>
</ul>
<br />
上記の用語辞典のページに書かれているとおり、RESTは別にHTTP通信に限ったものでもないし、レスポンスがJSONでなくてはいけないわけでもないが、今のところはこのくらいの内容で理解しておくことにする。<br />
<br />
ここではサーバからのデータの取得のみを実装する。<br />
<br />
<br />
<h3>
サーバ側</h3>
<br />
サーバ側はPHPのYiiフレームワークで用意する。RESTでの実装を補助してくれるエクステンションがあるので、これを利用してみる。<br />
<br />
RESTFullYii<br />
<a href="http://www.yiiframework.com/extension/restfullyii/">http://www.yiiframework.com/extension/restfullyii/</a><br />
<br />
このエクステンションでは、ERestControllerクラスの派生クラスとしてコントローラを作成することで、GETやPOSTに対応する処理を書くことができるようになっている。また、URLとリソースを結びつけるURLフォーマットも用意されている。<br />
<br />
作成したのは<a href="https://github.com/ktakase00/andrest/blob/master/andrestsrv/protected/controllers/PostController.php" target="_blank">PostController</a>クラス。エクステンションのソースコードに含まれているREADME.mdの説明にしたがって、doRestList()、doRestView()メソッドを記述した。<br />
<br />
doRestList()は、ID指定なしでGETメソッドによりアクセスされた場合に呼び出される。ここでは8件の記事のデータを返すようにした。<br />
<br />
<table class="tbl"><tbody>
<tr><td class="src"><pre> $data = array(
array(
"post_id" => 1,
"time" => "3月3日 12時42分",
"title" => "Evernoteが不正アクセス被害"
),
array(
"post_id" => 2,
"time" => "3月4日 18時3分",
"title" => "警戒区域で初 ストビュー撮影"
),
:
);
$this->renderJson($data);</pre>
</td></tr>
</tbody></table>
<br />
記事の内容は2013年3月4日のYahoo!JAPANのニュースから拝借。<br />
<br />
doRestView()は、ID指定ありでGETメソッドによりアクセスされた場合に呼び出される。ここでは、8件の記事のうち、IDが指している番号の記事の内容を返すようにした。上記の一覧取得では各記事の内容をpost_id、time、titleとしているが、こちらではこれらに加えてcontentを返すようにしている。例えば、1が指定された場合は以下の内容を返す。<br />
<br />
<table class="tbl"><tbody>
<tr><td class="src"><pre> array(
"post_id" => 1,
"time" => "3月3日 12時42分",
"title" => "Evernoteが不正アクセス被害、全ユーザーのパスワードをリセット",
"content" => "米Evernoteは2日、同社のシステムに何者かが不正アクセスしたことを公表した。 ..."
),
</pre>
</td></tr>
</tbody></table>
<br />
アプリ側ではこれらのデータをJSON形式で受け取り、解析する。<br />
<br />
なお、この後登場するSpring for AndroidのRestTemplateが、HTTPレスポンスのタイプがapplication/jsonであることを期待しているので、HTTPヘッダのContent-Typeにapplication/jsonをセットしている。<br />
<br />
<br />
<h3>
アプリで通信をする際の準備</h3>
<br />
通信を行うアプリを開発する場合、以下の2点に注意する必要がある。<br />
<br />
<ol>
<li>パーミッションandroid.permission.INTERNETを許可する。</li>
<li>通信処理はUIスレッド(メインスレッド)で実行してはならない。</li>
</ol>
<br />
参考:<br />
Androidアプリでインターネット接続する為に必要な設定(android.permission.INTERNET)<br />
<a href="http://feedyomi.blog32.fc2.com/blog-entry-181.html">http://feedyomi.blog32.fc2.com/blog-entry-181.html</a><br />
<br />
android.os.NetworkOnMainThreadExceptionエラーへの対応方法<br />
<a href="http://garnote.com/2012/10/android-os-networkonmainthreadexception.html">http://garnote.com/2012/10/android-os-networkonmainthreadexception.html</a><br />
<br />
パーミッションの許可を指定しないと、実行時に以下の例外が発生した。<br />
<br />
<table><tbody>
<tr><td class="src"><pre>I/O error: socket failed: EACCES (Permission denied);
nested exception is java.net.SocketException: socket failed: EACCES (Permission denied)
</pre>
</td></tr>
</tbody></table>
<br />
UIスレッドで通信をしようとすると、上記参考ページのとおりNetworkOnMainThreadExceptionが発生する。<br />
<br />
パーミッションについては、参考ページのとおり<a href="https://github.com/ktakase00/andrest/blob/master/andrestcli/AndroidManifest.xml" target="_blank">AndroidManifest.xml</a>でパーミッションの指定をすればよい。<br />
<br />
通信処理を行うスレッドについては、AsyncTaskクラスなどを使用してワーカースレッドを作成する必要がある。<br />
<br />
参考:<br />
AsyncTask を利用した非同期処理<br />
<a href="http://android.keicode.com/basics/async-asynctask.php">http://android.keicode.com/basics/async-asynctask.php</a><br />
<br />
AsyncTaskLoaderを利用した非同期処理を行う<br />
<a href="http://techbooster.org/android/application/13492/">http://techbooster.org/android/application/13492/</a><br />
<br />
時代は AsyncTask より AsyncTaskLoader<br />
<a href="http://archive.guma.jp/2011/11/-asynctask-asynctaskloader.html">http://archive.guma.jp/2011/11/-asynctask-asynctaskloader.html</a><br />
<br />
AsyncTaskLoaderの方が便利だが、対象OSバージョンがAndroid 3.0以降であること、コールバックメソッドが必ずActivityになければならないことから、少々使いにくい印象がある。そこで、AsyncTaskを使用して、非同期処理完了時に呼び出されるハンドラを指定できるようにしてみた。<br />
<br />
ハンドラ用に<a href="https://github.com/ktakase00/andrest/blob/master/andrestcli/src/com/example/andrestcli/listeners/OnFetchListener.java" target="_blank">OnFetchListener</a>クラスを定義。インスタンスを生成したら、ハンドラとなるメソッドを定義する。そして、AsyncTaskクラスのインスタンス生成時にハンドラを渡しておく。<br />
<br />
これなら、一つのActivityで複数の非同期処理を実行したい場合に、ハンドラを個別に指定することができる。<br />
<br />
非同期処理の準備ができたら、いよいよ通信処理を実装していく。<br />
<br />
<br />
<h3>
Spring for Android</h3>
<br />
ここからが本題。AndroidでのHTTP通信はorg.apache.http.client.HttpClientを使用してもよいが、RESTを簡単に実装できるという、Spring for Androidを使ってみることにする。<br />
<br />
Spring for Android | SpringSource.org<br />
<a href="http://www.springsource.org/spring-android">http://www.springsource.org/spring-android</a><br />
<br />
Jackson JSON Processor<br />
<a href="http://jackson.codehaus.org/">http://jackson.codehaus.org/</a><br />
<br />
Spring for Androidでは、HTTPメソッドGET、POST、PUT、DELETEにそれぞれ対応する、getForObject()、postForObject()、put()、delete()というメソッドが用意されている。使い方は、delete()ならURLのみ、それ以外はURLとパラメータを指定して呼び出すだけである。<br />
<br />
マニュアルにしたがって、まずはインストールから。<br />
<br />
Spring for Androidのライブラリ(spring-android-rest-template-{version}.jarとspring-android-core-{version}.jar)をダウンロードしたら、以下の手順(Spring for Android Reference Documentation から抜粋)でプロジェクトに組み込む。<br />
<br />
<ol>
<li><span style="font-size: x-small;">Refresh the project in Eclipse so the libs/ folder and jars display in the Package Explorer.</span></li>
<li><span style="font-size: x-small;">Right-Click (Command-Click) the first jar.</span></li>
<li><span style="font-size: x-small;">Select the BuildPath submenu.</span></li>
<li><span style="font-size: x-small;">Select Add to Build Path from the context menu.</span></li>
<li><span style="font-size: x-small;">Repeat these steps for each jar.</span></li>
</ol>
<br />
<div>
libsフォルダに各jarファイルをコピーしたら、Eclipse上で右クリックし、「Add to Build Path」でビルドパスに追加。これでRestTemplateが使用できるようになる。</div>
<div>
<br /></div>
<div>
ついでに、JSONの解析にJacksonというライブラリを使用するので、同様にプロジェクトに組み込んでおく。ダウンロードは上記の参照ページから。</div>
<div>
<br /></div>
<br />
<h3>
一覧表示</h3>
<br />
記事の一覧を取得する。記事データの構造は前述のとおり、post_id、time、title、contentの4つの要素で構成されている。まずはこの構造に合わせたクラスを用意しておく。一覧取得ではcontentは使用しないが、単一記事と共用で使用できるクラスとするために含めている。<br />
<br />
<br />
<table class="tbl"><tbody>
<tr><td class="src"><pre>public class Post {
private Long post_id;
private String time;
private String title;
private String content;
public String getPost_id() {
return this.post_id.toString();
}
public void setPost_id(Long post_id) {
this.post_id = post_id;
}
public String getTime() {
return this.time;
}
public void setTime(String time) {
this.time = time;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return this.content;
}
public void setContent(String content) {
this.content = content;
}
}
</pre>
</td></tr>
</tbody></table>
<br />
<br />
そして、AsyncTaskを継承したクラス<a href="https://github.com/ktakase00/andrest/blob/master/andrestcli/src/com/example/andrestcli/models/PostList.java" target="_blank">PostList</a>を作成。以下が記事一覧を取得する部分。<br />
<br />
<table class="tbl"><tbody>
<tr><td class="src"><pre> private ArrayList<HashMap<String, String>> data;<hashmap string="" tring="">
:
protected Long doInBackground(String... params) {
// TODO Auto-generated method stub
RestTemplate rest;
String url = this.context.getString(R.string.post_url);
// Create a new RestTemplate instance
rest = new RestTemplate();
try {
Log.i("App", "start");
// Add the String message converter
rest.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
Post posts[] = rest.getForObject(url, Post[].class);
this.data.removeAll(null);
for (int i=0; i<posts.length; i++) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("post_id", posts[i].getPost_id());
map.put("time", posts[i].getTime());
map.put("title", posts[i].getTitle());
this.data.add(map);
}
Log.i("App", "end");
}
catch (Exception e) {
Log.e("App", e.getMessage());
}
return null;
}
</hashmap></pre>
</td></tr>
</tbody></table>
<br />
getForObject()でサーバへ記事の一覧を要求すると、結果がPostクラスの配列で得られる。これをListViewにセットできる形にするため、メンバー変数dataに保存した。<br />
<br />
<br />
アクセスするURLはres/values/<a href="https://github.com/ktakase00/andrest/blob/master/andrestcli/res/values/params.xml" target="_blank">params.xml</a>ファイルに定義している。<br />
<br />
<br />
<table class="tbl"><tbody>
<tr><td class="src"><pre><resources>
<string name="post_url">http://hostname/path/index.php/api/post/</string>
</resources>
</pre>
</td></tr>
</tbody></table>
<br />
api/post/ にGETでアクセスすることで、サーバ側のPostController、doRestList()が呼び出され、記事の一覧取得が行われる。<br />
<br />
<br />
<h3>
単一記事表示</h3>
<br />
単一記事を取得する場合も、一覧取得とほぼ同様の処理となる。ソースは<a href="https://github.com/ktakase00/andrest/blob/master/andrestcli/src/com/example/andrestcli/models/PostDetail.java" target="_blank">PostDetail</a>クラスを参照。<br />
<br />
getForObject()の結果は、取得される記事は1件だけなので、Postクラスのインスタンスが1つだけ返される。<br />
<br />
またアクセスするURLは api/post/1 などのように記事のIDを付加している。これによりサーバ側ではdoRestView()メソッドが呼び出され、該当の記事の取得が行われる。<br />
<br />
<br />
<h3>
JUnit</h3>
<br />
サーバからのデータ取得と画面表示ができたら、テストコードも書いてみる。Androidでのテストについては以下のページを参照。<br />
<br />
Androidアプリ開発でテストを始めるための基礎知識<br />
<a href="http://www.atmarkit.co.jp/fsmart/articles/androidtest01/01.html">http://www.atmarkit.co.jp/fsmart/articles/androidtest01/01.html</a><br />
<br />
Testing Fundamentals<br />
<a href="http://developer.android.com/tools/testing/testing_android.html">http://developer.android.com/tools/testing/testing_android.html</a><br />
<br />
さらに、非同期処理の部分については以下も参照。<br />
<br />
AsyncTaskをJUnitでテストする方法<br />
<a href="http://wavetalker.blog134.fc2.com/blog-entry-68.html">http://wavetalker.blog134.fc2.com/blog-entry-68.html</a><br />
<br />
テストはプロジェクトを「Android Test Project」で作成する。ActivityのテストをするにはActivityInstrumentationTestCase2の派生クラスを作成する。<br />
<br />
上記@ITの記事では見つけられなかったが、ActivityInstrumentationTestCase2の派生クラスを作る際、引数なしのデフォルトコンストラクタがないとテストが実行されなかった。デフォルトコンストラクタは自分で記述する必要がある。<br />
<br />
<a href="https://github.com/ktakase00/andrest/blob/master/andrestcliTest-test/src/com/example/andrestcli/test/AndrestcliActivityTest.java" target="_blank">AndrestcliActivityTest</a>クラスを作成。以下のテストを行う。<br />
<br />
<br />
<ul>
<li>記事の一覧が取得できること。</li>
<li>取得した記事の件数が一致すること。</li>
<li>リストビューのアイテム数が記事の数と一致すること。</li>
</ul>
<br />
<br />
<br />
<table class="tbl"><tbody>
<tr><td class="src"><pre> public void testGetPostList() throws Exception {
// 非同期処理完了時のハンドラーを定義
OnFetchListener handlerForTest = new OnFetchListener() {
@Override
public void onFetch(Context context, AsyncBase asyncTask) {
// unlock testcase's thread.
signal.countDown();
}
};
</pre>
<pre> // create subclass of test target asynctask.
PostList posts = new PostList(activity, handlerForTest);
</pre>
<pre> // execute asynctask.
// AndrestcliActivityのOnCreate()で実行されているものとは別にもう一度実行する
posts.execute("");
</pre>
<pre> // wait for asynctask.
signal.await(30, TimeUnit.SECONDS);
</pre>
<pre> PostList result = activity.getPostList();
assertNotNull("記事の一覧が取得できること。", result);
assertEquals("取得した記事の件数が一致すること。", 8, result.getData().size());
</pre>
<pre> ListView listView1 = (ListView)activity.findViewById(R.id.listView1);
assertNotNull("リストビューが取得できること。", listView1);
assertEquals("リストビューのアイテム数が記事の数と一致すること。", 8, listView1.getCount());
}
</pre>
</td></tr>
</tbody></table>
<br />
サーバのモックをしていないので、サーバが動作していないとテストに失敗してしまうが、とりあえずこれでテストの実行を確認することができる。実施しているのは、記事の一覧が取得できることと、それをListViewで表示できていること、としている。<br />
<br />
実は、テスト対象のAndrestcliActivityはOnCreate()で記事一覧の取得を行っており、テストコードでも記事一覧の取得を行っているので、一覧取得が2回動いてしまっている。テストをするには都合の悪い作りだっただろうか。<br />
<br />
<br />
<h3>
おわり</h3>
<div>
<br />
通信処理をUIスレッドで行ってはいけない、という制限がなんとも面倒だ。画面に記事を表示するというだけの機能しかないのに、ずいぶんと手間がかかった印象がある。<br />
<br />
しかし、もっと多機能なアプリを作るにはどのみち非同期の処理は必要になってくるだろうし、一度非同期の処理を作ってしまえば後は使いまわすだけなので、実質的には取っ掛かりが少々面倒、というところだろうか。<br />
<br />
Spring for Androidはそれほど苦労もなく使えたので、便利なライブラリだと思う。GET以外のメソッドについては別の機会に試してみる。<br />
<br />
アプリが通信機能を持つことは多いだろう、覚えておいて損はないはず。<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/14099319546219098184noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-1076950294150689622013-01-18T19:24:00.001+09:002013-01-18T19:38:04.830+09:00make のお話山本です。<br />
<br />
今回は古くて旧い make を取り上げてみます。<br />
昔と違って、今ならビルド自動化ツールの選択肢として rake も ant も grunt も xbuild もありますが、それでも敢えて make です。<br />
<h4 class="hdr">
make とは</h4>
make とは、あらかじめ Makefile に<b><u>生成物</u> </b>とその<u><b>材料</b></u>との間の<u>依存関係</u>を書いておくことで、生成物を得るのに<u>必要十分</u>なプロセスを自動的に行うツールです。<br />
特に、中間生成物が存在するケースで再生成漏れを防ぐのに有用です。<br />
<h5>
make の特徴</h5>
<ul>
<li>基本的に、何かを生成するために外部のプログラム(コンパイラ etc.)を起動する。シェルスクリプトに思想が似ています。</li>
<li>自力でソースを解析してまで依存関係を判断することはない。ただし、外部ツール(mkdep 等)の支援のもとで依存関係の記述を自動化させる事例はあります。</li>
</ul>
<h5>
make の種類</h5>
make には、主に2種類のメジャーな方言があります。 基本的な記述方法は一緒ですが、条件分岐やinclude等の特殊な構文や、変数展開時に使えるオプション指定に違いがみられます。<br />
<ul>
<li>GNU make</li>
<li>BSD make (a.k.a. pmake)</li>
</ul>
<h4 class="hdr">
make 基礎篇</h4>
<h5>
依存関係(ルール)</h5>
<blockquote class="tr_bq">
<u><i>ターゲット</i></u>:<u><i>依存物</i></u><br />
<u><i>レシピ</i></u></blockquote>
ターゲット、依存物ともに複数個指定ができます。(その場合はスペース区切り) <br />
レシピは行頭にタブ文字。複数行指定ができます。行頭にタブ文字以外が出現すると、そこでルールの記述が終わります。<br />
ターゲット以外はいずれも省略可能。<br />
レシピを省略した場合は、そのターゲットが指定された依存物に依存している事実だけが記述されたことになり、 具体的なレシピは他の記述か拡張子をベースにしたデフォルト(推論規則)になります。<br />
<h5>
変数展開</h5>
<blockquote class="tr_bq">
$<u><i>V</i></u></blockquote>
もしくは<br />
<blockquote class="tr_bq">
${VARIABLE}</blockquote>
ただし、{}で括らない書式は、変数名が1文字な場合に限られます。<br />
<h5>
変数定義</h5>
<h6>
普通に代入する</h6>
<blockquote class="tr_bq">
VAR = the value to be set</blockquote>
行頭に空白は書けません。そのかわり、等号の前後に空白文字を入れることは可能です。<br />
なお、変数に別の変数の値を代入することもできますが、代入された変数の値が実際に展開されるのは後になります。したがって、<br />
<blockquote class="tr_bq">
A=$B<br />
B=hoge<br />
dummy:<br />
echo $A</blockquote>
は、 hoge が出力されます。<br />
<h6>
その場で変数展開したものを代入する</h6>
前述した挙動が望ましくなく、その時点での値を代入したい場合は、<br />
<blockquote class="tr_bq">
A:=$B</blockquote>
とします。<br />
<h6>
未定義な場合に代入する</h6>
<blockquote class="tr_bq">
A ?= some default value</blockquote>
とすると、コマンドライン(後述)や環境変数などで値が与えられていない場合に限って代入が行われます。<br />
<h6>
追加する</h6>
<blockquote class="tr_bq">
A+= append to A</blockquote>
とすると、変数の値に追加することができます。例えば、<br />
<blockquote class="tr_bq">
A= Unique<br />
A+= Vision</blockquote>
とした場合、 $A は Unique Vision と展開されます。(Unique と Vision の間に空白が入っている点に注目) <br />
<h6>
コマンドラインから</h6>
<blockquote class="tr_bq">
make VAR=value</blockquote>
のように、コマンドラインから変数を指定することもできます。 <br />
<h4 class="hdr">
make 応用篇</h4>
<h5>
記述量を減らそう!</h5>
<h6>
自動変数</h6>
make が勝手に設定してくれる変数がいくつかあります。<br />
主なものを挙げてみます。 <br />
<ul>
<li>$< 依存物</li>
<li>$@ 生成物 </li>
</ul>
注意点としては、これらの変数はレシピ実行中に今まさに生成しようとしているターゲットに関する値として展開されます。<br />
つまり、中間生成物の生成レシピを実行している最中では、それぞれ中間生成物の依存物と中間生成物そのものの名前に展開されるということです。 <br />
<h6>
パターンによるルール</h6>
<blockquote class="tr_bq">
all: foo.css bar.css<br />
foo.css: foo.styl<br />
@stylus foo.styl<br />
bar.css: bar.styl<br />
@stylus bar.styl</blockquote>
と書く代わりに、<br />
<blockquote class="tr_bq">
all: foo.css bar.css<br />
%.css: %.styl<br />
@stylus $<</blockquote>
と書けます。($< は依存物を表す自動変数)<br />
<h6>
変数の展開時パターン置換</h6>
<blockquote class="tr_bq">
SRCS= foo.styl bar.styl</blockquote>
と書いてあるとすると、<br />
<blockquote class="tr_bq">
${SRCS:%.styl=%.css}</blockquote>
は<br />
<blockquote class="tr_bq">
foo.css bar.css</blockquote>
に展開されます。<br />
<br />
<h6>
共通部分の別ファイルへの切り出し</h6>
変数置換を駆使するなどして、ルールから具体的な値が排除できたら、ルールを別ファイルに切り出して、本体 Makefile から include させることで Makefile をまたいだ共通化が図れます。<br />
<blockquote class="tr_bq">
all: foo.css bar.css<br />
%.css: %.styl<br />
@stylus $<</blockquote>
の例を取り上げると、<br />
<ul>
<li>Makefile:<br /><blockquote>
STYLES= foo.styl bar.styl<br />
include stylus-common.mk</blockquote>
</li>
<li>stylus-common.mk:<br /><blockquote>
all: ${STYLES:%.styl=%.css}<br />
%.css: %.styl<br />
@stylus $<</blockquote>
</li>
</ul>
のような2ファイルに分割できます。(GNU make の場合)<br />
この例ではあまりありがたみはありませんが、もう少し複雑なルール集合を、いろいろな Makefile で使いたくなってきた時に威力を発揮します。<br />
(なお、 BSD make 系では、 include filename.mk の代わりに .include "filename.mk" などとします)<br />
<h5>
風変わりなレシピ</h5>
<h6>
常に失敗するレシピ</h6>
例えば、仕様書が更新されたらテストを更新しなければいけないことを明確化することを考えましょう。<br />
具体的にテストをコマンドで更新することは一般的には不可能ですが、更新の必要があることを開発者が知る必要もあるわけです。<br />
その場合、<br />
<blockquote class="tr_bq">
<blockquote class="tr_bq">
testcase.js: specification.doc<br />
@echo 仕様が更新されています。テストケースも更新しましょう。 >&2<br />
@false</blockquote>
</blockquote>
のように、最後に false を実行させることで無理やり失敗させる手が有効です。<br />
<ul>
</ul>
Anonymoushttp://www.blogger.com/profile/00618575252116270595noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-65487399512991173482013-01-09T20:39:00.001+09:002013-01-09T20:39:39.279+09:00Backbone.jsとYiiフレームワークの連携<style type="text/css">
h4.hdr {
font-size: 20px;
border-left-style: solid;
border-left-width: 10px;
border-left-color: #666699;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #666699;
padding: 2px;
margin-bottom: 12px;
}
table.tbl {
border-collapse: collapse;
}
table.tbl td {
border-style: solid;
border-width: 1px;
border-color: #666666;
}
table.tbl td.word {
width: 50px;
}
blockquote.blq {
font-size: small;
border-style: solid;
border-width: 1px;
padding: 4px;
margin-right: 10px;
}
</style>
<br />
<div>
高瀬です。<br />
<br />
Backbone.jsとYiiフレームワークを使用してアプリケーションを開発する場合の、ソースコードの配置や書き方について検討してみる。<br />
<br />
サンプルアプリケーションのソースコードは<a href="https://github.com/ktakase00/tprefix/tree/demo_20130108" target="_blank">こちら</a>を参照。<br />
<br />
<br />
<h4 class="hdr">
サンプルアプリケーションの概要</h4>
<div>
Webブラウザ上でデータベースのER図が書けないものかと思い立ったので、それをテーマにアプリケーションを作成する。</div>
<div>
<br /></div>
<div>
アプリケーションはプロジェクトを1つの単位として、その中にスキーマやテーブルの定義を書いていく、という形にする。ここで、プロジェクトにリビジョンを付けて、編集履歴を残せるようにしたい。リビジョンはプロジェクトの作成直後を1とし、DB管理者のレビューを経て確定。その後さらに編集を開始する時に2に更新する、というサイクルを想定する。</div>
<div>
<br /></div>
<div>
画面構成や機能はまだまだ未検討で、今回はひとまずプロジェクトの作成、編集、削除ができるところまでを実装する。</div>
<div>
<br /></div>
<div>
画面は以下のとおり。まず、作成済みプロジェクトの一覧が画面の左上に表示される。</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3vXmGgkhpbwlZHHW-H0CJuEtVvRjtQMWjpCGpcm_wXy7ITqtU3zeLUD13LFTpclLj3QaTiS-o1tlNlvBEn4KKrRCn0rzsdb6plgvWuZ6qzofGq9Z_lc2pZJOQo4ZrbL9sZF2RVa5qiGg/s1600/tprefix02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3vXmGgkhpbwlZHHW-H0CJuEtVvRjtQMWjpCGpcm_wXy7ITqtU3zeLUD13LFTpclLj3QaTiS-o1tlNlvBEn4KKrRCn0rzsdb6plgvWuZ6qzofGq9Z_lc2pZJOQo4ZrbL9sZF2RVa5qiGg/s1600/tprefix02.png" height="183" width="320" /></a></div>
<div>
<br /></div>
<div>
プロジェクト名にカーソルを重ねると、そのプロジェクト名の右側にメニューが表示される。メニュー項目は「編集」「プロパティ」「削除」とあり、それぞれER図の編集画面への遷移、プロジェクト名などのプロパティ編集、プロジェクトの削除、を行う。「レビュー」というメニュー項目を追加することを考えているが、今回は未実装。<br />
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOMj_Q-v5w0-lwYjy3UrpPLkP2-NOvwu0ZSmqXt5onhCcpgehcisbhrUtLMQfxMa4QoHchUbJ9fRLl0rea-1P1AcwKEmjuJlPvDe-IUmpmEmYwg005eW3KdUw_BLtS2vVfGcLBd0wIwdQ/s1600/tprefix03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOMj_Q-v5w0-lwYjy3UrpPLkP2-NOvwu0ZSmqXt5onhCcpgehcisbhrUtLMQfxMa4QoHchUbJ9fRLl0rea-1P1AcwKEmjuJlPvDe-IUmpmEmYwg005eW3KdUw_BLtS2vVfGcLBd0wIwdQ/s1600/tprefix03.png" /></a></div>
<div>
<br /></div>
<div>
プロジェクト一覧にカーソルを重ねた時、メニューと同時に、一覧の下側に「新しいプロジェクト」というリンクが表示される。「新しいプロジェクト」をクリックすると、画面の中央に「プロジェクトの作成」というダイアログを表示する。ダイアログにプロジェクト名を入力し、「作成」をクリックするとプロジェクトが作成される。作成されたプロジェクトは自動的にプロジェクト一覧に表示される。<br />
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLFe67VEIwJQuUgsS0t4O1kiZCs5quGljLN1ZObsh9SldH4sncxHVNqZkXbAqo2zGZYDV6if0yShkHnwSshrIkOwXSA778R9FNj5jd6cDW1TamXMLVz58MNs4Wo1PZ1HQBOq2LmXXxiMc/s1600/tprefix04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLFe67VEIwJQuUgsS0t4O1kiZCs5quGljLN1ZObsh9SldH4sncxHVNqZkXbAqo2zGZYDV6if0yShkHnwSshrIkOwXSA778R9FNj5jd6cDW1TamXMLVz58MNs4Wo1PZ1HQBOq2LmXXxiMc/s1600/tprefix04.png" height="125" width="320" /></a></div>
<div>
<br /></div>
<div>
<br />
プロジェクトのメニュー項目「編集」は現在未実装。「プロパティ」は「プロジェクトの作成」とほぼ同様のダイアログが表示され、プロジェクト名を変更できる。プロジェクト名が変更されると自動的にプロジェクト一覧に反映される。<br />
<br />
メニュー項目「削除」をクリックすると、画面中央に確認メッセージが表示される。「OK」をクリックするとプロジェクトの削除が行われる。削除されたプロジェクトは自動的にプロジェクト一覧から消える。<br />
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7iYhmavEgZQlqxYp-Ivaoyc6tO1tPgFqg9qoxCk_AkydXi3LCeA4Bq_kOKwmMxcab3pIgCo8miycVn0H_w86_KNofcx1zQEhkaTGak6F-OkGZ3zSFVyAdKsPbfAPkaJsXT-MQVAnFaYY/s1600/tprefix05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7iYhmavEgZQlqxYp-Ivaoyc6tO1tPgFqg9qoxCk_AkydXi3LCeA4Bq_kOKwmMxcab3pIgCo8miycVn0H_w86_KNofcx1zQEhkaTGak6F-OkGZ3zSFVyAdKsPbfAPkaJsXT-MQVAnFaYY/s1600/tprefix05.png" height="88" width="320" /></a></div>
<div>
<br />
画面には表示されないが、プロジェクトを作成すると、自動的にリビジョン1を表すデータが作成される。プロジェクトを削除するとリビジョンのデータがすべて削除される。現時点ではリビジョンは1固定。<br />
<br />
開発するアプリケーションの名前はtprefix(ティー・プレフィックス)とする。以下、この名前で記述する。<br />
<br />
tprefixは以下のデータベース、開発言語、フレームワークで開発する。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td>構成</td>
<td>DB/言語</td>
<td>フレームワーク</td>
</tr>
<tr>
<td>データベース</td>
<td>PostgreSQL 9.x</td>
<td>-</td>
</tr>
<tr>
<td>サーバ側</td>
<td>PHP 5.4</td>
<td><a href="http://www.yiiframework.com/doc/guide/1.1/ja/index" target="_blank">Yii</a></td>
</tr>
<tr>
<td>ブラウザ側</td>
<td>JavaScript</td>
<td><a href="http://backbonejs.org/" target="_blank">Backbone.js</a></td>
</tr>
</tbody></table>
<br />
なお、JavaScriptのHTMLテンプレートエンジンとして、<a href="http://handlebarsjs.com/" target="_blank">Handlebars.js</a>を使用する。<br />
<br />
<br />
<br /></div>
<h4 class="hdr">
データベース</h4>
<div>
tprefixが記憶、管理するデータにはプロジェクトやリビジョン、スキーマ、テーブル、カラムなどが考えられる。その他にもユーザやカラム定義などを予定しているが、今回は「プロジェクト」「プロジェクトリビジョン」のテーブルのみを用意する。その他のテーブルの内容は検討中。</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlJ3RF8FOFjIv-ndAcR4T-cGeTd9X1uxWVMqekxYkWYvn9Ec8K7qk8UnPdtkB3Uw8rQINF1P2WgETrgddz_KIJ1FA30RLQ1voNgeGlDfm-ARUL_7PpI815mkG3waWJWs4i6pnKbkNki2E/s1600/tprefix09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlJ3RF8FOFjIv-ndAcR4T-cGeTd9X1uxWVMqekxYkWYvn9Ec8K7qk8UnPdtkB3Uw8rQINF1P2WgETrgddz_KIJ1FA30RLQ1voNgeGlDfm-ARUL_7PpI815mkG3waWJWs4i6pnKbkNki2E/s1600/tprefix09.png" height="189" width="320" /></a></div>
<br />
<br />
プロジェクトテーブルにはプロジェクトの名前を登録する。<br />
プロジェクトリビジョンテーブルは、プロジェクトのリビジョンを登録する。今回はリビジョンIDは前述のとおり1固定とする。説明内容、レビュー日時、レビュー結果内容は未使用。<br />
<br />
どちらのテーブルにもある「削除日時」は、レコードの削除が行われた日時を登録する。すなわち、「レコードの削除」操作はテーブルからレコードを消してしまうのではなく、「削除日時」を登録することにより、削除済みであるとみなすようにする。<br />
<br />
<br />
<br />
<h4 class="hdr">
ソースコードの配置</h4>
<div>
ソースコードはYiiフレームワークのディレクトリ構造を元に配置した。<br />
<br /></div>
<table class="tbl">
<tbody>
<tr>
<td>サーバ側(PHP)</td>
<td>ブラウザ側(JavaScript)</td>
</tr>
<tr>
<td><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEUQSN41eE-oxl6VRAxwNfFLrPRx375nzutbnGEnouOxpHa7FXDMWBfW3aDmq-ON3Gip8oFHfSOu4z7s9CpPWODQ_Wr4ixAeQHC4wcWWonNHltbqaP9h63sWPVIIXODoftWLCCyPU4XTI/s1600/tprefix07.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEUQSN41eE-oxl6VRAxwNfFLrPRx375nzutbnGEnouOxpHa7FXDMWBfW3aDmq-ON3Gip8oFHfSOu4z7s9CpPWODQ_Wr4ixAeQHC4wcWWonNHltbqaP9h63sWPVIIXODoftWLCCyPU4XTI/s1600/tprefix07.png" height="320" width="108" /></a></div>
</td>
<td><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX4lOhY_-hGNfX76phn3-t5YWST7MYNA_o3bVuy2q00-iwRvW6ajlwlJ26t1QPeLnuLKueK8vYX3QvxzrK7qBid4O3kg22VB1PeAxszpFKgw7iM6V1FRaROQ06b5nWIXn0e5biLxYg22E/s1600/tprefix06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX4lOhY_-hGNfX76phn3-t5YWST7MYNA_o3bVuy2q00-iwRvW6ajlwlJ26t1QPeLnuLKueK8vYX3QvxzrK7qBid4O3kg22VB1PeAxszpFKgw7iM6V1FRaROQ06b5nWIXn0e5biLxYg22E/s1600/tprefix06.png" height="320" width="148" /></a></div>
</td>
</tr>
</tbody>
</table>
<br />
上図において、tprefixがルートを表す。protected以下にサーバ側のPHP、js以下にブラウザ側のJavaScriptが配置されている。<br />
<br />
protected以下はMVCに従ってmodels、controllers、viewsにそれぞれソースコードを作成。これに対して、js以下はBackbone.jsのクラスに従って、models、collections、viewsを作成。<br />
<br />
tprefixでプロジェクト一覧が表示されるページとなるのは、projectコントローラのindexアクション。URLでは http://(サーバ名)/index.php?r=project/index となる。<br />
<br />
<br />
<br /></div>
<div>
<h4 class="hdr">
サーバ側</h4>
</div>
<div>
サーバ側はMVCフレームワークに従って開発する。<br />
<br />
■モデル<br />
<br />
モデルでは、今回はプロジェクトとプロジェクトのリビジョンを扱うので、<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/protected/models/Project.php" target="_blank">Project.php</a>と<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/protected/models/ProjectRevision.php" target="_blank">ProjectRevision.php</a>を作成。これらは特にBackbone.jsとの関連は少ない。データベースへのアクセスにはCActiveRecordを使用。<br />
<br />
<blockquote class="blq">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" /></a></div>
Project.phpのsave()メソッドでトランザクションを使用しているが、トランザクション内の処理はtry/catchで例外を捕らえ、例外発生時にrollbackされるようにするべき。</blockquote>
<br />
■コントローラ<br />
<br />
コントローラは、2種類のアクションを用意する。1つはブラウザ側で必要になるJavaScript、CSS、HTMLテンプレートを用意するアクション。もう一つは、ブラウザ側からのAjaxでの要求に対応する応答を返すアクション。<br />
<br />
ProjectControllerでは、JavaScriptなどを用意するアクションは<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/protected/controllers/project/IndexAction.php" target="_blank">IndexAction.php</a>が担当。JavaScript、CSSのファイルを配列で定義する。このソースコード内にあるregisterPackage()メソッドは<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/protected/components/Controller.php" target="_blank">Controller.php</a>に定義しているメソッドで、<a href="http://blog.uniquevision.co.jp/2012/11/yii.html" target="_blank">こちらの記事</a>を参照。<br />
<br />
HTMLテンプレートも配列で定義する。これらはYiiのパーシャルビューとして、Controller::renderPartial()で描画させる。<br />
<br />
よって、このproject/indexアクションを呼び出すと、表示する内容が何もない、YiiのレイアウトのみのHTMLが生成される。生成されたHTMLのheadタグ内には配列で指定したJavaScriptとCSSのファイルが含まれ、bodyタグ内にはHTMLテンプレートが含まれる。レイアウト部分以外の画面の描画はJavaScriptで行う。<br />
<br />
<span style="font-size: x-small;">※レイアウト部分と言うのは、前述「サンプルアプリケーションの概要」の図にある、「My Web Application」や「Home About ...」などが表示されている部分。それ以外の部分というのは、プロジェクトの一覧が表示されている部分。</span><br />
<br />
もう一つのAjax要求に対応する応答を返すアクションでは、<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/protected/controllers/project/ListAction.php" target="_blank">ListAction.php</a>のようにモデルを使ってデータを抽出・登録し、結果をJSON形式で出力する。<br />
<br />
<blockquote class="blq">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" /></a></div>
JavaScriptのファイルをYii::app()->clientScript->registerPackage()でHTMLに反映させるようにしており、この場合headタグ内にscriptタグが列挙される。しかし、headタグ内でJavaScriptを読み込むと、ページ全体の描画が始まるまでに時間がかかるため、ページの表示が遅い。bodyの終了タグの直前でJavaScriptが読み込まれるようにするとページの表示が速くなる。表示を速くするためにはもう少し工夫が必要。</blockquote>
<br />
■ビュー<br />
<br />
ビューではHTMLテンプレートのみを定義した。このテンプレートは<a href="http://handlebarsjs.com/" target="_blank">Handlebars.js</a>によって実際のDOMに変換される。Handlebars.jsのテンプレートはscriptタグ内に記述するようになっているので、それぞれのパーシャルビューは<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/protected/views/project/_project_delete_dialog.php" target="_blank">_project_delete_dialog.php</a>のようにscriptタグで囲まれた内容となっている。<br />
<br />
■例外の出力<br />
<br />
Yiiではtry/catchによってハンドリングされなかった例外はsiteコントローラのerrorアクションで処理される。ここで、<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/protected/controllers/SiteController.php" target="_blank">SiteController.php</a>のとおり、もともとこのアクションは要求がAjaxであればエラー内容をechoで出力し、そうでなければrender()で描画するようになっている。この仕組みをそのまま使ってみようと思う。<br />
<br />
<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/protected/models/Project.php" target="_blank">Project.php</a>のprepare()メソッドで、更新または削除対象のレコードが見つからなかった場合は例外とした。この例外はsite/errorアクションに到達するが、<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/protected/components/NotFoundException.php" target="_blank">NotFoundException.php</a>内で表示するべきエラーメッセージを定めており、要求がAjaxだった場合はJSON形式、そうでない場合は文字列のみとしている。<br />
<br />
これにより、JavaScriptからの要求は通常Ajaxで行うようにするので、エラーが発生した場合でもJSON形式の応答がある、という前提でブラウザ側の開発ができるはず。<br />
<br />
<blockquote class="blq">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" /></a></div>
JavaScriptからの要求がすべてAjaxであるとして開発をするのであれば、NotFoundExceptionクラス内でJSON形式か文字列かを決めるのではなく、site/errorアクション内でやってしまった方がよいだろう。</blockquote>
<br />
<br />
<br /></div>
<div>
<h4 class="hdr">
ブラウザ側</h4>
</div>
<div>
■名前空間とプログラムの開始<br />
<br />
Backbone.jsやUnderscore.jsのファイルはmain.phpに記述しており、Ajax用ではないアクションで共通に読み込まれるようにしている。<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/js/app.js" target="_blank">app.js</a>というファイルも共通としており、これは名前空間の定義用。<br />
<br />
プログラムの開始は、プロジェクト一覧を表示する画面の場合、startupディレクトリ内にある<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/js/startup/s_project.js" target="_blank">s_project.js</a>となる。Webアプリケーション全体をシングルページとして開発する場合はわざわざstartupというサブディレクトリにこのファイルを配置する必要はないが、実験として、プロジェクト一覧の画面と、各プロジェクト内のER図を編集する画面を別のページとしてみようと考えており、起点となるソースコードを配置するディレクトリを用意した。<br />
<br />
■コレクション<br />
<br />
<div>
<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/js/collections/c_Project.js" target="_blank">c_Project.js</a>でプロジェクトの一覧を扱う。urlに、サーバ側のproject/listアクションを指定。これにより、fetch()メソッドを実行するとプロジェクト一覧が取得される。一覧取得が完了すると自動的にresetイベントが発生するので、<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/js/views/project/v_ProjectList.js" target="_blank">v_ProjectList.js</a>でハンドリングし、画面に描画する。<br />
<br /></div>
<div>
■モデル<br />
<br />
今回扱うモデルはプロジェクトのみ。<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/js/models/m_Project.js" target="_blank">m_Project.js</a>で定義している。ここで、困ったことが2点あった。<br />
<br />
1点目は、プロジェクトを新規作成する時、サーバ側のproject/saveアクションへプロジェクト名をPOSTしたいのだが、Backbone.Modelクラスのデフォルトのsave()メソッドを呼び出すと、サーバ側(PHP)の$_POST変数に送信しているはずの値が入らない。仕方がないので、jQueryのpost()で送信している。<br />
<br />
<blockquote class="blq">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" /></a></div>
$_POSTで受け取れないのは、Backbone.sync()により送信されるデータのエンコードがデフォルトではapplication/jsonになっており、これをPHPが受け取れていないため。Backbone.emulateJSON = true を指定することにより、$_POSTで受け取れるようになるかも、とのこと。</blockquote>
<br />
2点目は、Yiiとの連携とは関係ないが、プロジェクトの新規作成・編集・削除を行った際に自動的にプロジェクト一覧を再描画したいが、モデルが更新されたというイベントをどのようにしてコレクションに伝えればいいのかが分からなかった。無理やりな感じだが、プロジェクト一覧を描画しているビューのelが参照している要素に対してchangeイベントを発火して伝えている。<br />
<br />
と、これを書きながら思ったが、もしかしてコレクションがモデルのaddイベントをハンドリングしていればいいだけの話だろうか。うむむ、気づくのが遅かった。<br />
<br />
<blockquote class="blq">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf4yeHfkaaSvN__1WYGNINSBbejYglpmbXyCUxrQVLW-vIL68qoDg9SlvS3Ik3egUl7V5SAsvgXhdqa_r424STZYFTIKAvw3d7nlweKOD7zyWPAnLIgzp5_q_Cd4VSltqhSfwfayB5flM/s1600/1357749172_arrow_right.png" /></a></div>
オブジェクト同士に関連がなく、ハンドリングの指定ができないような場合のために、前述のapp.jsで定義しているappオブジェクトを、Backbone.Eventの派生クラスにしておき、appオブジェクトにtrigger()させる、という方法もある。</blockquote>
<br />
<br />
■ビュー</div>
<div>
<br /></div>
<div>
ビューは<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/js/views/project/v_ProjectItem.js" target="_blank">v_ProjectItem.js</a>のようにモデルやコレクションに対応するものと、<a href="https://github.com/ktakase00/tprefix/blob/demo_20130108/src/js/views/project/v_ProjectEditDialog.js" target="_blank">v_ProjectEditDialog.js</a>のようにダイアログなどの表示する部品に対応するものを作成した。ダイアログなどはサーバ側のビューで定義されているHTMLテンプレートから生成する。<br />
<br />
ビュー同士や、別のビューが持っているモデルへのイベントの伝達方法が分からず、至らない点が多くあるので、追々直していくことにする。<br />
<br />
<br />
<br />
<h4 class="hdr">
おわり</h4>
ER図の編集画面くらいまでは作りたかったのに、勉強不足でいろいろ苦戦してようやくここまで、といったところ。<br />
<br />
バリデーションをしていなかったり、エラー処理が不十分だったりと、実用レベルではない部分もあるので、そのあたりも身に付けていきたい。<br />
<br />
tprefixの開発はまだまだ続く。はず。<br />
<br /></div>
</div>
Anonymoushttp://www.blogger.com/profile/14099319546219098184noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-71420793767400157552012-11-29T18:56:00.000+09:002012-11-29T18:56:53.280+09:00PostgreSQLのSQLチューニングについて青柳です。<br />
<br />
PostgreSQLのSQLをチューニングするのに役に立つドキュメントは本家のドキュメントです。<br />
ただし、本家のドキュメントは読みにくいところがあるので、用語の解説とわかりやすい説明のリンクをつけました。<br />
本家を読みながらか、先にこちらを読んでから本家を読むとよいです。<br />
あと、実際のチューニング例のリンクも追加しました。<br />
<br />
第 14章性能に関するヒント<br />
<a href="http://www.postgresql.jp/document/9.2/html/performance-tips.html">http://www.postgresql.jp/document/9.2/html/performance-tips.html</a><br />
<br />
■用語<br />
・シーケンシャルスキャン<br />
テーブルを順に走査していく。テーブルの多数を読む場合、インデックススキャンよりも有効。インデックススキャンはインデックス用のデータ領域にアクセスするので余分にアクセスすることになる。例えば本の全てを読むのに一々目次を見ながら読むのは効率的では無いのと一緒。<br />
<br />
・インデックススキャン<br />
インデックス用のデータを検査し、本体のテーブルにアクセスする。検索する件数が少ない場合有効。例えば本で目的の章のみ見たい場合、全部読むよりも目次からページを探す方が有効なのと一緒。<br />
<br />
・ビットマップインデックススキャン<br />
テーブルの行に対応する位置を0/1で表現して検査する。フラグなど識別する値が少ないと(カーディナリティが低い)有効。<br />
<br />
<br />
・ネステッドループ結合<br />
2つのテーブルを行単位で逐次比較。<br />
データが小さい場合向いている<br />
<br />
・マージ結合<br />
2つのテーブルをソートしておいてから(もしくはBtreeインデックスを使って)結合。<br />
データ量が多い場合向いている<br />
<br />
・ハッシュ結合<br />
ハッシュ表を作成しておいてから2つのテーブルを結合<br />
ソートメモリが多い場合向いている<br />
<br />
<br />
[PostgreSQLウォッチ]第17回 新しい実行プラン・タイプによるPostgreSQL 8.1の性能向上<br />
<a href="http://itpro.nikkeibp.co.jp/members/ITPro/oss/20050514/160833/?ST=oss&P=1">http://itpro.nikkeibp.co.jp/members/ITPro/oss/20050514/160833/?ST=oss&P=1</a><br />
ビットマップスキャンについて詳しく説明している<br />
<br />
基礎から理解するデータベースのしくみ(9)<br />
<a href="http://itpro.nikkeibp.co.jp/article/COLUMN/20060113/227241/">http://itpro.nikkeibp.co.jp/article/COLUMN/20060113/227241/</a><br />
ビットマップスキャンに対する図がわかりやすい<br />
<br />
Oracle SQLチューニング講座(3)<br />
SQLチューニングの必須知識を総ざらい(後編)<br />
<a href="http://www.atmarkit.co.jp/fdb/rensai/orasql03/orasql03_1.html">http://www.atmarkit.co.jp/fdb/rensai/orasql03/orasql03_1.html</a><br />
結合の図がわかりやすい<br />
<br />
■チューニングの例<br />
<br />
Linuxトラブルシューティング<br />
第3回 PostgreSQLを遅くしている犯人はどこだ?<br />
<a href="http://www.atmarkit.co.jp/flinux/rensai/troubleshoot03/ts03c.html">http://www.atmarkit.co.jp/flinux/rensai/troubleshoot03/ts03c.html</a><br />
わかりやすいチューニングの例<br />
<br />
20090107 Postgre Sqlチューニング(Sql編)<br />
<a href="http://www.slideshare.net/kwappa/20090107-postgre-sqlsql-presentation#btnNext">http://www.slideshare.net/kwappa/20090107-postgre-sqlsql-presentation#btnNext</a><br />
いささか古い記述があるがわかりやすい<br />
<br />
<br />aoyagikouheihttp://www.blogger.com/profile/02332627244925169468noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-16475299741356567012012-11-09T19:22:00.000+09:002012-11-09T19:22:19.119+09:00Yiiフレームワークのテーマとレイアウト機能<style type="text/css">
h4.hdr {
font-size: 20px;
border-left-style: solid;
border-left-width: 10px;
border-left-color: #666699;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #666699;
padding: 2px;
margin-bottom: 12px;
}
table.tbl {
border-collapse: collapse;
}
table.tbl td {
border-style: solid;
border-width: 1px;
border-color: #666666;
}
table.tbl td.word {
width: 50px;
}
td.src {
overflow: scroll;
white-space: nowrap;
width: 500px;
line-height: 0.8em;
}
</style>
<br />
高瀬です。<br />
<br />
YiiはPHPでWebアプリケーションを開発するためのMVCフレームワーク。以下のWebサイトを参照。<br />
<br />
<a href="http://www.yiiframework.com/doc/guide/1.1/ja/quickstart.what-is-yii" target="_blank">初めに: Yii とは何か | The Definitive Guide to Yii | Yii Framework</a><br />
<br />
Yiiでの開発を始めて半年ほどではあるけれども、使おうと思っても触れることができなかった機能に、「テーマ」と「レイアウト」がある。ここではそれらの使い方を考えてみる。<br />
<br />
<br />
<h4 class="hdr">
テーマの作成</h4>
「テーマ」はWordPressのテーマのように、Webサイトの見栄えを切り替えるために利用する。以下を参照。<br />
<br />
<a href="http://www.yiiframework.com/doc/guide/1.1/ja/topics.theming" target="_blank">その他のトピック: テーマ | The Definitive Guide to Yii | Yii Framework</a><br />
<br />
実際に使ってみよう。試しに、Yiiが自動生成する初期プロジェクトの見栄えを変えるテーマを作成してみる。<br />
<br />
[初期プロジェクトのテーマ]<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMoHZRy_RXMK_zc11f6tEE65oWRo4sG6s-S2OFdp10m9JKbzw6SJwgmRHmoJNSQNkS9x_TPYbHlas8gPIe7cpwlvV1E0sAp-8Co_bayaBaa8tPE7Ec7oH-MejySCGFJ0_oAGbvJJoyHRE/s1600/001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMoHZRy_RXMK_zc11f6tEE65oWRo4sG6s-S2OFdp10m9JKbzw6SJwgmRHmoJNSQNkS9x_TPYbHlas8gPIe7cpwlvV1E0sAp-8Co_bayaBaa8tPE7Ec7oH-MejySCGFJ0_oAGbvJJoyHRE/s1600/001.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6Ui51g7fIpKUGOW9ZO6wNR3fUJAJQqwTQiHZ3PXYQWAwW150DT4reMDAOfTlfBuwNtP39Yn67htLocEFq3KhIaMMrdoinMtSlHhfqJvvwmbkZ5ygxWsovWFM5UTcJqQlxHl8M085gYMA/s1600/002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="171" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6Ui51g7fIpKUGOW9ZO6wNR3fUJAJQqwTQiHZ3PXYQWAwW150DT4reMDAOfTlfBuwNtP39Yn67htLocEFq3KhIaMMrdoinMtSlHhfqJvvwmbkZ5ygxWsovWFM5UTcJqQlxHl8M085gYMA/s1600/002.png" width="320" /></a></div>
<br />
[変更後のテーマ]
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvnOZ_JzUpNGnd2z6tgmvGtqguGH9sKHqEcrZMuTDM6EBvAm-Skzr_WdSlE6EVQRqAx6-rYDvkchn7hGHcTQXBjv_NVX-OVmcUJtmSjlDM169BE0qE6v7iCQ8GK-JDu7eOVFHOGWD90R0/s1600/003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="126" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvnOZ_JzUpNGnd2z6tgmvGtqguGH9sKHqEcrZMuTDM6EBvAm-Skzr_WdSlE6EVQRqAx6-rYDvkchn7hGHcTQXBjv_NVX-OVmcUJtmSjlDM169BE0qE6v7iCQ8GK-JDu7eOVFHOGWD90R0/s1600/003.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicZb1GERBzSh_fjq2SA8az01b9-51673H20dBZBk8ReUT_HvzpFkw0Z_WGXYsK3bmYqw0KYZmWtz0VKCtRXRRorYdZrxrtjeynzoEtjSrL1w2iCIHQIPodUS_akz3Cz3pQGYAzsMYfxxI/s1600/004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="169" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicZb1GERBzSh_fjq2SA8az01b9-51673H20dBZBk8ReUT_HvzpFkw0Z_WGXYsK3bmYqw0KYZmWtz0VKCtRXRRorYdZrxrtjeynzoEtjSrL1w2iCIHQIPodUS_akz3Cz3pQGYAzsMYfxxI/s1600/004.png" width="320" /></a></div>
<br />
<br />
<br />
変更後のテーマには「uniquevision」という名前を付けることとする。まず最初にすることは、ページのレイアウトを決めているファイルmain.phpを、新しく作るテーマ用にコピーすることだ。<br />
<br />
(basePath)/protected/views/layouts/main.php<br />
↓<br />
(basePath)/themes/uniquevision/views/layouts/main.php<br />
<div>
<br /></div>
<span style="font-size: x-small;">※basePathは、プロジェクトのルートパスを指していることとする。</span><br />
<br />
テーマはthemesというディレクトリ内に作成する。作成したディレクトリの名前がテーマの名前となる。<br />
<br />
では、uniquevisionテーマのカスタマイズをしてみよう。コピーしたmain.phpを編集して、ページの左上に表示されている「My Web Application」の表示を、ユニークビジョンのロゴ画像に変更してみる。<br />
<br />
ロゴ画像のファイルはテーマごとのディレクトリ内に配置したいので、以下のパスを作成し、保存。その上で、パスをビューに記述する。<br />
<br />
(basePath)/themes/uniquevision/images/ti_logo.gif<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">(変更前)</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><div id="header"></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> <div id="logo"><?php echo CHtml::encode(Yii::app()->name); ?></div></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"></div><!-- header --></span><br />
<span style="font-family: 'Courier New', Courier, monospace;">↓</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">(変更後)</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><div id="header"></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> <div id="logo"><img src="<?= $this->themeBaseUrl; ?>images/ti_logo.gif" /></div></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"></div><!-- header --></span></td>
</tr>
</tbody>
</table>
<br />
テーマのディレクトリに配置した画像ファイルのパスを指すには、Yii::app()->theme という変数を利用することができる。テーマのパスを参照しやすいように、Controllerクラスで以下のように変数を定義した。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><span style="white-space: pre;">class Controller extends CController</span></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><span style="white-space: pre;">{</span></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><span style="white-space: pre;"> :</span></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><span style="white-space: pre;"> </span>public $themeBaseUrl = '';</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /><span style="white-space: pre;"> public function isThemeUsed() {
return !is_null(Yii::app()->theme);
}</span></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><span style="white-space: pre;">
</span>public function init()<br /><span style="white-space: pre;"> </span>{<br /><span style="white-space: pre;"> </span>parent::init();<br /><span style="white-space: pre;"> </span>if ($this->isThemeUsed()) {<br /><span style="white-space: pre;"> </span>$this->themeBaseUrl = Yii::app()->theme->baseUrl . DIRECTORY_SEPARATOR;<br /><span style="white-space: pre;"> </span>}</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> Yii::app()->clientScript->registerCoreScript('jquery');<br /><span style="white-space: pre;"> </span>}</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span><br />
<div>
<br /></div>
</td>
</tr>
</tbody></table>
<br />
$this->themeBaseUrl という変数を使用して画像ファイルへのパスを作っている($thisはコントローラのインスタンス)。<br />
<br />
さて、画像ファイルのパスの切り替え派できたが、CSSはどうするか。これもテーマごとのディレクトリ内に配置したい。とりあえず元のCSSファイルがあるディレクトリをコピーしてしまおう。<br />
<br />
(basePath)/css<br />
↓<br />
(basePath)/themes/uniquevision/css<br />
<br />
コピー後のCSSファイルを参照するには、やはり$this->themeBaseUrl を使用する。レイアウト用のmain.php内では、元は Yii::app()->request->baseUrl という変数でパスを生成しているので、これを書き換える。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">(変更前)</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/main.css" /></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">↓</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">(変更後)</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><link rel="stylesheet" type="text/css" href="<?= $this->themeBaseUrl; ?>css/main.css" /></span></td>
</tr>
</tbody>
</table>
<div>
<br /></div>
<div>
main.css を編集しておくと、テーマによりデザインが切り替わることが確認できる。<br />
<br />
最後に、新しく作成したテーマ uniquevision を有効にする。有効にするには、config/main.phpにテーマの名前を記述する。<br />
<br />
(basePath)/protected/config/main.php<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">return array(</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'name'=>'My Web Application',</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'theme'=>'uniquevision',</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :
</span></td>
</tr>
</tbody>
</table>
</div>
<br />
<br />
<h4 class="hdr">
clientScriptの設定によるテーマごとのJavaScriptとCSS</h4>
views/layouts/main.php 内に記述されたCSSのファイルパスは前述のようにテーマごとに切り替えることができた。では、clientScriptで指定するJavaScriptやCSSの場合はどうだろうか。<br />
<br />
例えば、すべてのテーマで共通に「<a href="http://www.abeautifulsite.net/blog/2008/12/jquery-alert-dialogs/" target="_blank">jQuery Alert Dialogs</a>」を使いたいとする。その上で、テーマ個別のJavaScriptも使いたいとする。<br />
<br />
config/main.php で、以下のように定義してみた。<br />
<br />
(basePath)/protected/config/main.php<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">return array(</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'components'=>[</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'clientScript'=>[</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'packages'=> [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'common' => [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'baseUrl' => '',</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'js' => [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'js/jquery.alerts.js',</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'js/sample.js'</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ],</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'css' => [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'css/jquery.alerts.css',</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ],</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ],</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'uniquevision' => [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'baseUrl' => 'themes/uniquevision/',</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'js' => [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'js/sample_uv.js'</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ],</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'css' => [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ],</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ],</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ],</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ],</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span></td>
</tr>
</tbody>
</table>
<div>
<br /></div>
packages内、common はどのテーマでも利用するもの、uniquevision は uniquevision テーマが有効になっている時に common に加えて利用するもの、とする。これらを読み込むために、コントローラに以下のメソッドを定義する。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">class Controller extends CController</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">{</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> public function registerPackage($js = [], $css = [])</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> {</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $depends = ['common'];</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> if ($this->isThemeUsed()) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $depends []= Yii::app()->theme->name;</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> Yii::app()->clientScript->packages[Yii::app()->name] = [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'baseUrl' => '',</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'js' => $js,</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'css' => $css,</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'depends' => $depends</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ];</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> Yii::app()->clientScript->registerPackage(Yii::app()->name);</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></td>
</tr>
</tbody>
</table>
<div>
<br /></div>
depends の指定において、common は常に読み込み対象とし、テーマが設定されている場合にだけ、テーマと同じ名前のパッケージも読み込み対象としている。<br />
<br />
このメソッドをアクションから呼び出せば、config/main.phpに記述したJavaScriptとCSSがビューの<head>内に取り込まれる。アクションからの呼び出しは以下のとおり。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">class SiteController extends Controller</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">{</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> public function actionIndex()</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> {</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $this->registerPackage();</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $this->render('index');</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></td>
</tr>
</tbody>
</table>
<div>
<br /></div>
<br />
デフォルトのテーマ用とuniquevisionテーマ用にそれぞれJavaScriptを用意し、jQuery Alert Dialogsでメッセージボックスを表示させるなどすると、どちらのテーマでも動作することを確認することができるだろう。<br />
<br />
アクション個別で利用したいJavaScriptやCSSがある場合はどうすればいいだろうか。<br />
<br />
上記のControllerクラスに定義したメソッド registerPackage() は、引数で追加のJavaScriptとCSSを指定できるようにしてある。ここに必要なファイルを指定することにする。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> public function actionIndex()</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> {</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $basePath = $this->isThemeUsed() ? 'themes/' . Yii::app()->theme->name . '/' : '';</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $js = [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $basePath . 'js/index.js'</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ];</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $css = [];</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $this->registerPackage($js, $css);</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $this->render('index');</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></td>
</tr>
</tbody>
</table>
<div>
<br /></div>
上記はテーマが指定されていればそのテーマ用のJavaScriptを参照するようになっているが、どのテーマでも共通で使用するJavaScriptを指定したい場合もあるだろう。その場合は、$basePath で指定しているパスを付加しなければよい。<br />
<br />
ここまで、ページのヘッダー/フッター部分やJavaScript、CSSの切り替えをしてきたが、この後はビューの内容をテーマで切り替えてみる。<br />
<br />
<br />
<h4 class="hdr">
レイアウトによる段組み</h4>
Yiiは初期プロジェクトで2種類の段組みを指定できるようになっている。これはControllerクラスの変数 $layout で指定されている。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">class Controller extends CController</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">{</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> public $layout='//layouts/column1';</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> public $menu=array();</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span></td>
</tr>
</tbody>
</table>
<div>
</div>
<br />
$layout '//layouts/column2' を指定すると、2列の段組みでページが表示されるようになる。2列の場合、左側の列がメインのコンテンツを表示するエリア、右側の列がサイドバーのエリアになっている。<br />
<br />
簡単に2列の段組みにできるのだが、通常、ビューで内容を描画できるのは左側のメインコンテンツ部分だけで、右側のサイドバーには上記Controllerクラスの変数 $menu で指定されるメニュー一覧で固定されてしまっている。任意の内容を表示させたくても、それを引き渡すためのパラメータが用意されていない。さて、どうしたものか。<br />
<br />
実験として、初期プロジェクトのテーマでのログイン画面は1列、uniquevision テーマのログイン画面は2列の段組みとしてみよう。uniquevision テーマでは、サイドバー側にログイン用入力フォームを設置する。<br />
<br />
ではまず、2列にした場合にサイドバーに描画させる内容をビューに引き渡すパラメータをControllerクラスに用意する。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">class Controller extends CController</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">{</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> public $sidebarParams = null;</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span></td>
</tr>
</tbody>
</table>
<br />
<br />
次に、uniquevision テーマの2列用レイアウトのファイルをコピーする。<br />
<br />
(basePath)/protected/views/layouts/column2.php<br />
↓<br />
(basePath)/themes/uniquevision/views/layouts/column2.php<br />
<br />
2列の場合、サイドバー側はパーシャルで描画するようにしよう。パーシャルの名前と、それに引き渡すパラメータは $sidebarParams で引き渡す。<br />
<br />
(basePath)/themes/uniquevision/views/layouts/column2.php<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><?php $this->beginContent('//layouts/main'); ?></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><div class="span-19"></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> <div id="content"></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> <?php echo $content; ?></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </div><!-- content --></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"></div></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><div class="span-5 last"></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> <div id="sidebar"></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><?php $this->renderPartial($this->sidebarParams['partial']); ?></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </div><!-- sidebar --></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"></div></span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><?php $this->endContent(); ?></span></td>
</tr>
</tbody>
</table>
<br />
<br />
アクションでは以下のようにする。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="src"><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">class SiteController extends Controller</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">{</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> public function actionLogin()</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> {</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $model=new LoginForm;</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> :</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> if ($this->isThemeUsed()) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> if ('uniquevision' == Yii::app()->theme->name) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $this->layout = '//layouts/column2';</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $this->sidebarParams = [</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'partial' => '_login_form',</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> 'model' => $model</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> ];</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> $this->render('login',array('model'=>$model));</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></td>
</tr>
</tbody>
</table>
<div>
<br />
テーマが指定されている場合、2列の段組みにして、Controllerクラスの変数 $sidebarParams にパーシャルの名前と、ログイン用入力フォームで使用するモデルをセットしておく。それぞれ、column2.php と、そこから呼び出されるパーシャル(上記の場合は _login_form.php) で参照される。<br />
<br />
左側のメインコンテンツはデフォルトのテーマの場合も、uniquevisionテーマの場合も login という名前のビューを描画する。ただし、デフォルトのテーマではメインコンテンツ側(loginビュー)に入力フォームを設置し、uniquevisionテーマではサイドバー側(_login_formパーシャルビュー)に入力フォールを設置。<br />
<br />
これで、段組みが1列の場合でも2列の場合でもログイン用入力フォームを動作させることができた。<br />
<br />
なお、layouts/column1.php は uniquevision テーマにコピーしていないが、ログイン画面以外の画面が初期プロジェクトと同様に1列の段組みで表示される。つまり、レイアウトやビューのファイルがテーマに定義されていない場合は、デフォルトのテーマ(protected/views以下のファイル)が使用され、差分となるファイルだけをテーマに定義すればよいことが分かる。<br />
<br />
<br />
<h4 class="hdr">
おわり</h4>
テーマもレイアウトもWebページの見栄えを制御するものだが、うまく使えば見栄えを切り替えるための分岐処理などは書かなくてもよくなりそうだ。しかし、テーマごとに同じ内容のソースファイルを設置してしまうとメンテナンス性がよくないので、どのように利用するかはプロジェクトごとに検討する必要がある。<br />
<br />
ちょうどこの記事を書いている時に、あるプロジェクトで本番環境、ステージング環境、開発環境で画面の背景色を切り替えたい、という要件があり、一部テーマを利用した。活用できるケースは意外に多いのかもしれない。<br />
<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/14099319546219098184noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-18800307485641837072012-08-29T18:52:00.001+09:002012-08-29T18:52:49.050+09:00MongoDBのAggregationフレームワークの紹介<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">青柳です。</span></span><br />
<span style="font-family: 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', 'MS Pゴシック', arial, helvetica, clean, sans-serif; line-height: 24px;">そろそろリリースされるMongoDB2.2で利用できるAggregationフレームワークについて紹介します。</span><br />
<br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">例としてTwiiterAPIのHomeの結果をコレクションとして保存します。</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">具体的には以下のリンク先を参考にしてください。</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">https://dev.twitter.com/docs/api/1/get/statuses/home_timeline</span></span><br />
<br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">・Count</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">コレクションやクエリーにマッチするオブジェクトの数を返します。</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;"><br /></span></span>
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">> db.tweet.count();</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">> db.tweet.count({user.screen_name: "aoyagikouhei"});</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;"><br /></span></span>
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">indexがあると早くなります。</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">> db.tweet.ensureIndex({user.screen_name: 1});</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;"><br /></span></span>
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">・Distinct</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">指定したキーに対してコレクションの個別のリストを返します。</span></span><br />
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;"><br /></span></span>
<span style="font-family: Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3, MS Pゴシック, arial, helvetica, clean, sans-serif;"><span style="line-height: 24px;">>db.tweet.distinct("user.screen_name");</span></span><br />
["aoyagikouhei", "masason"]<br />
<br />
クエリーもオプションでつけられます。<br />
> db.tweet.distinct("user.screen_name", {id : {$gte : 1234567}});<br />
<br />
Distinctは単体のBSONを返すので最大ドキュメントサイズ(4MB/16MB)を超える場合はMapReduceを使います。<br />
<br />
・Group<br />
SQLのgroup byと同じような、要素をグループ化した配列を返します。<br />
例えば、IDが1234567以降で各ユーザがつぶやいた数の集計は以下のようになります。<br />
<br />
> db.tweet.group({<br />
key : {"user.screen_name" : true},<br />
cond : {id : {$gte : 1234567}},<br />
initial : {count : 0},<br />
reduce : function(obj, prev) {prev.count++;}<br />
});<br />
<br />
Groupも単体のBSONで返すので結果が大きい場合はMapReduceを使います。<br />
また、複雑な集計の場合もGroupでは限界があるので、MapReduceを使います。aoyagikouheihttp://www.blogger.com/profile/02332627244925169468noreply@blogger.com0tag:blogger.com,1999:blog-7807065904082582927.post-17644150822924788502012-08-23T17:39:00.000+09:002012-08-29T19:48:41.259+09:00Webサイト設計で利用したいツール<style type="text/css">
h4.hdr {
font-size: 20px;
border-left-style: solid;
border-left-width: 10px;
border-left-color: #666699;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #666699;
padding: 2px;
margin-bottom: 12px;
}
table.tbl {
border-collapse: collapse;
}
table.tbl td {
border-style: solid;
border-width: 1px;
border-color: #666666;
}
table.tbl td.word {
width: 50px;
}
</style>
<br />
<h2>
</h2>
<div>
高瀬です。<br />
<br />
ここ最近で個人的に仕事上で気になった、以下の3つの言葉について書いておく。<br />
<ul>
<li>マークアップエンジニア</li>
<li>ワイヤーフレーム</li>
<li>コンセプトダイアグラム</li>
</ul>
<br /></div>
<h4 class="hdr">
マークアップエンジニア</h4>
<div>
</div>
「マークアップエンジニア」という単語で検索してみると、2007~2008年くらいの記事が上位に表れる。すなわちその頃によく使われていた単語だということなのだろう。<br />
<br />
<div>
</div>
<strong>ITPro マークアップ・エンジニアのTips(2008年)</strong><br />
<a href="http://itpro.nikkeibp.co.jp/article/COLUMN/20080529/304737/" target="_blank">第1回 マークアップ・エンジニアって何?</a> <br />
<br />
<div>
</div>
この記事によると、Webページに表示する情報を、デザインとユーザインターフェイスを含めて設計し、HTMLで実装する職種、ということになるだろうか。その単語を考えたという人(森田 雄 氏)のスライドを見つけた。<br />
<br />
<div>
</div>
<strong>slideshare(2010年) </strong><br />
<a href="http://www.slideshare.net/securecat/careerpath-for" target="_blank">マークアップエンジニアのキャリアパス</a><br />
<br />
<div>
</div>
おっと、「フロントエンドエンジニア」という単語が登場した。<br />
<br />
<div>
</div>
<strong>とみー(2011年)</strong><br />
<a href="http://www.tommyjp.com/2011/02/blog-post_07-5.html" target="_blank">フロントエンドエンジニアを探す方法5つ</a><br />
<br />
<div>
</div>
抜粋:<br />
-----<br />
<span style="color: #274e13;">フロントエンドエンジニアとはバックエンドエンジニアと対比して説明される職種で、ここでは、HTMLやJavaScript、CSSといったクライアントサイドの技術を中心に扱う、いわゆるWebアプリケーション部分を担当する人々と考えていただいて問題ないかと思われます。</span><br />
-----<br />
<br />
<div>
</div>
<strong>komacchi.com/blog(2010年)</strong><br />
<a href="http://www.komacchi.com/blogs/2010/05/post_113.php" target="_blank">フロントエンドエンジニア(マークアップエンジニア)とコーダーの違い</a><br />
<div>
</div>
<br />
サーバ側の開発を行うエンジニアに対比させた呼び方ということであれば、フロントエンドエンジニアとマークアップエンジニアの仕事に大きな違いはないのかもしれない。<br />
<br />
<div>
</div>
ともかく、「HTMLコーダー」はHTML+CSSでコーディングする人、マークアップエンジニアとフロントエンドエンジニアはJavaScriptでのプログラミングもするし、設計もする、という位置付けだと認識する。<br />
<br />
<div>
</div>
<div>
</div>
<div>
</div>
<h4 class="hdr">
ワイヤーフレーム</h4>
先日、同じプロジェクトを担当しているメンバーに、「画面のワイヤーですでに作っているものがあれば参考にしたいので見せてほしい」とお願いしたところ、色付き、影付き、写真付きの画面図案を出してくれた。<br />
<br />
<div>
</div>
うむむ、「ワイヤー」「フレーム」というくらいだから線と枠で作成されたレイアウト図だと思っていたのだが、これはもはやデザインを含めた画面の完成図案に近いのではないだろうか。自分の認識違いなのか。<br />
<br />
<div>
</div>
では、本でも読んでみよう。<br />
<br />
<div>
</div>
<strong>Webデザインレシピ(2012年)</strong><br />
<a href="http://webdesignrecipes.com/my-first-book/" target="_blank">現場のワークフローで覚える Webサイト制作教室</a><br />
<br />
<div>
</div>
この本では、「ワイヤーフレームという言葉に明確な定義はない」としており、そのうえで以下のような言葉を使うこととしている。<br />
<br />
<div>
</div>
<table class="tbl"><tbody>
<tr> <td>サイトマップ</td><td>Webサイト全体の構造を表す図。登場するページとそのつながりを示す。</td> </tr>
<tr> <td>ワイヤーフレーム</td><td>サイトマップに、各ページや導線の詳細を記述したもの。Webサイトの設計図。</td> </tr>
<tr> <td>ページレイアウトのワイヤーフレーム</td><td>ページの構成要素と配置を記述した図。</td> </tr>
<tr> <td>デザインモックアップ</td><td>ページのデザインを決定するために、ページを画像として作成したもの。</td> </tr>
</tbody> </table>
<div>
</div>
<br />
この定義に当てはめると、自分の認識していた「ワイヤーフレーム」とは「ページレイアウトのワイヤーフレーム」で、見せてもらったのは「デザインモックアップ」に近い内容、ということになりそうだ。<br />
<br />
<div>
</div>
そうすると、次に自分はどちらの図を作成するべきなのかだが、この時点では自分が開発を担当する機能に必要な、情報の整理や画面の構成要素の洗い出しを行う段階だったはずで、「ページレイアウトのワイヤーフレーム」の辺りで間違いではなかったつもり。<br />
<br />
<div>
</div>
ならば、もう少しワイヤーフレームという言葉の、社内での定義を明確にしておきたいと思う。<br />
<div>
</div>
<ul>
<li>ワイヤーフレームは、Webページの構成要素を洗い出すとともに、実際に表示するサイズで配置を決定するために作成する図。</li>
</ul>
<div>
</div>
くらいでどうだろうか。ポイントは表示する内容が網羅されていること。これによりサーバサイド側で用意しなければならないデータやロジックが決まってくる。さらに、実際に画面に表示する際のサイズで作成すること。デザインモックアップにする際に、想定していた画像などのサイズが大幅に変わってしまった、なんてことがあるようでは、何のために設計しているのか分からない。<br />
<br />
<div>
</div>
サイトマップやその詳細は、まとめて「サイトマップ」と呼べばいいと思う。Webシステム全体の機能の洗い出しをした段階でできてくる成果物になるのではないだろうか。これに対してページレイアウトは詳細設計の段階の成果物になるだろう。<br />
<br />
<div>
</div>
定義を決めたところで、ワイヤーを書いてみよう。例として、以下のようなWebシステムの開発を想定する。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="word">概要</td><td>社内で各メンバーが読んだ書籍を紹介するサイト。書籍のタイトルや感想を投稿すると、それに対して他のメンバーがコメントを記入する。</td>
</tr>
<tr>
<td>目的</td><td>書籍の情報を共有することで、社内での技術書の利用と自己啓発を促す。</td>
</tr>
</tbody>
</table>
<div>
<br />
サイト名は、適当に「UV Shelf」とでもしておこう。以下の流れで進める。</div>
<br />
<ol>
<li>サイトマップ(サイト構成のみ)を作成する</li>
<li>サイトマップ(各ページの詳細含む)を作成する</li>
<li>各ページのワイヤーフレームを作成する</li>
</ol>
では、サイトマップから。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://cacoo.com/diagrams/wc3bZVIvEZFns17m" target="_blank"><img border="0" height="228" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5F6InL5PR-bXz4_ZSaV9ODMu8oi9Y6NYxBjKjco9hRiazTEMv813E6yugrVJRCwc_ihGLagbUJQbDk7WmngI-jCdToBBtB7h3djX4XIoXFz6AnjaGEA_MZuqTa24tcn5ZS6vk3f_dGNo/s320/sitemap01.png" width="320" /></a></div>
<div>
</div>
<br />
社内で利用することを想定しているのでユーザ認証なんてどうでもいいが、投稿やコメントをする際にあらかじめユーザ名がわかっている方がいいように思うので、一応ログイン画面などを付けてみた。<br />
<br />
次にサイトマップの詳細。管理画面とユーザ登録画面の導線がサイトマップと違ってしまったが、これはサイトマップを直す方向で考えることにする。<br />
<br />
<div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://cacoo.com/diagrams/94xVTHIupMGmWCkS" target="_blank"><img border="0" height="105" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwd96K32-pScp5unruJGdLTbqcFNP26EIWB2BKu3NRXcGHvBP87mfowAb3LBGiLf27jcN2icICrdFL7P7o1zgYRsyQGkh8JJKDwUC1wI6Q58zGNdKdfTS1Q3qSj85vrRbMbiBOC3OKhzE/s320/sitemap02.png" width="320" /></a></div>
<div>
</div>
<div>
<br />
最後にTOP画面のワイヤーフレーム。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://cacoo.com/diagrams/sq1SZJz6fABfQkFW" target="_blank"><img border="0" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTx1ZkCQjQ1C7Xz2JNSReJi6mXVChg-88kksuRPdgf3aMB4lTsukwNX9ToLQabiVY4UOPQTw2L7CYFkEkNA_wV9Kk4bpGUmX9EF9PNSWhazTWOu2NE3TSEXKvYoQ8JIZhgU6-1X73lQig/s320/wire01.png" width="320" /></a></div>
<br />
TOP画面には書籍の一覧と、ログイン(ログアウト)、書籍の投稿、管理画面へのボタンまたはリンクを配置。これでサイトマップ詳細に記述した画面の構成要素を満たす。<br />
<br />
また、横幅はスクロールバーを含めて920px、スクロールバーを除くと900pxとなるようにしている。これで実際のWebページ表示のサイズを表現する。適当に描いたのでマージンなどはバラバラなのだが、きっちり作りこめばデザインモックアップに利用できるようになるはず。<br />
<br />
と、ここまで書いて、そういえばページングの機能が抜けているなぁとか、パスワード変更をする画面がないなぁとか、いろいろ気づくことがある。まだまだ仕様が詰め切れていないようだ。<br />
<br />
<br /></div>
<div>
ちなみにこの図は<b>Cacoo</b>で作成している。</div>
<div>
</div>
<a href="https://cacoo.com/lang/ja/;jsessionid=6D7103597ECDA3D1D6CD137A9B7E9C47.3" target="_blank">Cacoo</a><br />
<div>
<br />
ワイヤーフレームを作成するツールは他にも以下のようなものがある。<br />
<br />
<b>Webクリエイターボックス(2010年)</b><br />
<a href="http://www.webcreatorbox.com/tech/website-wireframe-tools/" target="_blank">Webサイトの骨組み: ワイヤーフレームを素早く・手軽に・美しく制作する</a><br />
<br />
<h4 class="hdr">
コンセプトダイアグラム</h4>
Webサイトをリニューアルする案件において、現行サイトの整理をするために「コンセプトダイアグラム」を作ってみた、というメンバーがいる。<br />
<br />
少し調べて見ると、Webサイトのアクセス解析などで利用されているらしい。立ち寄った本屋では以下の本に4ページだけ記載されているのを発見した。<br />
<br />
<b>murak.net(2012年)</b><br />
<a href="http://murak.net/post/analysis/626" target="_blank">『入門ウェブ分析論[増補改訂版]』レビュー</a><br />
<div>
<br /></div>
<div>
以下、その本の内容である。コンセプトダイアグラムの前に、Webサイトを分析するうえで覚えておかなければならない用語がある。</div>
<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="word">用語</td><td>意味</td>
</tr>
<tr>
<td>KGI</td><td>Key Goal Indicator:経営目標達成指標。サイトの目的、目標。目標は数値を含めて設定する。</td>
</tr>
<tr>
<td>CSF</td><td>Critical Success Foctor:重要成功要因。KGIに決定的な影響を与える要素。</td>
</tr>
<tr>
<td>KPI</td><td>Key Performance Indicator:業務評価指標。CSFの具体的な施策と目標設定。</td>
</tr>
</tbody>
</table>
<br />
それぞれ、いくつか例が記載されている。以下、分析対象のサイトをメディアサイト、広告型サイトとした場合の例。<br />
<br />
<table class="tbl">
<tbody>
<tr>
<td class="word">指標</td><td>指標例</td>
</tr>
<tr>
<td>KGI</td><td>利用者を800万人に増やし、月間1000万円の広告収入を得る</td>
</tr>
<tr>
<td>CSF</td><td>・閲覧数を増やす<br />
・滞在時間を増やす<br />
・ブランド名を理解してもらう</td></tr>
<tr>
<td>KPI</td><td>・新規ユーザのページビュー数を1.5倍にする<br />
・新規ユーザの訪問数を月間500万から800万に増やす<br />
・サイト名(ブランド名)での検索回数を1.5倍に増やす<br />
・1訪問あたりの滞在時間を1.5倍に増やす<br />
・1訪問あたりの平均ページビュー数を10から12に増やす<br />
・広告掲載ページのページビュー数を現在の2倍に増やす<br />
・広告枠を現在の1.5倍に増やす</td>
</tr>
</tbody>
</table>
<br />
<br />
KGIやKPIの指標を設定するためには、対象サイトの目的やユーザ像を理解しておく必要がある。サイトを整理し、理解するために、コンセプトダイアグラムを作成する、という手法がある。<br />
<br />
「入門 ウェブ分析論」の内容抜粋は以上。というわけで、コンセプトダイアグラムは、Webサイトの利用者が閲覧している、主要なコンテンツと導線を図に描き、どのようなKPIを設定するのがよいかを検討するための図、といえる。<br />
<br />
実例が以下のサイトで紹介されている。<br />
<br />
<b>Web担当者Forum(2011年)</b><br />
<a href="http://web-tan.forum.impressrd.jp/e/2011/09/01/11017" target="_blank">アクセス解析新手法「コンセプトダイアグラム」とは? サイトの全体像を可視化して知るべき指標を知る【レポート】</a><br />
<div>
<br /></div>
<div>
また、コンセプトダイアログによるWeb改善フレームワークを提唱されている清水 誠氏のスライドが以下のサイトに掲載されている。</div>
<div>
<br /></div>
<div>
<div>
<b>Six Apart ブログSix Apart ブログ(2012年)</b></div>
<div>
<a href="http://blog.sixapart.jp/2012-06/draw-concept-diagram.html" target="_blank">「Webサイト改善は図を描きなさい!」 〜 SiteCatalyst達人から教わった分析フレームワーク</a></div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<h4 class="hdr">
おわり</h4>
<div>
「技術ブログ」ということで、もっと実装寄りの内容が期待されていると分かってはいるのだけれども、現在取りかかっている案件の作業フェーズの都合上、今回はずいぶんと設計よりな話になってしまった。<br />
<br />
このブログにおいて自分が担当するテーマは「マークアップエンジニア」ということになっているが、テーマを決める際に「Web関連」などと曖昧なことを言ってしまったためにそうなったのであって、果たしてそれに向かっていけるかどうか不安ではある。<br />
<br />
マークアップにたどり着くのはまだまだ先になりそうな気はしているが、一応次回以降も自分担当の回はそれっぽい方向で。<br />
<br /></div>
</div>
Anonymoushttp://www.blogger.com/profile/14099319546219098184noreply@blogger.com0