LoginSignup
5
6

More than 5 years have passed since last update.

SymfonyのFormで年月をプルダウンで選択するフィールドの作り方

Posted at

:point_down: のような年月のみを選択する場合向け。 DateTime オブジェクトとマッピングする前提。

スクリーンショット 2017-02-22 20.01.30.png

どうやるのが正解かはわからないけど、 DateType をそのまま使うと苦しいのでとりあえず雑に作って動いた拡張 FormType を載せておきます。

<?php
namespace Fivestar\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\ReversedTransformer;
use Symfony\Component\OptionsResolver\OptionsResolver;

class YearMonthType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function getParent()
    {
        return DateType::class;
    }

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        if ($options['widget'] === 'choice') {
            $builder
                ->resetViewTransformers()
                ->addViewTransformer(new DateTimeToArrayTransformer(
                    $options['model_timezone'], $options['view_timezone'], array('year', 'month')
                ))
            ;
        }

        if ($options['input'] === 'array') {
            $builder
                ->resetModelTransformers()
                ->addModelTransformer(new ReversedTransformer(
                    new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], array('year', 'month'))
                ))
            ;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'widget' => 'choice',
        ]);
    }

    /**
     * {@inheritdoc}
     */
     public function finishView(FormView $view, FormInterface $form, array $options)
     {
         if ($options['widget'] === 'choice') {
             $view['day']->vars['attr']['style'] = 'display:none;';
         }
     }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'year_month';
    }
}

DateType をベースにプルダウン形式を使う前提として、 daystyle="display:none;" で消すという雑な対応で。フィールドの存在ごと消したいけどおそらく無理っぽい。

ポイントは Transformer 系を一度リセットして再設定してるところ。DateTime オブジェクトで年月のみを扱う場合、内部的には日や時分秒も補完され、1度オブジェクトを経由すると daynull ではなく 1 扱いになるため、 Transformer の第3引数にて yearmonth のみを参照するようにしておくと平和です。

$ php -r "var_dump((new DateTime('2015-12'))->format('Y-m-d H:i:s'));"
string(19) "2015-12-01 00:00:00"

入力必須要素なら finishView あたりで適当な value 値を day にぶっこんどきゃいいんだけど、入力が任意の場合は空のままサブミットすると (null)-(null)-01 とかなって死ぬし、検索してると入力必須の場合しか考慮してなさそうなコードも結構多かったので、諸々考慮されたやつが標準なりで入ってるといいなと思いました。僕はしません。

表示結果

$builder->add('yearMonth', YearMonthType::class);

とりあえずそのまま出した場合:

スクリーンショット 2017-02-22 20.01.30.png

{{ form_row(form.yearMonth) }}

クレカの有効年月用:

スクリーンショット 2017-02-22 20.05.44.png

{{ form_row(form.yearMonth, {
  'date_pattern': '{{ month }}/{{ year }}',
}) }}
5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6