Issei.M's Techlog

Web/iOS エンジニアの僕が技術関連のメモ等をつらつらと。主に Symfony について書いています。

[Symfony][Form] フィールドタイプはよく考えて決めよう

Symfony2を使い始めて2年くらい経ちましたが、未だ全容をつかみきれない機能の1つがForm。今回はそんなFormのフィールドタイプについて話します。

通常、Symfony2のFormではフィールドタイプを次のようにして定義します。

$builder->add('addressLine', 'text', ['label' => '住所']);
$builder->add('latitude', 'hidden');
$builder->add('longitude', 'hidden');

テンプレートはこんな感じ。

{{ form_start(form) }}
  {{ form_row(form.addressLine) }}
  <button type="submit">送信</button>
{{ form_end(form) }} {# hiddenフィールドはここで自動出力されます #}

この場合、画面上には「住所」を入力するテキストフィールドだけが表示されますが、サブミットの直前に住所の内容からJavaScriptでGoogleMap APIから取得し、hiddenタイプの緯度・経度に位置情報が入る仕様だとします。(実装例は割愛)

サブミットを行うと、データは次のようにマップされます。

$form->handleRequest($request);
var_dump($form->getData());

/*
 * ※DTOは使わないのでデータは配列です。
 * [
 *   'addressLine' => string '東京都渋谷区...',
 *   'longitude'   => string '35.658534',
 *   'latitude'    => string '139.701330',
 * ]
 */

緯度経度がfloatではなくstringとなっています。当然といえば当然なのですが...。
因みにはじめからnumberタイプを定義していれば、適切にfloat型としてマップされます。

$builder->add('latitude', 'number');
$builder->add('longitude', 'number');

※但しこの場合はinput[type=text]として画面上にUIが表示されます。

クライアントサイドの都合で安直にhiddenを使うと今回のような悲劇を生みます。(´・ω・`)
解決法は色々ありますが今回のケースではオーソドックスに DataTransformer を使うのが良さそうです。

$builder->add(
    $builder
        ->create('latitude', 'hidden')
        ->addViewTransformer(new NumberToLocalizedStringTransformer())
);
$builder->add(
    $builder
        ->create('longitude', 'hidden')
        ->addViewTransformer(new NumberToLocalizedStringTransformer())
);

この他にDataTransformerを使わずにnumberタイプを定義しておいて、テンプレート側でhiddenにすると言う方法もあります。

{{ form_start(form) }}
  {{ form_row(form.addressLine) }}
  {{ form_row(form.longitude, {type: 'hidden'}) }}
  {{ form_row(form.latitude, {type: 'hidden'}) }}
  <button type="submit">送信</button>
{{ form_end(form) }}

この場合form_endでのhiddenフィールド自動出力の恩恵が受けられませんが、最終的なUIがテキストボックスであればこちらの方が良いかもしれません。
Symfony2が予め用意しているフィールドタイプは、numberのようにデータを適切に変換してくれる物が多いからです。

何はともあれこれでデータは適切にfloatに変換されました。めでたしめでたし。