MT::Objectでは精度仕様・桁数を指定したfloatは使えない
Locationで位置情報を保存後、保存直後に再構築したページでは正しい位置を指すが、再度編集画面を開くと、指定した位置がズレている、という不具合が見つかりました。
調べてみると、
8122 Geary Boulevard, San Francisco, CA 94121
Latitude 37.77938700000001 Longitude -122.506663
が保存後
Latitude 37.7794 Longitude -122.507
になっていました。
Floatの精度仕様と桁数
このような場合、DDLで必要な精度仕様と桁数を指定します。
float(桁数,小数点以下の桁数)
ググってると緯度、経度を扱う場合小数点以下6桁ぐらいあればよいようなので、
float(9,6)
というようになりますね。
MTの型指定ではどうやるの
MTでは独自オブジェクトやプラグインで追加するカラムの指定は、このように行います。
object_types:
modeltype:
colname: 型タイプ オプション
__PACKAGE__->install_properties ({
column_defs => {
'colname' => '型タイプ オプション',
参考:
文字列の場合長さを指定できるようになっており、このように指定します。
hoge: string(100)
この方式に従うと、floatではこうでしょうか?
lat: float(9,6)
float(9,6) で指定できそうでできない
試してみましたが、だめでした。普通に指定なしで作られました。
ソースを追ってみると
- カラム追加DDLを組み立てるために、
MT::ObjectDriver::DDL->add_column_sql
float(桁数,小数点以下の桁数)
のようなSQLを組み立てるために、MT::ObjectDriver::DDL->column_sql
- モデルのカラム定義を取得するために
MT::Object->column_def
MT::Object->__parse_def
でstring(100) not null
のような文字列から{'type' => 'string', 'size' => 1, 'not_null' => 1}
のような連想配列のリファレンスを作る- 連想配列のリファレンスを
MT::ObjectDriver::DDL->type2db
に渡して、varchar(100)
のような型指定を得る(MySQLの場合はサブクラスMT::ObjectDriver::DDL::mysql
のtype2db
が呼ばれます。)
といった流れになっています。
float(9,6)
で指定できないのは、
__parse_def
で(9,6)
が無視されるMT::ObjectDriver::DDL->type2db
でfloat
固定で桁数指定などを行わないことになっている
という2つの仕様によるもののようです。
float(9,6) で指定できるようにするには
以下のようにMT_DIR/lib
以下の各ファイルを変更することで対応できました。
__parse_def
で{'type' => 'float', 'precision' => 10, 'scale' => 6}
のように桁数と精度を連想配列に入れるようにするif ( $def =~ s/\((\d+),(\d+)\)// ) { $def{precision} = $1; $def{scale} = $2; }
MT::ObjectDriver::DDL::mysql->type2db
で連想配列に精度が含まれるときはfloat(桁数,小数点以下の桁数)
のような型指定を返すようにする。elsif ( $type eq 'float' ) { if ( $def->{precision} && $def->{scale} ) { return 'float(' . $def->{precision} . ',' . $def->{scale} . ')'; } else { return 'float'; } }
また、DBに作成済みのカラム情報をパースするため、
MT::ObjectDriver::DDL::mysql->column_defs
にも少し追加。my ($precision, $scale) = ( $coltype =~ m/float\((\d+),(\d+)\)/i ? ( $1, $2 ) : ( undef, undef ) ); $coltype = $ddl->db2type($coltype); if ( ( $coltype eq 'float' ) && $precision && $scale ) { $defs->{$colname}{precision} = $precision; $defs->{$colname}{scale} = $scale; }
その他、MySQL以外への対応が必要ならそれぞれ用のサブクラスへの変更が必要でしょう。
コアの書き換えが必要なんて現実的ではない
ので、結局文字列型にして範囲計算などは暗黙の型キャストを期待することにしました。ちょっとモヤモヤ。
object_types:
entry:
lat: string(20) indexed
lng: string(20) indexed