Laravel 標準の Date バリデーションは、次のときに TRUE を返します。

OR 値がPHPの DateTime クラスのインスタンスである
AND 値が strtotime で理解できる文字列である
date_parse で年月日を返す
checkdate で年月日が妥当である

文字列の具体例でまとめると以下のようになります。

文字列 バリデーション MySQL保存
例01 now FALSE
例02 +1 day FALSE
例03 next Thursday FALSE
例04 2016-09-30 + 1day TRUE ×
例05 30 September 2016 TRUE ×
例06 2016-09-30 TRUE
例07 20160930 TRUE
例08 2016/09/30 TRUE
例09 09/30/2016 TRUE ×
例10 09-30-2016 FALSE
例11 30/09/2016 FALSE
例12 2016-09-30 10:10:00 TRUE

date_parse を通すため 例01〜例03のような論理指定は弾かれますが、例04のような記述では前半部分が年月日の配列値を返すためバリデーションは通ります。
また、アメリカ式の月日年(例09)が通って、ヨーロッパ式の日月年(例11)は弾かれます(これは環境のタイムゾーンに影響されるでしょう)。

いずれにせよ、バリデーションに通ってもそのままデータベースに保存できるとは限りません。
データベース保存を前提にするなら、ISO-8601 の "YYYY-MM-DD" 書式に限定したほうが何かと都合が良いですね。

 

date_format による ISO-8601限定

入力され日付のフォーマットを限定するために date_format が用意されています。引数は date_parse_from_format を通して処理されます。
datedate_format は両方を同時に使用することはできません。

'date'     => 'date_format:Y-m-d',
'datetime' => 'date_format:Y-m-d H:i:s',

 

Date と Datetime の拡張

データベースの保存フィールドで DateDatetime を区別し、入力フォームのインターフェースもこの2つしかないならば、date_format を使用せずに、date バリデーションを拡張してしまうこともできます。

書式を正規表現で限定してからオリジナルのバリデーションを通せばよいでしょう。

    /**
     * date (without datetime)
     *
     * @param string $attribute
     * @param string $value
     * @return true
     */
    protected function validateDate($attribute, $value)
    {
        // YYYY-MM-DD に限定する
        if (!preg_match('/^[0-9]+-[0-9]+-[0-9]+$/', $value)) return false;

        return parent::validateDate($attribute, $value);
    }

    /**
     * datetime
     *
     * @param string $attribute
     * @param string $value
     * @return bool
     */
    public function validateDatetime($attribute, $value)
    {
        // YYYY-MM-DD hh:mm に限定する
        if (!preg_match('/^[0-9]+-[0-9]+-[0-9]+ ([0-9]+):([0-9]+)/', $value, $m)) return false;

        return parent::validateDate($attribute, $value);
    }

validateDete() は標準の置き換えなので protected 宣言ですが validateDatetime() は追加関数として public で宣言しています。

 

日付入力のフォームインターフェイス

入力バリデーションとして DateDatetime を用意したならば、入力フォームもそれに対応しなければなりませんが、HTML5 の日付フォームはブラウザの対応に依存し、非対応な環境を無視したとしてもユーザーインターフェイスが人によって異なりサポートが難しいものです。

私たちのプロジェクトの管理画面では、Bootstrapベースの SmartAdmin を導入してインターフェイスの統一を図っています。

SmartAdmin の日付フォームは jQuery UI DatepickerBootstrap Timepicker の組み合わせを採用していますが、HTML5の datetimedatetime-local には対応してません。スクリプトを書くことで datetime 属性(厳密には datetime-local 属性)の入力フォームを実現する必要があります。

datetime

bladeソース(の概念)と対応するjQueryスクリプト

<div class="input-group-datetime">
  <input type="hidden" name="date_start" value="{{ old('date_start') }}">
  <input class="datepicker">
  <input class="timepicker">
</div>
$('.input-group-datetime').on('change', 'input:visible', function() {
  // 日付または時刻が変更された
  var $parent = $(this).parents('.input-group-datetime'),
    dt = $parent.find('.datepicker').val(),
    tm = $parent.find('.timepicker').val();
  if (dt && !tm) {
    // 時刻が空なら00:00で補完
    tm = '00:00';
    $parent.find('.timepicker').val(tm);
  }
  if (!dt) {
    // 日付が空なら時刻も削除
    $parent.find('.timepicker').val('');
  }
  // 日時を合成して値を格納
  $parent.find(':hidden').val((dt) ? dt + ' ' + tm + ':00' : '');
});
メルマガ購読の申し込みはこちらから。

By ymikome