digital matter

‘Programming’ カテゴリーのアーカイブ

Doctrine_Collectionをループする

Symfony 1.4+Doctrine
やたら不便(だと個人的には思っている)なDoctrineの、SELECTに関するメモ。

やりたかったことは、ある条件で抽出した複数行に1行ずつ処理を加えて書き戻すというフロー。
fetchArray()とかでは配列しか返ってこないので意味がなく、fetchOne()では先頭行しか返ってこない。
fetchOne()を複数回実行すればいいのかと思ったら無限ループに陥った。

findByではorderByをするのにフックを使わないといけないらしいのでパス。

一応、以下の方法で解決しました。

$collection = Doctrine_Query::create()
    ->select('u.*')
    ->from('Users u')
    ->where('u.flag = ?', '1')
    ->orderBy('u.id')
    ->execute();

var_dump(get_class($collection)); // Doctrine_Collection

$iter = $collection->getIterator();
var_dump(get_class($iter)); // ArrayIterator

while ($record = $iter->current()) {
    var_dump(get_class($record)); // Users
    var_dump($record->getId());
    $iter->next();
}

next()呼ばないといけないのは面倒だなと思ったら、foreachで使えたらしい。

$collection = Doctrine_Query::create()
    ->select('u.*')
    ->from('Users u')
    ->where('u.flag = ?', '1')
    ->orderBy('u.id')
    ->execute();

var_dump(get_class($collection)); // Doctrine_Collection

$iter = $collection->getIterator();
var_dump(get_class($iter)); // ArrayIterator

foreach ($iter as $record) {
    var_dump(get_class($record)); // Users
    var_dump($record->getId());
}

これでかなりマシになった。

最終的にはこれで。

$collection = Doctrine_Query::create()
    ->select('u.*')
    ->from('Users u')
    ->where('u.flag = ?', '1')
    ->orderBy('u.id')
    ->execute();

var_dump(get_class($collection)); // Doctrine_Collection

foreach ($collection->getIterator() as $record) {
    var_dump(get_class($record)); // Users
    var_dump($record->getId());
}

あとはループ内で$record['data'] = ‘hoge’;$record->save();とかしておけばOK。

Silverlight3とBlend3とビヘイビア

Silverlightの開発、ビヘイビアが結構便利なのだけど、あまりまだ洗練されてないのと情報少ないので発見したこと共有しておきます。

まとめを書きたいけどちょっと時間取れないので、ざっくり、どんなものかを書いておきます。

とにかくコードを書かなくて良くなる

コード書く量が減れば、コードをビジネスロジックに集中できるので、見通しが良くなります。デザイナに渡すのも楽かもしれないですね。

簡単な処理はストーリーボードも不要

全部ストーリーボードでやろうとしていたら、結局制御のためにプログラムを書かなきゃいけない事態になっていたので、これも便利ですね。イージングもかけられます。

ときどき動かない条件があるらしい?

マウスオーバーで画像を切り替える処理をやりたくて、ChangePropertyActionに親コントロール(Image)のSourceを切り替える処理をやらせたら、Silverlight自体表示されなくなってしまいました。ローディング100%の状態で先に進まない…。

他にもHyperlinkActionに直接http://から始まるURLを書いたら同じ現象になりました。使い方間違ってるだけだとは思うけど。これについての情報はこっちで解決。

Blend 3 silverlight application & website

<HyperlinkButton Tag="/Views/Page1.xaml" Background="Black" Height="47" HorizontalAlignment="Left" VerticalAlignment="Top" Width="133" NavigateUri="/Page1.xaml" Cursor="Stylus" />

ビヘイビア使わずにHyperlinkButtonにするってことですね…

状態を制するとインターフェースの設計が楽になる

やっぱりインターフェースを重点的に考えられているだけあって、状態を作ってしまえば、あとは状態の遷移だけ意識すればいいという点で便利。

マウスオーバーでずるっと出てくるメニューとか、GoToStateActionを使うと簡単に作れます。状態を先に保存しておいて、それぞれの状態を切り替える処理をクリックイベントだのチェックボックスのチェックだののイベントに関連付けられます。

ただし、「状態」を使うために別コントロールにしておく必要あり。あと、別コントロールにしたあと、一度ビルドしないとプロジェクト内の他のXAMLから使えないっぽい?アセットにコントロールが出てこないと焦ったときはとりあえずビルド。

マウスオーバーでずるっと出てくるメニューはストーリーボードでやらないほうがいい

なぜならアニメーション中にフォーカス外れたとき、MouseLeaveイベントにストーリーボードのBeginとか書いてあると、メニューが出きっていないのにストーリーボードのスタート地点で出きった位置に飛ばされるからです。

その点、GoToStateActionはその辺もきれいに処理してくれるので便利。

ずるずる並び替えられる処理もビヘイビアで

FluidMoveBehaviorを使えばパネルの中の要素をドラッグアンドドロップで変更する処理を一発で設定できるらしいです。試してないけど。

動画あった。これかな?

Animated WPF Panels (animating collection views) | Software and UX

トリガーはビヘイビアを選択したときにプロパティに出てくる

もう一度言う、「トリガーはビヘイビアを選択したときにプロパティに出てくる」。わかりにくい探しにくい…一度覚えてしまえば大丈夫なのだろうけど…

ついでに言うと、状態の選択を解除するには状態タブの中のベースを選択しておけばいいと思う。

その他、デモとか

色々解説してくれているサイトがあったので、紹介しておきます。

kirupa.com – Using Custom Visual States, Page 1 (Visual StatesとGoToStateActionの話)

kirupa.com – Using the FluidMove Behavior, Page 1 (FluidMoveBehaviorのデモあり)

サイト上で全部のデモを見られないのが残念だけど、ソース公開してくれているので、一度ダウンロードして動きを見てみるといいと思いますよ。

Encoding.GetString(Byte[])はSilverlightで使えない

今日のびっくり。

‘System.Text.Encoding.GetString(byte[])’ はアクセスできない保護レベルになっています。

Encoding.UTF8.GetString(data);

こんなコードを書くとビルド時エラーになります。

なぜか、Encoding.GetString(Byte[], Int32, Int32)は使えます。

Encoding.UTF8.GetString(data, 0, data.Length);

こうしておけばOK。何だろう、この仕様。

今日の微妙エラー

コントロールはひとつの case ラベル (‘default:’) から別のラベルへ流れ落ちることはできません。

switch (message)
{
    case "hoge":
        funcA();
        break;
    default:
        funcB();
        break; // これがないとエラー
}

他の言語のクセでbreak書かなかったら起きた。

「フォールスルー」ってことなんですね。

ちなみにcaseのあとのブロックにコードを書かなければフォールスルーはできるという話。

Windowsフォームへの、やたら簡単なSilverlightホスティング方法

  1. VisualStudioでフォームをひとつ作ります。
  2. WebBrowserコントロールを配置します。
  3. フォームのLoadイベントに以下を記述。
        private void Form1_Load(object sender, EventArgs e)
        {
            string html = @"<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN"" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"">
<!-- saved from url=(0014)about:internet -->

<html xmlns=""http://www.w3.org/1999/xhtml"" >
 <head>
  <title>Test</title>

  <script type=""text/xaml"" id=""testXaml"">
<?xml version=""1.0""?>
<Canvas xmlns=""http://schemas.microsoft.com/client/2007"">
 <Ellipse Fill=""Pink"" Width=""200"" Height=""100"" Canvas.Left=""40"" Canvas.Top=""30"" />
 <Rectangle Fill=""Orange"" Width=""100"" Height=""160"" Canvas.Left=""150"" Canvas.Top=""50"" />
 <TextBlock Text=""This is text on Silverlight"" Canvas.Left=""10"" Canvas.Top=""150"">
  <TextBlock.RenderTransform>
   <RotateTransform Angle=""-30"" />
  </TextBlock.RenderTransform>
 </TextBlock>
</Canvas>
  </script>
 </head>

 <body>
   <object type=""application/x-silverlight-2"" width=""800"" height=""600"">
   <param name=""source"" value=""#testXaml""/>
  </object>
 </body>
</html>";
            if (webBrowser1.Document == null)
            {
                webBrowser1.Navigate("about:blank");
            }
            webBrowser1.Document.OpenNew(true);
            webBrowser1.Document.Write(html);
        }

結果

20100127103949

XAMLソースはこちらから拝借。

【コラム】The Silverlight Times (16) 覚えておきたい「インラインXAML」 | ネット | マイコミジャーナル

WebBrowserの使い方はこちらを参考にしました。

WebBrowserコントロールのコンテンツを文字列により設定するには?[2.0、C#、VB] - @IT

自作ドライバを配布するためのインストーラー

DPInst.exeというものがとにかく便利すぎたのでメモ。

何せ上記実行ファイルを配布したいinfと同じフォルダに置くだけ。DPInst.exeを実行すればインストールが開始されます。

参考にしたのはこちらのサイト。

DPInst.exeの使い方 / How to use DPInst.exe WinUSB.sys事始め/ウェブリブログ

インストール画面をカスタマイズしたい場合に限り、定義XMLファイルを設置すればいいようです。

サンプルとして使えるXMLや画像一式は以下のフォルダにあります。

C:\WinDDK\7600.16385.0\src\setup\DPInst

日本語の場合はlanguage codeに0×0411をセット。

<language code="0×0411">

あとはUTF-8にして日本語で記述できます。

0×0411を定義しておかないと、

現在の言語では、デバイス ドライバのインストール ウィザードはサポートされません。

このパッケージを提供した製造元に問い合わせてください。

と表示されて先に進めません。

ちなみにNSISでやることを最初考えていて、それは以下のページに書いてありました。

Driver installation and update – NSIS

こっちはまだ試していないのですが、アプリケーションとかも一緒にインストールする場合にはNSISの方が便利かも。

hid.libをリンクしようとしたときにLNK2001エラーになる問題

MUDFでドライバ開発中なのですが、hid.libを使いたくて、sourcesにhid.lib追加、internal.hにHidsdi.hのインクルード記述をしてみたところ、ビルド時にLNK2001エラーが発生。

結局はHidsdi.hがCソースなのでextern "C"{}で囲ってやればOKというお話です。

extern "C"{
#include <Hidsdi.h>
}

SafariのkCFErrorDomainCFNetwork error 303.

iPhone用のWebサイトを作っていてSafariで発生した
Operation could not be completed. (kCFErrorDomainCFNetwork error 303.)
というエラー。

ページを読み込む途中で上記のエラーで止まってしまうので悩んでいたのですが、Proxyが問題でした。

というか、サーバがTransfer-Encoding: chunkedで送信してきた場合に、Proxyの挙動が仕様と異なる場合に起こるようです。TCPコネクションの問題かな…?

Transfer-Encoding: chunkedになるのは主にサーバからPHPとかで動的にページ書き出す場合ですね。

サーバ側でContent-Lengthを返すようにするか、Proxyを外すかってところですかね。

Fiddler使う際には注意。

リバースProxy環境でのmixiアプリのOAuth

CakePHPでmixiアプリのOAuthを処理させる方法は以下のサイト参照。ホントにシンプルでわかりやすい解説。

[cakephp] mixiアプリのOAuthのリクエストを受け取る – 「のーぶるじゃすぱー」略して「のぶじゃす」のBLOG

通常の環境ではこのまま実装すればOK。

で、このあとロードバランサーの入ったサーバに持ってきたらOAuthが正しく認証されない。

OAuthRequestのhttp_urlを見てみると、

http://192.168.0.2:8001:80//api/hoge/hoge/…

という悲しいことになっていて、これがどうも問題のようだと。というか、ホスト:ポート:ポートってどういうことだ…

本来ならもちろん、

http://mixiapp.sample.com:80/api/hoge/hoge/…

みたいなアドレスになっていないといけない。

ちなみに送信側はmixiアプリのサンプルそのまんまな感じのJavaScript

var params = {};
params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = 0;
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
var url = "http://mixiapp.sample.com/api/hoge/hoge/";
gadgets.io.makeRequest(url, func, params);

解決策は

function validateOAuth () {

    $get = $_GET;
    unset($get['url']);
    $http_url = 'http://mixiapp.sample.com:80/' . ltrim($_SERVER['REQUEST_URI'],'/');
    $request = OAuthRequest::from_request(null, $http_url, array_merge($get, $_POST));
    if ($_GET['oauth_consumer_key'] == 'mixi.jp') {
        @$signature_valid = $this->MixiAppliOauth->check_signature($request, null, null, $get["oauth_signature"]);
    }

    if ($signature_valid == true || Configure::read('debug')) {

        $this->oauth_valid = true;
        $this->viewer_id   = $get['opensocial_viewer_id'];
        $this->owner_id    = $get['opensocial_owner_id'];

        return $get;

    }

    return false;

}

というように、from_requestの第二引数にURLを指定してやればOK。なぜかスラッシュが2つ付いていたので削って付けなおしてます。

iPhoneのSafariでtouchendイベントを使うときの注意

iPhone用のドラッグアンドドロップで操作できるWebアプリを作っていて、うっかりはまったtouchendの挙動についてのメモ。

最初に書いたコードはこんな感じでした。

function touchhandler(e) {
    var x = e.touches[0].pageX;
    switch (e.type) {
        case 'touchstart':
            break;
        case 'touchmove':
            break;
        case 'touchend':
            break;
    }
}

document.getElementById('sample').addEventListener("touchend", touchhandler, false);

このプログラムではtouchendの処理が実行されません。

というのもまぁ、気づけば簡単なことなんですが、最後の指をスクリーンから離してtouchendのイベントが発生するときは、もうスクリーンに触ってないのでe.touches[0]がundefinedになるんですね…

なので、touchendのときは、引数で受け取ったe.touchesは注意しましょうという話。

今回参考にさせていただいたのは以下のサイト。

flashcast:フリーで働くITエンジニア集団のブログ: ipod touch用のWeb Applicationを作成してみる(canvasでお絵かき編)

SitePen

Web開発参考サイト – iPhone 3G DevWiki