PostgreSQL勉強会@札幌 2011-08-09

PostgreSQL勉強会@札幌で話をしてきました。

DBの問題をDBレイヤーで解決する試みだと思って見てください。


個人的には、一意な部分インデックスくらいは張っといてほしいと思いますが、TRIGGERやRULEを駆使されたDBは正直めんどかなーと思います。


どの方法もキー更新もトリガーもそれにかかるコストの問題は残ってますね。
当日は、「別表に削除データを追い出せば、INDEXのサイズを抑えられる。」という意見があったり、「高コストな削除処理は、削除フラグだけ立てて深夜のバッチで回収する」という話もしました。結局どこかとのトレードオフになるんだと思います。


あと、スライドにはありませんが、当日はORM的な解決法(soft-delete系plugin)にも軽く触れてます。
SE-PostgreSQLの話はごくごく簡単な紹介だけしました。
削除データのテーブルに継承を使うという話もしました。


あとで気づいたんですが、43ページのエントリーテーブルのデータにマイナスのものがありますがプラスの間違いです。


削除フラグは宗教ですね。思想が合わないからと言って争わないでください:)

ほむほむ

ほむほむ言わせるのが流行ってるみたいですね。

言語を作る気はさらさらないので、手っ取り早くperlのフィルター使ってほむほむしちゃえばいいんじゃね?ってことでAcme::Buffyをパクってこんな感じでしょうか?

package Acme::Homura;
use strict;
use warnings;
our $VERSION = '1.5';

my $horns = "ほむ " x 2;

sub _slay {
    my $willow = unpack "b*", pop;
    my $demons = $horns;
    foreach ( split //, $willow ) {
        $demons .= $_ ? 'ほむほむほむ ' : 'ほむほむ ';
    }
    return $demons;
}

sub _unslay {
    my $demons = pop;
    $demons =~ s/^$horns//g;
    my @willow;
    foreach ( split / /, $demons ) {
        push @willow, /ほむほむほむ/ ? 1 : 0;
    }
    return pack "b*", join '', @willow;
}

sub _evil {
    return $_[0] =~ /\S/;
}

sub _punch {
    return $_[0] =~ /^$horns/;
}

sub import {
    open 0 or print "Can't rehomura '$0'\n" and exit;
    ( my $demon = join "", <0> ) =~ s/.*^\s*use\s+Acme::Homura\s*;\n//sm;
    local $SIG{__WARN__} = \&evil;
    do { eval _unslay $demon; exit }
        unless _evil $demon and not _punch $demon;
    open my $fh, ">$0.homu" or print "Cannot homura '$0'\n" and exit;
    print $fh _slay $demon and exit;
    return;
}
1;

Hello World!を出力するならこんな感じで書いたファイルを保存して

ほむ ほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほ む ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむ ほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほ む ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほ むほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほ むほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほ む ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほ む ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほ むほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむほむ ほむほむ ほむほむ ほむほむ ほむほむ

でもって、こう。

$ perl -MAcme::Homura ./test.pl.homu
Hello World!

めでたしめでたし。


ちなみに上のコマンドは、素のperlスクリプトに対して実行すると、XXXX.homu というファイル名でほむほむコードに変換したファイルを出力します。
上のほむほむ言ってるコードの元はこれです。

#!/usr/bin/perl

print "Hello World\n";

ほむほむの代わりに猫マナーだとこんなのも可能。

猫マナー 猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫 マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ー ナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫 マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ー ナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ー ナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ー ナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ー ナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫 マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ー ナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ー ナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫 マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ー ナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ー ナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ー ナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫 マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫 マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ー ナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 猫マナー猫マナー猫マナー ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫 ーナマ猫ーナマ猫ーナマ猫

第02回北海道情報セキュリティ勉強会(セキュポロ)が開催されますよ

近隣の人たちはこぞって参加するといいとおもうよ!
前回も濃かったけど、今回もきっと濃いよ!
2009年04月11日(土曜日)だよ!


アナウンス&参加登録はこちらから
http://secpolo.techtalk.jp/2ndworkshop


もしかするとおやつもあるかもよ!(゚∀゚)

ActionFromからtemplateを生成するプラグイン(β)

しばらくご無沙汰してました。
で、いきなり本題ですが、ActionFromを定義したら、templateファイルくらいは自動で生成してほしいと思いませんか?
そんなものぐさなあなたに送るプラグインです:)


以下ではプラグインをインストールした場合の動作について説明します。
プラグインのダウンロード先とインストール方法は最後に記載してます。
素のEthnaの挙動では無いので勘違いしないようにしてください。

概要

このプラグインを導入すると、ActionFormに定義した内容でtemplateファイルを出力することができるようになります。

使い方

1. actionの作成

例として、ユーザがプロフィールを入力する画面を想定します。入力項目は「姓」「名」「ニックネーム」「性別」にします。
まずはいつも通りの手順でアクションを作成します。

ethna add-action profile_add
ethna add-action profile_add_do

user_addがフォームを表示するactionで、user_add_doが入力値を受け取るactionになることを想定しています。

2. ActionFormの編集。

いつも通りの手順でuser_add_doを編集して、フォームから受け取る値を定義します。
ここでは以下のようなActionFormを定義します。

<?php
class APPID_Form_ProfileAddDo extends APPID_ActionForm
{
    var $form = array(
        'lastname' => array(
            'name'          => '',
            'required'      => true,
            'type'          => VAR_TYPE_STRING,
            'form_type'     => FORM_TYPE_TEXT,
        ),
        'firstname' => array(
            'name'          => '',
            'required'      => true,
            'type'          => VAR_TYPE_STRING,
            'form_type'     => FORM_TYPE_TEXT
        ),
        'nickname' => array(
            'name'          => 'ニックネーム',
            'required'      => true,
            'type'          => VAR_TYPE_STRING,
            'form_type'     => FORM_TYPE_TEXT,
        ),
        'sex' => array(
            'name'          => '性別',
            'required'      => true,
            'type'          => VAR_TYPE_INT,
            'form_type'     => FORM_TYPE_RADIO,
            'option'        => array(1 => '', 2 => ''),
        ),
   );
}
class APPID_Action_ProfileAddDo extends APPID_ActionClass
{
    function prepare()
    {
        if ($this->af->validate() > 0) {
            return 'profile_add';
        }
        return null;
    }
}
?>
3. プラグインの実行

これでプラグインを実行する準備は完了しました。templateファイルを自動で生成する為に以下のコマンドを実行してください。
第一引数はプラグイン名、第二引数がターゲットとなるテンプレート名、第三引数が先ほど作成したActionFormのアクション名になります。

ethna build-form profile_add_do profile_add

以上で完了です。

4. 動作確認

ブラウザからprofile_addにアクセスすると、以下の画面が表示されます。これはプラグインによって自動生成されたページです。

バリデーションでエラーが発生した場合は、以下の画面が表示されます。

で、何が起きたの?

このプラグインを実行すると、user_add_doのActionFormに設定された内容で、user_addのtemplateを出力します。
出力されたテンプレートファイルは以下の様になります(抜粋)

    <body>
        <h1>profile_add</h1>
        <div class="buildform">

            {if count($errors)}
            <div class="errors">
                <p><em>Oops... the following errors were encountered:</em></p>
                <ul>
                {foreach from=$errors item=error}
                    <li>{$error}</li>
                {/foreach}
                </ul>
            </div>
            {/if}
            {form ethna_action="profile_add_do"}
            <fieldset>
                <legend>profile_add_do</legend>
                <div class="row">
                {if is_error('lastname')}
                    <label for="lastname" class="error key">{form_name name="lastname"}<em>*</em></label>
                    {form_input name="lastname" id="lastname" class="error"}
                {else}
                    <label for="lastname" class="key">{form_name name="lastname"}<em>*</em></label>
                    {form_input name="lastname" id="lastname"}
                {/if}
                </div>
                <div class="row">
                {if is_error('firstname')}
                    <label for="firstname" class="error key">{form_name name="firstname"}<em>*</em></label>
                    {form_input name="firstname" id="firstname" class="error"}
                {else}
                    <label for="firstname" class="key">{form_name name="firstname"}<em>*</em></label>
                    {form_input name="firstname" id="firstname"}
                {/if}
                </div>
                <div class="row">
                {if is_error('nickname')}
                    <label for="nickname" class="error key">{form_name name="nickname"}<em>*</em></label>
                    {form_input name="nickname" id="nickname" class="error"}
                {else}
                    <label for="nickname" class="key">{form_name name="nickname"}<em>*</em></label>
                    {form_input name="nickname" id="nickname"}
                {/if}
                </div>
                <div class="row">
                {if is_error('sex')}
                    <label for="sex" class="error key">{form_name name="sex"}<em>*</em></label>
                    {form_input name="sex" id="sex" class="error"}
                {else}
                    <label for="sex" class="key">{form_name name="sex"}<em>*</em></label>
                    {form_input name="sex" id="sex"}
                {/if}
                </div>
            <div class="buttonrow">
            {form_submit value="OK"}
            </div>
            </fieldset>
            {/form}
        </div>
    </body>

ケルトンファイルを編集するには

phpテンプレートなので、phpで記述してください。skelファイル名はbuild.form.tplです。
テンプレート内では以下の値が使用できます。

$formActionFormの内容(array)中身はvar_dump()してください:)
$macro内部値(array)中身は(以下略
プロジェクト固有のスケルトンファイルはプロジェクトのトップディレクトリ配下にあるskelディレクトリに配置することが出来ます。

先生!生成したコードが意味不明です

現状のプラグインに含まれるテンプレートは、エラーの項目に色を付けたりlabelを張ったりしてるので、わかりにくいかもしれません。
というわけで、まずはテンプレートをシンプルにしてみましょう。おおよそこんな感じになります。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=<?php echo $macro['client_enc']; ?>">
    </head>
    <body>
        <h1><?php echo $macro['action_name']; ?></h1>
        {if count($errors)}
        <ul>
            {foreach from=$errors item=error}
            <li>{$error}</li>
            {/foreach}
        </ul>
        {/if}
<?php foreach($form as $action_name => $defs): ?>
        {form ethan_action="<?php echo $action_name; ?>"}
        <table>
<?php foreach($defs as $key => $def): ?>
            <tr>
                <th>{form_name name="<?php echo $key; ?>"}</th>
                <td>{form_input name="<?php echo $key; ?>"}</td>
            </tr>
<?php endforeach; ?>
        </table>
        {form_submit value="OK"}
        {/form}
<?php endforeach; ?>
    </body>
</html>

プラグインを通して出力されるコードは以下の様になります。

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <h1>profile_add</h1>
        {if count($errors)}
        <ul>
            {foreach from=$errors item=error}
            <li>{$error}</li>
            {/foreach}
        </ul>
        {/if}
        {form ethan_action="profile_add_do"}
        <table>
            <tr>
                <th>{form_name name="lastname"}</th>
                <td>{form_input name="lastname"}</td>
            </tr>
            <tr>
                <th>{form_name name="firstname"}</th>
                <td>{form_input name="firstname"}</td>
            </tr>
            <tr>
                <th>{form_name name="nickname"}</th>
                <td>{form_input name="nickname"}</td>
            </tr>
            <tr>
                <th>{form_name name="sex"}</th>
                <td>{form_input name="sex"}</td>
            </tr>
        </table>
        {form_submit value="OK"}
        {/form}
    </body>
</html>

残念な話

ActionFormでrequiredの値が変更になった場合は、再生成する必要があります。理由はテンプレートファイルの生成時にrequiredの項目に「*」をつけているからです。



この問題を解決するにはrequiredの値を取得するsmarty_function_form_required()的なフォームヘルパを作成してtemplateからrequiredの値を取得すれば良いのでしょうが、そこまでは手をつけていません。

「*」が必要なければ、スケルトンファイルから該当箇所を削除してください。



イイワケをすると、method(GET/POST)の指定とか、enctypeとか、入力ヒントとか、フォーム名(例えば"請求フォーム"とかの和名) とかがActionFormで設定できて、それがtemplateにも反映されたり、templateファイルが存在しない場合は都度自動で生成できたり、いろいろムニャムニャできた方が幸せになれるかなーとは思うのですが、改造しまくってデフォのEthnaから離れすぎるのもどうよ、と思って区切りのいいところでやめました:)

その他

ActionFormのテンプレートも通常通り利用可能です。

既存のアプリケーションに与える影響

テンプレートファイルを出力するだけなので、既存のアプリケーションへの影響はありません。

ダウンロード

2.5.0のpreview2で確認してます。PHP5でしかテストしていません。PHP4で動作したら教えてください:)

ダウンロードEthna_Plubin_BuildForm-0.0.tzg

インストール

ダウンロードしたファイルをEthnaをインストールした先のディレクトリで展開してください。

既知のバグ

  • 引数のアクション名・テンプレート名が複数指定できてしまいます。有効なのは先頭の2つです。

ToDo

  • オプションのテストをほとんどやってないのでやる
  • バグ修正

Debian(etch)向けのEthna-2.5.0-preview2パッケージ

パッケージ管理はaptにお任せした方が幸せになれますよ。たぶん。
…という訳で、Debian(etch)向けEthna-2.5.0-preview2のdebパッケージを置いておきます。

インストール方法

sources.listの追加

/etc/apt/sources.listに以下の行を追加してください。

deb http://www.brownmush.net/pub/packages/debian/ ./
deb-src http://www.brownmush.net/pub/packages/debian/ ./

続いて以下のコマンドを実行します。(aptitudeを利用する方はapt-getをaptitudeに読み替えてください)

# apt-get update
インストール

以下のコマンドを実行します。

# apt-get install ethna

途中で以下のメッセージが表示されるので、yを入力して先に進んでください。

WARNING: The following packages cannot be authenticated!
  ethna
Install these packages without verification [y/N]? y

以上でインストールは完了です。

インストール時の注意

phpがインストールされていない環境で上記コマンドを実行するとデフォルトでphp4をインストールしようとするので、php5を利用したい場合は以下のように明示的にphp5を指定します。

# apt-get install ethna php5 php5-cli

ソースの入手

パッケージをrebuildしたい場合は以下のコマンドでソース一式を入手してください。

# apt-get source ethna

パッケージを直接ダウンロードする

apt経由ではなくdebパッケージを直接ダウンロードしたい方はこちらからどうぞ。

URL
http://www.brownmush.net/pub/packages/debian/

依存関係

以下の依存関係を張ってあります。

PEAR依存しなくなったんじゃないの?というツッコミがありそうですが、EthnaクラスがPEARクラスに依存しなくなった…という意味なので、少なくともEthna_PearWrapper.phpPEARを呼んでいる以上、PEARを入れないとethnaコマンドが動きません。


あと、ethna add-projectを実行すると、デフォルトでPEAR::DBを利用するので、php-db(いわゆるPEAR::DB)をdependsするかが悩ましいところなのですが、このパッケージではRecommends扱いにしました。もし、PEAR::DBが必要なら、下記のコマンドでインストールしてください。もちろん他のDBアクセスレイヤーを使用しても構いません。

# apt-get install php-db

その他

  • パッケージングの都合上、ソースに以下の修正を加えてあります。patchはソースパッケージに含まれます(dpatch形式)
    • smartyのpathをetch向けに変更
    • phpバイナリのpathをetch向けに変更
    • Ethna本体のテストケース及びコマンドの削除
  • Ethna_PearWrapper.phpはパッケージを分割してphp-pearに依存関係を張るようにしたり、DB周りも同じような考え方で分割したり等、改良の余地はあると思いますが、その辺りは正式リリースになってから考えます:)

vi派のためのプラグイン

なんだかカッコいいタイトルをつけてますが、お遊びネタです。実用的ではないと思います:)
例えば「管理者がユーザ情報の編集を実行するアクション」を作ろうとして、こんな感じのコマンドを入力したとします。

ethna add-action admin_user_edit_do

で、カレントのディレクトリが/path/to/APPID/template/ja_JP/admin/userだったとします。
実際にファイルを編集しようとすると、

vi ../../../../app/action/Admin/User/Edit/Do.php  

とかやりたくなるんですよね。TABで補完したところでやっぱり面倒だったりするのです。
ディレクトリをさかのぼる間に何のファイルを開こうとしてたか忘れたりするのは自分だけでしょうか:)


実際は、add-actionした際に出力したファイル名が表示されるので、それをコピペすれば済んだりしますが、actionとviewとtemplateを行き来する場合などやっぱりディレクトリをさかのぼってしまいたくなるわけです。

プラグインの使い方

プラグイン導入後に以下のコマンドを叩きます。

ethna source admin_user_edit_do

すると、以下の3つのファイルをvimで同時に開きます。

アクション /path/to/APPID/app/action/Admin/User/Edit/Do.php
ビュー /path/to/APPID/app/view/User/Edit/Do.php
テンプレート /path/to/APPID/template/ja_JP/admin/user/edit/do.tpl

イメージはこんな感じです。

ちなみに分割された窓の移動はCtrl+wを2回押すと順番に移動します。

オプション

プロジェクトのetc配下にあるAPPID-ini.phpに以下の指定を追加することができます。




キーデフォルト値説明
sourceforce_openfalseファイルが無い場合に強制的に開くか
quoteシングルクォート(')ファイル名の前後につけるクォート文字
command
vim %s %s %s
実行するコマンド名
orderarray('action', 'view', 'template')コマンドに渡すファイル名の順番

設定のサンプルは以下となります。

<?php
$config = array(
    'source' => array(
        'force_open' => true,
        'quote' => '"',
        'command' => 'vim -o %s %s %s',
        'order' => array(
            'action', 'view', 'template',
        ),
    ),
);
?>

ちなみにcommandに'vim -O %s %s %s'を指定すると縦に3分割になります。

既知の問題

このプラグインからviを開こうとすると、以下の警告が出力されて1秒弱固まります。もっさりです。
ものすごくイライラするので覚悟しておいてください。

Vim: Warning: Output is not to a terminal

それと、viの画面描写が結構な頻度で乱れます。Ctrl+lを多用することになります。
ものすごくイライラするので覚悟しておいてください。


あと、viじゃなくてvimじゃね?的な突っ込みはやめてください:)

ダウンロード

Ethna 2.5.0のpreview1とpreview2で動作確認してます。


PHP5でしかテストしていません。PHP4で動作したら教えてください:)

ダウンロードEthna_Plugin_Handle_Source.php

インストール

Ethnaのインストール先ディレクトリにある./class/Plugin/Handle/配下にダウンロードしたファイルをコピーしてください。

Ethnaを多次元フォーム配列に対応させる


このpatchはEthna 2.5.0 preview3で正式に取り込まれました。
今後は下記の公式ドキュメントをご覧ください。
2009/01/30 追記

EthnaのActionFormを拡張して多次元のフォーム配列に対応させてみます。
ソースコードのtodoには多次元配列への対応したい旨の記述がありますが、待ちきれないので実装してしまいます。

Ethna 2.5.0のpreview1で確認してますが、preview2でも大丈夫だと思います。
追記[20081113]:preview1とpreview2で動作確認しました。
差分はpatchでまとめました。

以下ではpatchを当てた場合の動作について説明します。
patchのダウンロード先は最後に記載してます。
素のEthnaの挙動では無いので勘違いしないようにしてください。

ActionFormの書き方

このpatchを適用すると、中括弧"[]"の中にグループ名を指定して階層を分けることができるようになります。
例えば、「名前」と「自宅電話番号」、そして「携帯電話番号」を入力するフォームだと、以下のように書くことができます。

<?php
class APPID_Form_TestDo extends APPID_ActionForm
{
    var $form = array(
        'User[name]' => array(
            'name'          => '名前',
            'type'          => VAR_TYPE_STRING,
            'form_type'     => FORM_TYPE_TEXT,
        ),
        'User[phone][home]' => array(
            'name'          => '自宅電話番号',
            'type'          => VAR_TYPE_STRING,
            'form_type'     => FORM_TYPE_TEXT,
        ),
        'User[phone][mobile]' => array(
            'name'          => '携帯電話番号',
            'type'          => VAR_TYPE_STRING,
            'form_type'     => FORM_TYPE_TEXT,
        ),
    );
}
?>

テンプレート(フォームヘルパ)の書き方

フォームヘルパにはActionFormで指定したキーを必ずフルネームで指定してください。

{form ethna_action="test_do"}
<table>
    <tr>
        <th>{form_name name="User[name]"}</th>
        <td>{form_input name="User[name]"}</td>
    </tr>
    <tr>
        <th>{form_name name="User[phone][home]"}</th>
        <td>{form_input name="User[phone][home]"}</td>
    </tr>
    <tr>
        <th>{form_name name="User[phone][mobile]"}</th>
        <td>{form_input name="User[phone][mobile]"}</td>
    </tr>
</table>
{form_submit value="登録"}
{/form}

フォームに入力された値の取り出し方

以下のような値を入力したと仮定します。

フォームに入力された値は、以下に記載する、三通りの方法で取り出すことができるようになります。

1. 最下層のキーまで指定して値を取り出す

自宅電話番号を取り出す場合は以下のように書きます。

<?php
    $var = $this->af->get('User[phone][home]');
    print_r($var);
?>

この場合の出力は以下となります。

'01-2345-6789'

同様に、名前を取り出す場合は以下のように書きます。

<?php
    $var = $this->af->get('User[name]');
    print_r($var);
?>

この場合の出力は以下となります。

'剛田武'
2. 階層の途中から値を取り出す

自宅電話番号と携帯電話番号と取り出したい場合は、以下のように書きます。

<?php
    $var = $this->af->get('User[phone]');
    print_r($var);
?>

この場合の出力は以下となります。

array (
  'home' => '01-2345-6789',
  'mobile' => '090-1234-5678',
)
3. 親を指定してデータをすべて取り出す

名前と自宅電話番号、そして携帯電話番号をまとめて取り出す場合は以下のように書きます。

<?php
    $var = $this->af->get('User');
    print_r($var);
?>

この場合の出力は以下となります。

array (
  'name' => '剛田武',
  'phone' => 
  array (
    'home' => '01-2345-6789',
    'mobile' => '090-1234-5678',
  ),
)

フォームに値をセットする

フォームに値をセットするには以下のように書きます。値を取り出す場合と同様に三通りの方法でセットできます。

1. 最下層のキーまで指定して値をセットする

自宅電話番号をセットする場合は以下のように書きます。

<?php
    $this->af->set('User[phone][home]', '01-2345-6789');
?>

同様に、名前をセットする場合は以下のように書きます。

<?php
    $this->af->set('User[name]', '剛田武');
?>
2. 階層の途中から値をセットする

自宅電話番号と携帯電話番号とセットする場合は、以下のように書きます。

<?php
        $phone = array(
            'home'   => '01-2345-6789',
            'mobile' => '090-1234-5678',
        );
        $this->af->set('User[phone]', $phone);
?>
3. 親を指定してまとめて値をセットする

名前と自宅電話番号、そして携帯電話番号をまとめてセットする場合は以下のように書きます。

<?php
        $user = array (
            'name' => '剛田武',
            'phone' => array (
                'home' => '01-2345-6789',
                'mobile' => '090-1234-5678',
            ),
        );
        $this->af->set('User', $user);
?>

で、コレって何に使うの?

例えば、Doctrineと連携させて編集フォームを作成するときは、こんな感じで書けます。

  • 編集画面のActionClass
<?php
    function perform() // エラー処理は省略してます
    {
        // 該当IDのレコードを取得
        $user = Doctrine::getTable('User')->find($this->af->get('User[id]'))->toArray(true); 
        // データベースの値をフォームのデフォルト値として全部まとめてセットする
        $this->af->set('User', $user);

        // 編集フォームのviewを出力する
        return 'user_edit';
    }
?>

$userにはデータベースのカラム名をキーにしたデータが入っています。これをまとめてActionFormにセットします。

  • 編集完了画面のActionClass
<?php
    function perform() 
    {
        // 更新する該当IDのレコードを取得する
        $user = Doctrine::getTable('User')->find($this->af->get('User[id]')); 
        //入力されたデータを全部まとめてプロパティの値にマージする
        $user->merge($this->af->get('User')); 
        // データベースを更新する
        $user->save();

        // 編集完了画面のviewを出力する
        return 'user_edit_do'; 
    }
?> 

入力されたフォームの値をまとめて取得して、doctrineに渡しているのがポイントです。

難しい話

patchの当てた場合の仕様でちょっと難しい話を。興味の無い方は無視してください。

深い配列を指定した場合の挙動

以下のような深い階層のフォームでも使用できます。

<?php
    var $form = array(
        'test[a][b][c][d]' => array(
            'name'          => '深い配列',
            'type'          => VAR_TYPE_STRING,
            'form_type'     => FORM_TYPE_TEXT,
        ),
    );
?>

ただし、利用できる深さは10階層までに制限しています。
制限値は変更可能ですが、内部で再帰処理をしているので、実際には100階層が上限になります(これはPHPの仕様上の制限です)
配列の深さが制限を越えるとActionFormにsetしようとしても無視されます。またgetする場合もnullを返します。

ActionFormのform_varsはどうなるか

カスタムチェックのメソッドを使用する場合など、ActionFormのform_varsを直接参照しているケースがあると思います。
このpatchを適用することによって、form_varsの中身が階層構造になることに注意してください。
キーから希望するデータを取得するには、$this->get($name)を使用するのが簡単です。おおよそ以下のような書き方になります。

<?php
    function checkMobilePhone($name)
    {
        if (! preg_match('/^0\d0-\d{4}-\d{4}$/', $this->get($name))) { // $this->form_varsは使っていません
            // チェックメソッドを書かなくてもregexpで同じことができます:)
            $this->ae->add($name, "{form}には正しい番号を入力してください", E_FORM_INVALIDVALUE);
        }
    }
?>
配列型を指定した場合の挙動

配列の階層が増えるだけで、あとは通常のEthnaと変わりありません。
たとえば、以下のようなフォームを定義したとします。typeにarray(VAR_TYPE_STRING)を指定しています。

<?php
class APPID_Form_TestDo extends APPID_ActionForm
{
    var $form = array(
        'Artist[name]' => array(
            'name'          => '好きなキャラクター',
            'type'          => array(VAR_TYPE_STRING),
            'form_type'     => FORM_TYPE_TEXT,
        ),
    );
}?>

テンプレートは以下のように書いたとします。

{form ethna_action="test_do"}
<table>
    <tr>
        <th>{form_name name="Artist[name]"}</th>
        <td>
            <div>{form_input name="Artist[name]"}</div>
            <div>{form_input name="Artist[name]"}</div>
            <div>{form_input name="Artist[name]"}</div>
        </td>
    </tr>
</table>
{form_submit value="登録"}
{/form}

この場合、以下の方法でフォームに入力された情報を取得出来ます。

<?php
    $var1 = $this->af->get('Artist');
    $var2 = $this->af->get('Artist[name]');
    $var3 = $this->af->get('Artist[name][0]'); // 一つ目のフォームの値
    $var4 = $this->af->get('Artist[name][1]'); // 二つ目のフォームの値
?>
矛盾したフォームを定義した場合

多次元配列に対応したために、フォームに矛盾した定義を指定することができてしまいます。
例えば、ActionFormに以下をセットしたとします。

<?php
    var $form = array(
        'sample' => array(
            'name'          => '数値のみ指定可能な配列',
            'required'      => true,
            'type'          => array(VAR_TYPE_INT),
            'form_type'     => FORM_TYPE_TEXT,
        ),
        'sample[str]' => array(
            'name'          => '文字列が指定可能な多次元フォーム',
            'required'      => true,
            'type'          => VAR_TYPE_STRING,
            'form_type'     => FORM_TYPE_TEXT,
        ),
    );
?>

続いて、templateには以下のように指定したとします。

{form ethna_action="test_do"}
<table>
    <tr>
        <th>{form_name name="sample"}</th>
        <td>{form_input name="sample"}</td>
    </tr>
    <tr>
        <th>{form_name name="sample"}</th>
        <td>{form_input name="sample"}</td>
    </tr>
    <tr>
        <th>{form_name name="sample[str]"}</th>
        <td>{form_input name="sample[str]"}</td>
    </tr>
</table>
{form_submit value="登録"}
{/form}

この場合、多次元外列である"sample[str]"に対して、フォーム配列の"sample"で指定したバリデーションが有効になります。
その為、このケースの場合は、"sample[str]"に数値を入れない限りバリデーションが通りません。

ファイル型(VAR_TYPE_FILE)を指定した場合

他の型を使用した場合と特に変わりありません。つまり、階層構造を指定すれば、それに従った階層構造で値が取得できるようになります。
例えば、以下のような定義をしたとします。

<?php
    var $form = array(
        'File[foo]' => array(
            'name'          => 'ファイルfoo',
            'type'          => VAR_TYPE_FILE,
            'form_type'     => FORM_TYPE_FILE,
        ),
        'File[bar]' => array(
            'name'          => 'ファイルbar',
            'type'          => VAR_TYPE_FILE,
            'form_type'     => FORM_TYPE_FILE,
        ),
    );
?>

続いて、templateには以下のように指定したとします。

{form ethna_action="test_do" enctype="file"}
<table>
    <tr>
        <th>{form_name name="File[foo]"}</th>
        <td>{form_input name="File[foo]"}</td>
    </tr>
    <tr>
        <th>{form_name name="File[bar]"}</th>
        <td>{form_input name="File[bar]"}</td>
    </tr>
</table>
{form_submit value="登録"}
{/form}

そして、下記の取得でフォームの値を出力したとします。

<?php
    $var = $this->af->get('File');
    print_r($var);
?>

この場合の出力は以下となります。

array (
  'foo' => 
  array (
    'name' => 'favicon.ico',
    'type' => 'image/x-icon',
    'size' => 318,
    'tmp_name' => '/tmp/phps2O45r',
    'error' => 0,
  ),
  'bar' => 
  array (
    'name' => 'authorized_keys.txt',
    'type' => 'text/plain',
    'size' => 611,
    'tmp_name' => '/tmp/phpn3njWB',
    'error' => 0,
  ),
)

制限事項

このパッチでできないことを挙げます。

自動で番号を割り当てるフォーム

階層の途中に自動で番号を割り当てるような配列は使用できません。
例えば、以下のようなActionFormの定義は使用できません。

<?php
    var $form = array(
        'sample[][data1][data2]' => array(
            'name'          => 'テスト',
            'required'      => true,
            'type'          => VAR_TYPE_INT,
            'form_type'     => FORM_TYPE_TEXT,
        ),
    );
?>

おそらくsample[0][data1][data2]、sample[1][data1][data2]のような取得方法を期待してると思いますが、対応していません。

一次元フォームと多次元フォームの重複

一次元フォームと多次元フォームでキーが重複する場合は、どちらかの値が受け取れません。
追記[20090120]:
一次元フォームと多次元フォームでキーが重複する場合は、どちらかの値が受け取れる場合があり、両方受け取れない場合もありますので、このような定義は避けてください。
(詳しくはid:mumumu-tanさんからの日記コメントを確認してください)

<?php
    var $form = array(
        'sample' => array(
            'name'          => '数値(一次元)',
            'required'      => true,
            'type'          => VAR_TYPE_INT,
            'form_type'     => FORM_TYPE_TEXT,
        ),
        'sample[xxx]' => array(
            'name'          => '数値(多次元)',
            'required'      => true,
            'type'          => VAR_TYPE_INT,
            'form_type'     => FORM_TYPE_TEXT,
        ),
    );
?>

sampleのデータしか受け取ることができなかった場合、sample[xxx]のバリデーションも有効なので、結果的にバリデーションを通過できません。
またその逆のパターンとなる場合もあります。
FireFox(3.0.3)の場合はフォームの順序でどちらのフォームの値が送られるか変わるようです。
ブラウザごとの挙動は分かりませんが、このような定義は避けてください。

既存のアプリケーションに与える影響

以前の動作に影響を与えないように注意してますが、このパッチを適用する場合は十分にテストしてください。
Ethna本体に付属のethna_run_test.phpでチェックしたところ、パッチを当てる前後でテスト結果は同じになりました。
ただし、このテストは一次元の配列の場合のみのテストであることに注意してください。そして多次元配列フォームの動作を保障するものではありません。

注意

パッチの適用は自己責任でお願いします。
このpatchに関する不明な点をEthna本家のメーリングリストに質問しないでください。

ダウンロード

2.5.0のpreview1で確認してます。preview2以上で動作したら教えてください。
追記[20081113]: preview1とpreview2で動作確認しました。
おまけにPHP5でしかテストしていません。PHP4で動作したら教えてください:)

ダウンロードethna_multivars-2.5.0pre1-1.patch

patchの適用方法

ダウンロードしたパッチをEthna_ActionForm.phpに適用するには以下のコマンドを実行してください。

# cd /path/to/Ethna/class/
# patch < /path/to/patch/ethna_multivars-2.5.0pre1-1.patch

patchの対象となるファイルは、Ethna_ActionForm.phpのみです。
patchを当てる前にバックアップを取っておくことをお勧めします。

直接本体にpatchを当てたくない場合は、appディレクトリあたりにpatchを適用後のファイルを置いて、コントローラに手を加えてください。