はじめに
前回のRepository編に続き、FormType編をやっていきます。
FormTypeはその名の通り、フォームを作成する元になるファイルです。
登録・編集フォームや検索フォームなど様々なFormTypeが作成されています。
FormTypeをマスターしようとすると、膨大な記事になりますし、僕もすべて使いこなせてるかと聞かれれば、「No!!」と答えます。
この記事では、基本的な実装方法と、拡張方法を説明します。
FormTypeの作成
参考にする本体ソース:Eccube/Form/Type/Front/EntryType.php
参考ソースを元に必要最低限な部分を取り出してみます。
namespace Eccube\Form\Type\Front;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class EntryType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    }
    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'entry';
    }
}
基本的には、この形に肉付けをしていけば良いかなと思います。
getBlockPrefixでリターンされてる文字列はname属性になり、name="entry[***]"という感じになります。
フィールド設定
さて、肉付けですが、buildForm内にaddで追加していきます。
基本的なパーツとしてはcompany_nameの設定が分かり易いかと。
$builder
    ->add('company_name', TextType::class, [
        'required' => false,
        'constraints' => [
            new Assert\Length([
                'max' => $this->eccubeConfig['eccube_stext_len'],
            ]),
        ],
    ]);
$builderにaddメソッドで、第1引数に名前、第2引数にフィールドタイプ、第3引数にoptionを設定できます。
第1引数の名前は好きにつけて良いのですが、後ほど説明するEntityと紐づけする設定を行うと、Entityに自動で値をセットしてくれるようになります。
そのため、紐づけたEntityのプロパティ名と同じ名前を付けないとエラーになってしまいます。
オプションでmappedを調べてみると幸せになる可能性もあります。
フィールドタイプやオプションは公式リファレンスをお読みください。
https://symfony.com/doc/3.4/reference/forms/types.html
symfony3.4のページですが、必要に応じてバージョン選択してください。
フィールドのグループ化
では、他にaddされたフィールドを見てみましょう。
その中にNameTypeやKanaType AddressTypeといったリファレンスにないフィールドが設定されています。
これは何か?
それは、useされている物を見れば分かりますが、Eccube\Form\Type\に作成されているFormTypeです。
例)Eccube/Form/Type/NameType
パッと見た感じ理解しがたいですが、name01とname02が設定されています。
出来上がったテキストフィールド名はname="entry[name][name01]"とname="entry[name][name02]"になっています。
オプションの設定をすれば、ある程度柔軟な使い回しが出来るように作られています。
頭がパンクしそうになるかもですが、本体ソースのFormTypeを追ってみてください。
結構利用できそうなものが作られていますよ。
Entityに紐づけ
Eccube/Form/Type/Front/EntryTypeは新規会員登録とマイページの登録情報編集で使われているFormTypeです。
ここで入力された情報は会員情報なので、Eccube/Entity/Customerにセットしなければなりません。
ですので、下記のメソッドでEntityクラスを紐づけます。
/**
 * {@inheritdoc}
 */
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'data_class' => 'Eccube\Entity\Customer',
    ]);
}
少しだけControllerを見てみましょう。
Eccube/Controller/EntryController
/** @var $Customer \Eccube\Entity\Customer */
$Customer = $this->customerRepository->newCustomer();
/* @var $builder \Symfony\Component\Form\FormBuilderInterface */
$builder = $this->formFactory->createBuilder(EntryType::class, $Customer);
createBuilderの時にEntryType::classとCustomerのEntityオブジェクトを設定していますね。
EntityのないContactだと
Eccube/Controller/ContactController
$builder = $this->formFactory->createBuilder(ContactType::class);
createBuilderの時にContactType::classだけ設定されています。
ContactTypeにもEntityを紐づけるような記述はないです。
Eccube/Form/Type/Front/ContactType
Form Events
Eccube/Form/Type/Front/EntryTypeでは、$builder->addEventListenerが二つ設定されています。
symfony公式ドキュメント(Form Events)
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
    $Customer = $event->getData();
    if ($Customer instanceof Customer && !$Customer->getId()) {
        $form = $event->getForm();
        $form->add('user_policy_check', CheckboxType::class, [
                'required' => true,
                'label' => null,
                'mapped' => false,
                'constraints' => [
                     new Assert\NotBlank(),
                ],
            ]);
    }
}
);
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
    $form = $event->getForm();
    /** @var Customer $Customer */
    $Customer = $event->getData();
    if ($Customer->getPassword() != '' && $Customer->getPassword() == $Customer->getEmail()) {
        $form['password']['first']->addError(new FormError(trans('common.password_eq_email')));
    }
});
この部分ですね。
FormEventsの種類は公式ドキュメントを見て頂きたいのですが、ここではPRE_SET_DATAとPOST_SUBMITが設定されています。
FormEvents::PRE_SET_DATA
ドキュメントの翻訳ですが、
- 事前入力中に指定されたデータを変更します。
- 事前入力されたデータに応じてフォームを変更します(フィールドを動的に追加または削除します)。
上記目的で使用されます。
EntryTypeは会員新規登録とマイページの情報編集で利用されています。
そしてPRE_SET_DATAの処理は、Entity/Customerのインスタンスであり、IDが否定された場合(nullだったら)user_policy_checkを追加するようになっています。
新規登録フォームの時は「利用規約に同意してお進みください」のチェックボックスを追加して、マイページの情報編集の時は追加しない処理になっています。
FormEvents::POST_SUBMIT
タイミング的にはControllerの$form->handleRequest($request);の時に呼び出されます。
処理の内容を見てみると、入力されたパスワードが空でなくて、パスワードとメールアドレスが一緒の時、パスワードはメールアドレスと同じ値を設定できません。をいうエラーを設定しています。
複雑なバリデーションを追加したい時に使う印象があります。
FormType作成の説明は以上
FormType作成の説明は以上なのですが、伝えたい事が沢山ありすぎて、この説明でいいのか不安ではあります。
分からなければ、symfony公式を見る、ググル、勉強会へ参加してみる、質問してみるなどしてみましょう。
FormType拡張
EC-CUBEのドキュメントに拡張方法が記されています。
FormTypeのカスタマイズ
こちらを読むだけで大丈夫かなと思いますので、ここでの説明は省きます。
最後に
FromTypeを作成している時に、あーしたいこーしたいで躓くことが多々あります。
そうなった場合は、自分の検索力がものをいうと思いますので、頑張ってください。。。
必要な時、必要な事を検索して身に付けるだけで、気が付けばデキル人になっているものです。
Agentに対してのFormTypeですが、後々すべてを公開しますので、しばらくおまちください。
色々書いてみましたが、FromTypeについての説明はまだまだ足りません。
この記事で逆に迷わすような事になるかもと思いましたが、それでも少しでも参考になればと思います。