(Quick Reference)

7 Validation - Reference Documentation

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith

Version: null

7 Validation

Grails validation capability is built on Spring's Validator API and data binding capabilities. However Grails takes this further and provides a unified way to define validation "constraints" with its constraints mechanism.

Grails validation ถูกสร้างขึ้นโดยใช้พื้นฐานของ Spring's Validator API และความสามารถใน data binding แต่ทั้งนี้ทั้งนั้น Grails สามารถต่อยอดได้โดยการกำหนด "constraints"

Constraints in Grails are a way to declaratively specify validation rules. Most commonly they are applied to domain classes, however URL Mappings and Command Objects also support constraints.

Constraints ใน Grails เป็นวิธีการกำหนดกฏของ validation โดยส่วนใหญ่แล้วเราจะใช้ domain classes แต่เราอาจจะใช้ URL Mappings และ Command Objects ก็ได้

7.1 Declaring Constraints

Within a domain class constraints are defined with the constraints property that is assigned a code block:

ในโดเมนคลาส constraints จะถูกกำหนดไว้ในค่าของ constraints ที่อยู่ในบล๊อกของโค๊ด

class User {
    String login
    String password
    String email
    Integer age

static constraints = { … } }

You then use method calls that match the property name for which the constraint applies in combination with named parameters to specify constraints:

โดยคุณก็จะสามารถเรียกใช้ method ที่ตรงกับชื่อของค่านั้นสำหรับแต่ล่ะ constraint

class User {
    ...

static constraints = { login size: 5..15, blank: false, unique: true password size: 5..15, blank: false email email: true, blank: false age min: 18 } }

In this example we've declared that the login property must be between 5 and 15 characters long, it cannot be blank and must be unique. We've also applied other constraints to the password, email and age properties.

ในตัวอย่างนี้เราประกาศค่า login ว่าจะต้องมีตัวอักษรระหว่าง 5 ถึง 15 โดยไม่สามารถเป็นค่าว่างได้และต้องเป็นค่าเฉพาะตัว เราก็ยังกำหนดค่าของ password, email และ age ด้วย

By default, all domain class properties are not nullable (i.e. they have an implicit nullable: false constraint). The same is not true for command object properties, which are nullable by default.
โดยปกติแล้วค่าของโดเมนคลาสทั้งหมดจะไม่สามารถเป็นค่าว่างได้ โดยจะถูกกำหนดไว้ nullable: false constraint โดยหลักการนี้จะไม่ตรงกับ command object เพราะแต่ล่ะค่าจะเป็นค่าว่างโดยปกติ

A complete reference for the available constraints can be found in the Quick Reference section under the Constraints heading.

โดยข้อมูลอ้างอิงโดยสมบูรณ์จะสามารถหาได้จาก Quick Reference ในหัวข้อ Constraints

A word of warning - referencing domain class properties from constraints

It's very easy to attempt to reference instance variables from the static constraints block, but this isn't legal in Groovy (or Java). If you do so, you will get a MissingPropertyException for your trouble. For example, you may try

เป็นการง่ายมากที่จะลองใช้ค่าตัวแปร instance ในบล๊อกของ static constraints แต่นี้ไม่ถูกต้องตามหลักของ Groovy หรือแม้แต่ Java ถ้าคุณทำแบบนั้นคุณก็จะถูกแจ้งว่า MissingPropertyException โดยดังเช่นตัวอย่าง

class Response {
    Survey survey
    Answer answer

static constraints = { survey blank: false answer blank: false, inList: survey.answers } }

See how the inList constraint references the instance property survey? That won't work. Instead, use a custom validator:

โดยคุณจะเห็นได้ว่า inList constraint อ้างอิงกับค่าของ survey ซึ่งแบบนี้จะไม่ถูกต้องโดยเราจะต้องใช้ custom validator แทน

class Response {
    …
    static constraints = {
        survey blank: false
        answer blank: false, validator: { val, obj -> val in obj.survey.answers }
    }
}

In this example, the obj argument to the custom validator is the domain instance that is being validated, so we can access its survey property and return a boolean to indicate whether the new value for the answer property, val, is valid.

ในตัวอย่างนี้ ค่า obj เป็นค่าที่ถูกกำหนดขึ้นมาเองโดยเป็น instance ที่ถูกตรวจสอบ โดยเราสามารถเข้าถึง survey โดยการคืนค่า boolean เพื่อแสดงให้เห็นว่าค่า answer, val นั้นถูกต้องหรือเปล่า

7.2 Validating Constraints

Validation Basics

Call the validate method to validate a domain class instance:

เรียกใช้ validate เพื่อการตรวจสอบความผิดพลาดของ โดเมน

def user = new User(params)

if (user.validate()) { // do something with user } else { user.errors.allErrors.each { println it } }

The errors property on domain classes is an instance of the Spring Errors interface. The Errors interface provides methods to navigate the validation errors and also retrieve the original values.

ค่า errors ในโดเมนเป้น instance ของอินเตอร์เฟส Errors ในสปริง Errors อินเตอร์เฟสจะสามารถเรียกใช้ method เพื่อการหาข้อผิดพลาดและการเรียกข้อมูลที่ผิดพลาดขึ้นมาได้ด้วย

Validation Phases

Within Grails there are two phases of validation, the first one being data binding which occurs when you bind request parameters onto an instance such as:

ใน Grails จะมีสองระดับในการตรวจสอบความผิดพลาด ระดับที่หนึ่งคือ data binding ที่จะเกิดขึ้นเมื่อคุณเรียกข้อมูลจาก instance หนึ่งๆเช่น

def user = new User(params)

At this point you may already have errors in the errors property due to type conversion (such as converting Strings to Dates). You can check these and obtain the original input value using the Errors API:

โดยในจุดนี้คุณสามารถมีข้อผิดพลาดได้จากการเปลี่ยนกลับของ type เช่น เปลี่ยนจาก Strings เป็น Dates คุณสามารถตรวจสอบได้โดยการใช้ Errors

if (user.hasErrors()) {
    if (user.errors.hasFieldErrors("login")) {
        println user.errors.getFieldError("login").rejectedValue
    }
}

The second phase of validation happens when you call validate or save. This is when Grails will validate the bound values againts the constraints you defined. For example, by default the save method calls validate before executing, allowing you to write code like:

ระดับที่สองจะเกิดขึ้นเมื่อคุณเรียกใช้ validate หรือ save ในขั้นตอนนี้ Grails จะทำการตรวจสอบค่ากับ constraints ที่คุณกำหนดไว้ เช่น โดยปกติแล้ว save จะเรียกใช้ validate ก่อนที่จะทำการใดๆโดยคุณก็จะสามารถเขียนโค๊ดได้ตามตัวอย่างด้านล่าง

if (user.save()) {
    return user
}
else {
    user.errors.allErrors.each {
        println it
    }
}

7.3 Validation on the Client

Displaying Errors

Typically if you get a validation error you redirect back to the view for rendering. Once there you need some way of displaying errors. Grails supports a rich set of tags for dealing with errors. To render the errors as a list you can use renderErrors:

โดยปกติแล้วถ้าพบว่ามีข้อผิดพลาดเกิดขึ้น เราก็ต้องการที่จะกลับไปยังหน้าแสดงผล เพื่อแสดงข้อความข้อผิดพลาดนั้นๆ Grails สนับสนุนการแสดงนี้โดยใช้ tags เพื่อการแสดงข้อความที่ผิดพลาดโดยใช้ renderErrors

<g:renderErrors bean="${user}" />

If you need more control you can use hasErrors and eachError:

ถ้าคุณต้องการการความยืดหยุ่นมากกว่านั้นคุณอาจจะใช้ hasErrors และ eachError ได้

<g:hasErrors bean="${user}">
  <ul>
   <g:eachError var="err" bean="${user}">
       <li>${err}</li>
   </g:eachError>
  </ul>
</g:hasErrors>

Highlighting Errors

It is often useful to highlight using a red box or some indicator when a field has been incorrectly input. This can also be done with the hasErrors by invoking it as a method. For example:

โดยส่วนใหญ่แล้วเราจะแสดงข้อความโดยใช้กล่องข้อความสีแดง เมื่อฟิลด์นั้นๆถูกใส่ข้อมูลที่ผิดพลาด เราสามารถใช้วิธีนี้ได้โดยใช้ hasErrors

<div class='value ${hasErrors(bean:user,field:'login','errors')}'>
   <input type="text" name="login" value="${fieldValue(bean:user,field:'login')}"/>
</div>

This code checks if the login field of the user bean has any errors and if so it adds an errors CSS class to the div, allowing you to use CSS rules to highlight the div.

โค๊ดในส่วนนี้จะตรวจสอบว่า login ฟิลด์ของ user นั่นมีข้อผิดพลาดตรงไหนหรือเปล่า ถ้ามีก็จะเพิ่มลงไปใน errors ลงในคลาส CSS ของ div เพื่อให้ div นั้นแสดงกล่องข้อความสีแดงออกมาจาก CSS ที่เพิ่มไป

Retrieving Input Values

Each error is actually an instance of the FieldError class in Spring, which retains the original input value within it. This is useful as you can use the error object to restore the value input by the user using the fieldValue tag:

แต่ล่ะข้อความคือหนึ่ง instance ของ FieldError ในสปริงและก็จะมีค่าใสตัวของมันเอง โดยจะมีประโยชน์ในการใช้ error ออปเจกนั้นสำหรับเก็บค่าจากผู้ใช้โดยใช้ fieldValue tag

<input type="text" name="login" value="${fieldValue(bean:user,field:'login')}"/>

This code will check for an existing FieldError in the User bean and if there is obtain the originally input value for the login field.

โค๊ดส่วนนี้จะตรวจสอบค่า FieldError ที่มีอยู่แล้วใน User bean ถ้ามีก็จะเก็บค่านั้นลงใปในฟิลด์ login

7.4 Validation and Internationalization

Another important thing to note about errors in Grails is that error messages are not hard coded anywhere. The FieldError class in Spring resolves messages from message bundles using Grails' i18n support.

สิ่งสำคัญอีกอย่างนึงที่น่าจดจำไว้คือ ข้อความที่แสดงต่างๆใน Grails ไม่ได้ถูกกำหนดตายตัวไว้ในโค๊ดที่ไหน แต่คลาส FieldError ในสปริงเป็นตัวเรียกข้อความนั้นๆมาจาก message bundles โดยใช้ การสนับสนุนของ Grails' i18n

Constraints and Message Codes

The codes themselves are dictated by a convention. For example consider the constraints we looked at earlier:

โดยโค๊ดนั้นถูกกำหนดไว้อยู่แล้ว เช่นจากตัวอย่างด้านล่างโดยมองจาก constraints แล้ว

package com.mycompany.myapp

class User { ...

static constraints = { login size: 5..15, blank: false, unique: true password size: 5..15, blank: false email email: true, blank: false age min: 18 } }

If a constraint is violated Grails will by convention look for a message code of the form:

ถ้า constraint ไม่เป็นไปตามกฏ Grails ก็จะเรียกข้อความมาจาก

[Class Name].[Property Name].[Constraint Code]

In the case of the blank constraint this would be user.login.blank so you would need a message such as the following in your grails-app/i18n/messages.properties file:

โดยในตัวอย่างนี้ blank constraint ก็จะเป็น user.login.blank โดยคุณก็จะต้องมีค่านี้ในไฟล์ grails-app/i18n/messages.properties ด้วย

user.login.blank=Your login name must be specified!

The class name is looked for both with and without a package, with the packaged version taking precedence. So for example, com.mycompany.myapp.User.login.blank will be used before user.login.blank. This allows for cases where your domain class message codes clash with a plugin's.

โดยชื่อของคลาสจะถูกค้นหาโดยมีแพกเกจหรือไม่มีแพกเกจก็ได้ แต่ ถ้ามีแพกเกจ Grails จะให้ความสำคัญมากกว่า เช่น com.mycompany.myapp.User.login.blank ก็จะถูกเรียกก่อน user.login.blank ทั้งนี้ทั้งนั้นเพื่อในกรณีที่ คลาสโดเมนมีชื่อตรงกับคลาสใดๆในปลั๊กอิน

For a reference on what codes are for which constraints refer to the reference guide for each constraint.

สำหรับการอ้างอิงว่าค่าใดใช้สำหรับ constraints ใดสามารถอ้างอิงได้จาก reference guide สำหรับแต่ล่ะ constraint

Displaying Messages

The renderErrors tag will automatically look up messages for you using the message tag. If you need more control of rendering you can handle this yourself:

renderErrors tag นี้ก็จะมองหา message tag ก่อนถ้าคุณต้องการความยืดหยุ่นมากกว่านั้นก็อาจจะกำหนดได้เองโดยตัวอย่างด้านล่าง

<g:hasErrors bean="${user}">
  <ul>
   <g:eachError var="err" bean="${user}">
       <li><g:message error="${err}" /></li>
   </g:eachError>
  </ul>
</g:hasErrors>

In this example within the body of the eachError tag we use the message tag in combination with its error argument to read the message for the given error.

ในตัวอย่างนี้ในบอดี้ของ eachError เราใช้ message tag และผสมผสานกับค่า error เพื่ออ่านข้อความสำหรับแต่ล่ะข้อผิดพลาด

7.5 Validation Non Domain and Command Object Classes

Domain classes and command objects support validation by default. Other classes may be made validateable by defining the static constraints property in the class (as described above) and then telling the framework about them. It is important that the application register the validateable classes with the framework. Simply defining the constraints property is not sufficient.

Domain classes และ command objects สนับสนุนการตรวจสอบข้อผิดพลาดโดยปกติอยู่แล้ว ถ้าสำหรับคลาสอื่นๆก็สามารถทำให้ตรวจสอบข้อผิดพลาดได้ โดยการกำหนดค่า static constraints ในคลาสนั้นๆ แล้วบอกกับ Grails ให้รับรู้ถึงคลาสนั้นๆ การบอกให้ Grails รับรู้ถึงการมีอยู่ของคลาสนั้นๆมีความสำคัญมาก โดยเพียงการกำหนด constraints นั้นไม่พอเพียง โดยจะเห็นได้จากตัวอย่างด้านล่าง

The Validateable Annotation

Classes which define the static constraints property and are annotated with @Validateable can be made validateable by the framework. Consider this example:

คลาสที่กำหนดค่า static constraints และประกาศ @Validateable ไว้ด้วยสามารถจะทำให้เป็นคลาสที่ตรวจสอบความผิดพลาดได้ โดยจะเห็นได้จากตัวอย่างด้านล่าง

// src/groovy/com/mycompany/myapp/User.groovy
package com.mycompany.myapp

import org.codehaus.groovy.grails.validation.Validateable

@Validateable class User { ...

static constraints = { login size: 5..15, blank: false, unique: true password size: 5..15, blank: false email email: true, blank: false age min: 18 } }

You tell the framework which packages to search for Validateable classes in by assigning a list of Strings to the grails.validateable.packages property in Config.groovy:

คุณต้องบอกกับ Grails ว่า แพกเกจในให้ทำการค้นหา Validateable โดยการกำหนดค่าสตริงใน grails.validateable.packages ใน Config.groovy:

grails.validateable.packages = ['com.mycompany.dto', 'com.mycompany.util']

The framework will only search those packages (and child packages of those) for classes annotated with @Validateable.

เฟรมเวิกจะทำการค้นหาเฉพาะในแพกเกจนั้นๆและ แพกเกจลูกโดยหาจาก @Validateable

Registering Validateable Classes

If a class is not marked with Validateable, it may still be made validateable by the framework. The steps required to do this are to define the static constraints property in the class (as described above) and then telling the framework about the class by assigning a value to the grails.validateable.classes property in Config.groovy@:

ถ้าคลาสนั้นไม่ได้ถูกกำหนดให้เป็น Validateable เราก็ยังสามารถทำให้เป็นคลาสที่ถูกตรวจสอบความผิดพลาดได้โดยเฟรมเวิก โดยนอกจากกำหนดค่า static constraints ในคลาสดั่งที่เห็นในตัวอย่างด้านบนแล้ว เราก็ยังต้องบอกกับเฟรมเวิกถึงการมีอยู่ของคลาสนั้นด้วยโดยการคำหนดค่า grails.validateable.classes ใน Config.groovy@:

grails.validateable.classes = [com.mycompany.myapp.User, com.mycompany.dto.Account]