Technote

by sizuhiko

CakePHP1.2がRC2になった

<!– more –>投げていたパッチが全て取り込まれていた版になったけど、fixtureのcreateとdropにはまだ対応していないようだ。

オレオレ解決策はみつけたのだけれど、コードがかなり汚くなるし、思想的に微妙なので、これではパッチにはできない・・・

でもあのチケット見つからないので、自動create機能は将来のバージョンアップ項目になったのかしらね?

CakeにTestがやってきた

<!– more –>というタイトルでLTをしてきました。

第3回CakePHP勉強会です。

第1回には参加できていたのに、前回は既に予定があり、残念ながら参加できず、久々の参加です。

発表イメージはほぼ完成していたのに、結局スライドが完成したのは当日昼:ko:

しかも当初枠の10分ギリギリの内容&進行がおしていたこともあり、超特急でやらせてもらいました。

聴いている人は速過ぎるかな?と思いながら懇親会では暖かい声をかけていただき、ありがとうございました:cheers:

もうちょっと時間の枠があれば詳しくテストについて話せそうなので、そういう機会があればまたお邪魔したいと思います。

他の人の発表もたいへん興味深い内容ばかりで、参考になりました。エディターのプラグインは確かにあったら便利そう。PCのリソースを使いまくってブラウザでもエディタでもファイルを開きっぱなしというのは良くないのですかね?:p

あっ、SimpleTestなどについて書かれたテストのまとめ本も、もうしばらくで出ると思いますので、よろしくお願いします。:rolleyes:

それとCake1.2本にご協力いただけるかたがいましたら、こちらもよろしくお願いします。

しかしいつもながらにSlideShareにUpすると、ヒラギノ文字がダメダメになりますね。何とかして欲しい・・・・

CakePHP 1.2 RC1でTreeBehaviorの絞込み範囲指定が変更となった

<!– more –>TreeBehavior自体1.2から追加された機能なのに、変更になるなんて。。。

1.2βでは

$this->Category->setScope(array('company_id'=>'1'));
$categories = $this->Category->children();

のように書くと、company_idが1のカテゴリだけ階層構造で取得できていたのだが、RC1ではsetScopeがDeprecatedになってしまった。で、上記方法を使っている画面を表示すると、「(TreeBehavior::setScope) Deprecated - Use BehaviorCollection::attach() to re-attach with new settings」というエラーメッセージが出るが、お世辞にもどう変更していいかわかるメッセージじゃないし、ドキュメントにも書いてない。

なので、ソースを読み込むことに。まぁソース読めばわかります。結果としては、

$this->Category->$this->Behaviors->attach('Tree', array('scope'=>array('company_id'=>'1')));
$categories = $this->Category->children();

のように記述するのですが、これを使用箇所で全部変更するのは大変ですね。そこで、Categoryのモデルクラスに以下のようなメソッドを追加してあげました。

function setScope($scope = array()) {
    return $this->Behaviors->attach('Tree', array('scope'=>$scope));
}

ま、AppModelに追加しても良かったんですが、全部がTreeBehavior使っている訳ではないと思うので、その場合は、setTreeScopeのような名前に変更した方が良いかな?と思っています。

CakePHP 1.2 RC1からfindの条件指定方法が変更となった

<!– more –>CakePHP1.2 も RC1 となり、現在開発中のプロジェクトでβからRC1に変更したところ、様々なトラブルが。。。

1.2βでは1.1の検索方法を踏襲しており、以下のように記述できる。

$conditions = array("Post.title" => "This is a post");

//Example usage with a model:
$this->Post->find($conditions);

この例は完全一致(=)なので問題はなかったのだが、それ以外の<や>、LIKEやBETWEENなど記述方法が変更となっている。 なおINの場合は、

$conditions = array("Post.id" => array(1,2,3,4,5));

//Example usage with a model:
$this->Post->find($conditions);

のように記述でき、1.1などからの変更はない。 ではLIKEについてこれまでの記法をおさらいすると、

$conditions = array("Post.title" => "LIKE %post%");

//Example usage with a model:
$this->Post->find($conditions);

となっていた。しかしこれを1.2 RC1で実行すると、WHERE句は「'Post.title’ = ‘LIKE %post%'」のようになってしまう。そこで、dbo_source.phpを見て解析してみた。結果、LIKEなどの記法は

$conditions = array("Post.title LIKE" => "%post%");

//Example usage with a model:
$this->Post->find($conditions);

のように変更となっていた。この変更はかなり驚きだが、カラム名の方にLIKEや<=などの条件を移動することで、様々なSQLインジェクションを考えると、従来のようにvalue側を正規表現で切り分けるより安全な気がする。

値が1つの場合は良さそうだ。ではBETWEENの場合はどうだろう?従来は、

$conditions = array("Post.date" => "BETWEEN 2008-1-1 AND 2009-1-1");

//Example usage with a model:
$this->Post->find($conditions);

のように記述していたが、これもそのままだとWHERE句は「'Post.data’ = 'BETWEEN 2008-1-1 AND 2009-1-1'」のようになってしまう。ではLIKEと同じようにしてみると、

$conditions = array("Post.date BETWEEN" => "2008-1-1 AND 2009-1-1");

//Example usage with a model:
$this->Post->find($conditions);

WHERE句は「'Post.data’ BETWEEN '2008-1-1 AND 2009-1-1'」のようになってしまう。これも正しくない。このようなケースはさらに変更されており、以下のようになる。

$conditions = array("Post.date BETWEEN ? AND ?" => array("2008-1-1", "2009-1-1"));

//Example usage with a model:
$this->Post->find($conditions);

BETWEENなど値が複数ある場合は、カラム名上で?を使うと配列の値を順番にセットしてくれる。WHERE句は「'Post.data’ BETWEEN '2008-1-1’ AND '2009-1-1'」のように期待通りの結果となる。

なおLIKEなどでも

$conditions = array("Post.title LIKE ?" => array("%post%"));

//Example usage with a model:
$this->Post->find($conditions);

と書くことが可能だが、そこまで見栄えに統一感を持たせなくても良いかな?と思う。

Flex2でCoverFlowクローンを作る (5) ~並べて台形変形させる

<!– more –>現在、オブジェクト倶楽部のメールマガジンで連載している「Flexで体験するリッチクライアント」を補足するTechnoteです。

第五回は、画像を横に並べて、中央の画像以外は台形表示する手順を整理します。

■今回サンプルの完成イメージ

前回は画像を鏡面効果表示するサンプルを紹介しました。今回はその画像いくつか並べて中央以外は台形加工して表示します。今回は、カスタムコントロール(ImageReflector.as)とmxmlファイルを修正していきます。

■ファイルの準備

プロジェクトファイルをダウンロードして、任意のディレクトリで解凍します。

  • reflector.as3proj
  • reflector.mxml
  • ImageReflector.as
  • TransformUtil.as

reflector.as3projをダブルクリックすると、FlashDevelopが実行されます。

■要素を並べて、繰り返し表示するには

Flashでは、Boxを使うとBox内に要素を並べることができます。BoxにはHBox(横並び)とVBox(縦並び)の実装があり、CoverFlowでは横並びを利用します。

HBoxを使って以下のようなコードを書けばよいのですが、

<HBox>
  <local:ImageReflector id="imageReflector0" />
  <local:ImageReflector id="imageReflector1" />
  <local:ImageReflector id="imageReflector2" />
  <local:ImageReflector id="imageReflector3" />
</HBox>

これだとImageReflectorを必要な分、記述しなければいけないのでかなり面倒です。そこで、繰り返しを便利にしてくれるRepeaterという要素があります。今回のmxmlでは以下のように記述しています。

<HBox id="h_frame" height="400" width="500" backgroundColor="0x000000" paddingLeft="200" paddingRight="200" verticalScrollPolicy="off">
    <Repeater id="rp" dataProvider="{dp}">
        <local:ImageReflector id="imageReflector" asinId="{rp.currentItem}" creationComplete="onCreationComplete(event)"/>
    </Repeater>
</HBox>

Repeaterで繰り返したい要素はdataProviderというパラメータで指定することができます。dpは同じmxmlのスクリプト部分に記述しています。

[Bindable]
private var dp:ArrayCollection =  new ArrayCollection(["482228350X", "4774133655", "4274066940", "4822283143", "4839923221", "4873113202", "4797333502", "4822282295"]);

配列要素(ArrayCollection)に、amazonのasinidを指定していけば、値が変更になった場合も簡単です。後々XMLファイルに外だしにする場合もそれほど変更が必要になりません。

■画面とActionScriptで情報を交換する

dataProviderで指定した、asinidをActionScriptに引き渡すには、[Bindable]を宣言すれば良いだけです。この宣言は変数でも関数でも、直前に記述すれば、MXMLとActionScriptの間で情報を交換することができます。本サンプルでは、asinIdと、選択表示(今回は中央)のIndexをMXMLからActionScriptに対して引き渡しています。引渡し方法は、タグの中で指定する方法や、MXMLのScriptからID指定で渡す方法があります。

タグの中で指定するには、

<local:ImageReflector asinId="{rp.currentItem}"/>

と書くことで、ImageReflectorクラスのasinId変数の初期値として、rp.currentItemの値がセットされます。rp.currentItemはRepeater(idがrp)の現在のdataProvider配列の値です。繰り返している現在の値を引き渡しています。

スクリプトの中で指定するには、

imageReflector[0].selectedIndex = dp.length / 2;

と書くことで、idがimageReflectorの要素の0番目のselectedIndex変数に値をセットすることができます。imageReflectorはRepeaterで繰り返しているImageReflector要素のidで同じIDが複数出現すると、自動的に配列要素となってくれます。

■measureを修正して、個々の横幅を指定する

Flashが個々の要素の幅を決定するときに呼ぶmeasure関数で、選択要素は等幅、それ以外は台形変形したときに横幅を指定するようにします。

if(selectedIndex == repeaterIndex) {
    measuredWidth = _content.getExplicitOrMeasuredWidth();
}
else {
    measuredWidth = tiltingWidth;
}

selectedIndexはMXMLとバインディングしている変数ですが、repeaterIndexはどこにも出てきません。これは自分のコントロールがRepeaterの中にある場合に有効な変数で、自分がRepeaterの何番目かを知ることができるのです。ですからMXMLから指定された選択Indexである場合は、画像の幅を指定し、それ以外の場合はtiltingWidthを指定します。このtiltingWidthも変数宣言していませんが、今回追加したコードに関数が記述されています。

public function set tiltingWidth(value:Number):void
{
    // no set
}
public function get tiltingWidth():Number
{
    var contentWidth:Number = _content.getExplicitOrMeasuredWidth();
    var contentHeight:Number = _content.getExplicitOrMeasuredHeight();
    var radians:Number = Math.atan2(-tiltingMargin, maxTiltingWidth);
    var rotation:Number = radians * 180 / Math.PI;
    var w:Number = Math.abs(contentWidth * Math.cos(rotation * 180 / Math.PI));
    return Math.min(maxTiltingWidth, w);
}

functionの後にset,getと書くとプロパティ値のsetter,getterの意味を持つことができ、あたかもtiltingWidthというメンバ変数があるように見せかけることができます。

台形変形したときの横幅は、前回も出たMathの三角関数を使って出しています。図のように台形に変形したときに切り落とされる三角形の底辺となる幅を想定すればよいでしょう。最初に上のように高さがtiltingMarginで、幅がmaxTiltingWidthの三角形で斜辺への角度を求めます。

同じ角度で斜辺の長さが実際の画像幅になった場合に、底辺がどのくらいの幅になるかを求めます。そこで小さいほうの幅を採用すると幅の異なる様々な画像でもうまく幅を指定することができます。

■実際に描画する

updateDisplayListを修正して、ビットマップを台形変形するなどの描画を行います。

前回まではupdateDisplayListで直接画像を表示していましたが、transformImageという関数に変形後の幅を渡して画像を描画してもらうようにします。今回追加したtransformImage関数では、Kazuhiko Arase氏の四角形の自由変形という記事に掲載されている、TransformUtilクラスを使っています。台形変形すると様々な考慮が必要となるのですが、この変形ツールを指定すれば、4点の座標を指定するだけで良いので便利です。
transformImage関数では、図のように6つの点を計算しています。位置が計算できたら、drawImageShape関数でTransformUtilを呼び出して、描画領域へ表示するようにします。

■クロスドメインの注意事項

前回も書いたのですが、amazonの画像サーバのcrossdomain.xmlは画像サーバ内からだけしか参照できないので、ここで作成したSWFファイルをサーバにアップロードしてもうまく表示できません。

本記事上の完成イメージは、PHPサイトでプロキシしています。

まずPHPのコードは前回記事を参考にしてください。

■次回予告

アニメーション表示に取り掛かりたいと思います。