[EC-CUBE4]フォームの入力項目に独自のバリデーションルールを追加する

EC-CUBE4をやっている

転職してからお仕事でEC-CUBE4をちょこちょこ触っています。半年くらい使ってるとなんとなーくこういう時こうすれば良いというのが分かってきた気はするんですけど、はじめた頃は国産なこともあり英語の情報が全くなく、かと言って日本語の情報が充実してるかというと探すのにちょっと一苦労する感じだったのでぼちぼち記事に残していこうかなと思っています。

とは言え・・・

あまり技術トピックを書いたことがないので、どのような書き方が良いかなというのはあります。ひとまずいっちょやってみましょう。

バリデーションルールの追加

バリデーションルールだったり言い方はいろいろあるんでしょうけど入力チェックのことです。
先日「存在しない郵便番号が入力されないようにしてほしい」という要望が顧客からあって対応した時にいろいろ使えそうだなと思ったので、その時にやったことを例に解説します。

会員登録フォームを見てみる

EC-CUBE4ではFormTypeの仕組みを使ってフォームの情報を設定出来るようになっています。
Controllerのメソッドの処理を読んでるとFormTypeの定義がされているので、会員登録の処理を辺りを辿ってからなんとなーく郵便番号の入力項目がどのように定義されているかを見てみました。

src/Eccube/Form/Type/Front/EntryType.php

/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
   $builder
       ->add('name', NameType::class, [
           'required' => true,
       ])
       ->add('kana', KanaType::class, [])
       ->add('company_name', TextType::class, [
           'required' => false,
           'constraints' => [
               new Assert\Length([
                   'max' => $this->eccubeConfig['eccube_stext_len'],
               ]),
           ],
       ])
       ->add('postal_code', PostalType::class)
       ->add('address', AddressType::class)
       ->add('phone_number', PhoneNumberType::class, [
           'required' => true,
       ])
       ->add('email', RepeatedEmailType::class)
       ->add('password', RepeatedPasswordType::class)
       ->add('birth', BirthdayType::class, [
           'required' => false,
           'input' => 'datetime', 
           'years' => range(date('Y'), date('Y') - $this->eccubeConfig['eccube_birth_max']),
           'widget' => 'choice',
           'format' => 'yyyy/MM/dd',
           'placeholder' => ['year' => '----', 'month' => '--', 'day' => '--'],
           'constraints' => [
               new Assert\LessThanOrEqual([
                   'value' => date('Y-m-d', strtotime('-1 day')),
                   'message' => 'form_error.select_is_future_or_now_date',
               ]),
           ],
       ])
       ->add('sex', SexType::class, [
           'required' => false,
       ])
       ->add('job', JobType::class, [
           'required' => false,
       ]);
}

上のは会員登録フォームのFormTypeから一部抜粋したものです。buildForm関数の中で$builder変数にaddする形でフォームの項目を追加定義出来るようになっています。
郵便番号は第1引数にpostal_codeが渡されているところがそうなんですけど、第二引数で渡されているPostalTypeはちょっと特殊で郵便番号の入力項目で使われているクラスみたいです。1つ1つの定義に当てていくのは面倒なので今回はこのPostalTypeを拡張します。

FormExtensionを使って拡張する

あとはチュートリアルにもあるようにFormExtensionの仕組みを使って、郵便番号に独自のバリデーションルールを定義します。下記のパスに追加して試してみてください 🙂

FormTypeのカスタマイズ
https://doc4.ec-cube.net/customize_formtype

app/Customize/Form/Extension/PostalTypeExtension.php

<?php

namespace Customize\Form\Extension;

use Eccube\Common\EccubeConfig;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Eccube\Form\Type\PostalType;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class PostalTypeExtension extends AbstractTypeExtension
{
    
    /**
     * @var EccubeConfig
     */
    private $eccubeConfig;

    /**
     * @var ValidatorInterface
     */
    protected $validator;

    /**
     * @var ContainerInterface
     */
    protected $container;

    public function __construct(ContainerInterface $container, ValidatorInterface $validator, EccubeConfig $eccubeConfig) {
        $this->eccubeConfig = $eccubeConfig;
        $this->validator    = $validator;
        $this->container    = $container;
    }

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventSubscriber(new \Eccube\Form\EventListener\ConvertKanaListener());
        $builder->addEventSubscriber(new \Eccube\Form\EventListener\TruncateHyphenListener());

        // 郵便番号 バリデーション
        $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
            $form = $event->getForm();
            $data = $form->getData();

            if (!empty($data)) {
                // 郵便番号
                $postal_code = str_replace('-', '', $data);

                // datファイルを取得する
                $dat_path     = realpath($this->container->getParameter('eccube_theme_front_dir') . '/../../../html/user_data/assets/dataset/postal_code.dat');
                $dat_contents = str_replace('-', '', file_get_contents($dat_path));
                $postal_code_map = explode("\n", $dat_contents); 
                $postal_code_map = array_map('trim', $postal_code_map);
                $postal_code_map = array_values($postal_code_map);

                if(array_search($postal_code, $postal_code_map) === false) {
                    $form->addError(new FormError('正しい郵便番号を入力してください'));
                }
            }
        });
    }

    /**
     * {@inheritdoc}
     */
    public function getExtendedType()
    {
        return PostalType::class;
    }
}

getExtendedType関数の返り値に拡張したいFormTypeを定義すると、buildForm関数内で独自に定義を追加することが出来ます。addEventListener関数を使って独自のバリデーションルールを追加しています。このあたりはEC-CUBE4というよりかはSymfonyって感じですが・・・。こんな感じでフォームのイベントをフックしていろいろ出来るようになっています。FormEvents::POST_SUBMITを渡しているので、フォーム送信時にこの中に定義した処理が呼ばれます。
あとは単純な処理で入力値を受け取って、郵便番号情報を格納したデータと突き合わせを行い、OKだったらそのまま通過。ダメだったらaddErrorを呼んで利用者に警告を表示します。
ここの流れが分かってたらいろいろなバリデーションルールの追加にも対応出来るかなあと・・・どうでしょうか?

郵便番号のデータはこんな感じです。
https://gist.github.com/eccyun/9e572528838d53e5b4dd6956c2141709
※. 少し大きめのファイルなので、ご注意ください。

こんな感じで書いてみたものの・・・

参考になるんでしょうか?どうでしょうか。

この記事を書いた人

えっくん

ロードスターに乗っています。最近またブログを書きはじめました。
この名前を名乗るようになって15年が経ちWebの仕事をはじめて10年が経ちました。とほほ・・・。