概要
フォーム入力値をPOSTする前に扱いたいときは
Symfony\Component\Form\CallbackTransformer
を利用する。
公式ドキュメント:https://symfony.com/doc/current/form/data_transformers.html
経緯
EC-CUBE4のプラグイン開発中に、チェックボックスから入力する値を関係テーブルを作らずに1カラムにカンマ区切りで保存したいという場面に出会った。
当初は入力値がPOSTされた後にPOST値からパラメータを取り出してカンマ区切りの文字列に丸めてもう一度セットして保存、のような実装を行っていたがオブジェクトに出入りする値の型が変わりまくるため非常に気持ち悪く可読性に乏しい。
また、DBから取り出した文字列を配列にパースする処理も同一のアクションに含まれていたため、一目で見てわかるような処理でなかった。
そのため、フォームイベントなどを利用した実装はないかと考えて辿り着いた実装方法。
ちなみに当初の実装は↓のような感じ。
対象のパラメータは Config
というエンティティの param
というプロパティの想定で記載する。
また、 param
はフォームではチェックボックス(配列)、DBではカンマ区切りの文字列として扱われるものとする。
public function index(Request $request) {
$Config = $this->configReposiory->get(); // ← ここでとれるparamはカンマ区切りの文字列。
$param = $Config->getParam();
$param = explode(',', $param); // ← フォーム初期表示に使うために文字列 → 配列にパースする。
$Config->setParam($param);
$form = $this->createForm(ConfigType::class, $Config);
$form = handleRequest($request);
$if ($form->isSubmitted() && $form->isValid()) {
$Config = $form->getData(); // ← ここでとれるparamはフォーム入力値なので配列
$param = $Config->getParam();
$param = implode(',', $param); // ← DB登録のために配列 → 文字列に丸める。
$Config->setParam($param);
}
~~ 省略 ~~
$this->em->persist($Config);
$this->em->flush($Config);
}
上記ソースにコメントで示したようにそれぞれ前処理する必要があり、あまり親切でない。
実際には省略部でもっと値を利用したい場合も多いため、結構面倒。
解決策
フォーム入力は文字列、取り出しは配列で行うことができればよいわけだから、
-
Symfony\Component\Form\CallbackTransformer
を利用して、配列 → 文字列 としてPOSTされるようにする。 - エンティティに
parseParam()
というメソッドを定義して、getParam()
の代わりに利用する。
※ゲッタを単にプロパティを返すだけのメソッドでなく、文字列をパースする機能とするのはアンチパターンと考えたため2のようにしたが、本当は取り出しも意識せずに行えるようにしたい。なんかうまい方法あったら教えてください。
- 入力側
use Symfony\Component\Form\CallbackTransformer; // コンポーネント読み込みを忘れずに!
class ConfigType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
$transformer = new CallbackTransformer(
function($string) {
$decode = explode(',', $string);
},
function($array) {
$encode = implode(',', $array);
}
)
$builder
->add('param',ChoiceType::class, [
'choices' => $choices,
'multiple' => true,
'expanded' => true,
]);
$builder
->get('param')
->addModelTransformer($transformer);
}
}
- 出力側
/*
* @var string
*
* @ORM\Column(name="param", type="string")
*/
private $param;
public setParam($param) {
$this->param = $param;
return $this;
}
public getParam() {
return $this->param;
}
// ↓こいつを新しく実装した。
public parseParam() {
$ret = explode(',', $this->param);
return $ret
}
これによってコントローラは下記のようになった。
public function index(Request $request) {
$Config = $this->configReposiory->get();
$form = $this->createForm(ConfigType::class, $Config); // transformerで 文字列 → 配列 としてくれる!!
$form = handleRequest($request);
$if ($form->isSubmitted() && $form->isValid()) {
$Config = $form->getData(); // ← 入力時も 配列 → 文字列 としてくれるので心配無用。
$parse= $Config->parseParam(); // 文字列をパースしながら取得する
~~ ↑ ビジネスロジックで扱うときは配列の方が都合がよいケースが多い ~~
$this->em->persist($Config);
$this->em->flush($Config);
}
}
あとがき
スッキリ!
もっといい実装や指摘あったらお待ちしてます!
宣伝
EC-CUBE、Laravelの開発お任せください!!