Posted at

SkinnyFrameworkのバリデーション機能の独自実装例

More than 5 years have passed since last update.

SkinnyFrameworkのバリデーション機能はskinny.validator.ValidationRuleを派生させて作成することができます。

このバリデーション機能は基本的に対象フィールドの値のフォーマットを検証するようになっていますが、ちょっと強引に複雑なバリデーションを行う方法をあげてみます。


2つのフィールドの値を比較して検証する

2つのフィールド間の大小関係などを検証するバリデータを作ってみました。

/**

* 日時データ比較
*
* @param targetKey 比較対象のフィールドキー
* @param targetValue 比較対象の値
* @param compareName 比較方法の名称
* @param comparator 比較処理
*/

class compareDateTime(targetKey: String, targetValue: Any, compareName: String,
comparator: (DateTime, DateTime) => Boolean) extends ValidationRule {

def name = "compareDateTime"

override def messageParams = Seq(I18nKeyParam(targetKey), I18nKeyParam(compareName))

def isValid(v: Any): Boolean = {
val value = targetValue match {
case Some(v) => v
case None => null
case v => v
}

(v, value) match {
case (lhsStr: String, rhsStr: String) =>
(for (
lhs <- Try(DateTimeUtil.parseDateTime(lhsStr)).toOption;
rhs <- Try(DateTimeUtil.parseDateTime(rhsStr)).toOption
) yield {
comparator(lhs, rhs)
}).getOrElse(false)

case _ =>
// パラメータが存在しないことは許容する
true
}
}
}

case class greaterThanDateTime(targetKey: String, targetValue: Any) extends
compareDateTime(targetKey, targetValue, "greaterThan", (l, r) => l.compareTo(r) > 0)

case class greaterEqualDateTime(targetKey: String, targetValue: Any) extends
compareDateTime(targetKey, targetValue, "greaterEqual", (l, r) => l.compareTo(r) >= 0)

case class lessThanDateTime(targetKey: String, targetValue: Any) extends
compareDateTime(targetKey, targetValue, "lessThan", (l, r) => l.compareTo(r) < 0)

case class lessEqualDateTime(targetKey: String, targetValue: Any) extends
compareDateTime(targetKey, targetValue, "lessEqual", (l, r) => l.compareTo(r) <= 0)


messages.conf

error {

compareDateTime="{0} は {1} {2}の日時にしてください"
}

greaterThan="超"
greaterEqual="以上"
lessThan="未満"
lessEqual="以下"


使い方は以下のように比較対象のフィールドのメッセージキーと値を渡します。

override def createForm = validation(createParams,

paramKey("begin_date") is required & dateFormat,
paramKey("end_date") is required & dateFormat &
greaterEqualDateTime("model.beginDate", createParams.getAs("begin_date"))
)


DBにアクセスして検証する

フィールドの値ががDB中でユニークかどうかを検証するバリデーションは以下の感じです。

/**

* ユニークであること
*
* @param fieldName フィールド名
* @param paramType パラメータの型
* @param id 対象モデルの主キー値
* @param model モデル
*/

case class unique[Entity](fieldName: String, paramType: ParamType, id: Option[Long],
model: SkinnyCRUDMapper[Entity]) extends ValidationRule {

def name = "unique"

def isValid(v: Any): Boolean = isEmpty(v) ||
(model.countBy(sqls.ne(model.primaryKeyField, id.getOrElse(-1)).
and.eq(model.defaultAlias.field(fieldName), paramType.unapply(v))) == 0)
}


messages.conf

error {

unique="指定の{0}は既に存在しています"
}

使用例です。

override def updateForm = validation(updateParams,

paramKey("name") is required &
unique("name", ParamType.String, params.getAs("id"), model)
)