Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What is going on with this article?
@fuku_tech

【Rails】期間の重複に関するカスタムバリデーション

More than 1 year has passed since last update.

はじめに

実務に入ってもうすぐで1ヶ月が経とうとしていますが、巨大なアプリケーションに日々立ち向かったり、与えられた要件を何とか実装する日々を送っています。

今回は実務の中で出てきて勉強になった期間の重複に関するカスタムバリデーションについて自分の備忘録として書いていきます。

前提

  • 例として、あるイベントの抽選期間を登録・管理するためのテーブルを作成している場合を想定する。
  • 要件としては、抽選期間は被りがないようにしたい場合を想定する。
  • 今回の実装はモデル周りだけであり、コントローラやビューは既に実装しているものとする。
  • モデル名はLotteryPeriodモデルとする。

期間の重複検証ロジック

下記リンクで解説されているように、

比較開始日付 <= 対象終了日付 AND 比較終了日付 >= 対象開始日付

というとてもシンプルな表現で表せます。

カスタムバリデーションの作り方

今回はActiveModel::EachValidatorを利用した方法で実装していきます。
これはActiveRecord中にデフォルトではないバリデーションを自分で作りたい時などに使う手法です。
そして、何とたったの3ステップで実装できちゃいます。

1. カスタムバリデーション用ファイルの作成

% touch app/models/lottery_period_validator.rb

2. 作成したカスタムバリデーション用ファイルに実装

以下が目的の期間の重複を検証するためのバリデーションです。

今回はActiveModel::EachValidatorを利用し、validate_eachメソッド中に処理を記述していきました。

validate_eachメソッド中のrecordattributevalueはそれぞれ、以下を意味しています。

  • record:検証対象のモデルオブジェクト(この例ではLotteryPeriodモデル)
  • attribute:検証対象のフィールド名(この例ではlottery_period_start_atlottery_period_end_atなど)
  • value:検証対象の値(例えば、2019-03-31 14:00 00:00などの具体的な値のこと)

カスタムバリデーションをより詳細に知りたい方は、モデルの属性に自作のバリデーションを追加するなど、先人達が書いてくださった記事が沢山ありますのでそちらをご参考下さい。

app/models/lottery_period_validator.rb
# 抽選期間に重複がないかを検証するバリデーター
class LotteryPeriodValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    # 新規登録する期間
    new_start_date = record.lottery_period_start_at
    new_end_date   = record.lottery_period_end_at

    return unless new_start_date.present? && new_end_date.present?

     # 重複する期間を検索(編集時は自期間を除いて検索)
    if record.id.present?
      not_own_periods = LotteryPeriod.where('id NOT IN (?) AND lottery_period_start_at <= ? AND lottery_period_end_at >= ?', record.id, new_end_date, new_start_date)
    else
      not_own_periods = LotteryPeriod.where('lottery_period_start_at <= ? AND lottery_period_end_at >= ?', new_end_date, new_start_date)
    end

    record.errors.add(attribute, 'に重複があります') if not_own_periods.present?
  end
end

3. LotteryPeriodモデルの中身に追記

上記で設定したクラス名(上記例ではlottery_period)を用いてクラス名: trueと追加してあげるとそのバリデーションファイルを参照して検証が行われるようになります。

app/models/lottery_period.rb
 class LotteryPeriod < ActiveRecord::Base
   ・・・
   # lottery_period: true と記述することで上記バリデーションを使えるようになります。
   validates :lottery_period_start_at, lottery_period: true
   validates :lottery_period_end_at,   lottery_period: true
   ・・・
 end

まとめ

  • 期間の重複に関するカスタムバリデーションの自作方法についてまとめた。
  • 期間の重複条件はとてもシンプルに書けることを知ることができた。

参考URL

4
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
fuku_tech
元化学系研究者/2019.3月にエンジニアへ転向/Ruby/Rails/MySQL/GAS/TypeScript/GCP/AWS

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
4
Help us understand the problem. What is going on with this article?