จาก ตอนที่ 1 เราแค่บอกว่าเราจะทำ domain class อะไรบ้าง แต่ก็เขียนไปแค่อันเดียว blog นี้เราจะเขียน domain class ที่เหลือให้หมดเลย หลักๆ จะอิงจาก tutorial เพราะจะได้เรียนรู้ไปในตัว
ครั้งที่แล้วมี User ไปแล้ว คราวนี้ก็เหลือ Album, Picture, Image
มาที่ Album กันก่อน
class Album implements Comparable {
User user
String caption
String description
SortedSet pictures
Integer picturesCount = 0
Date dateCreated = new Date()
Date lastUpdated = new Date()
static belongsTo = User
static hasMany = [ pictures : Picture ]
static optionals = [ 'description' ]
static constraints = {
caption(size: 1..40, blank: false)
description()
}
int compareTo(obj) {
obj.id.compareTo(id)
}
}
การ implement interface comparable คือเพื่อจะทำให้ object ภายใน Class Album สามารถ sort ได้ compare ได้ เนื่องจากเป็นลิสต์ของ album
static belongsTo = User static hasMany = [ pictures : Picture ]
ตัวแปร belongsTo บอกว่า Album มีความสัมพันธ์กับ User แปลง่ายๆ ว่า Album ขึ้นกับ User เป็นความสัมพันธ์อีกด้านหนึ่ง และ Album ก็มีหลาย pictures จากตัวแปร hasMany
int compareTo(obj) {
obj.id.compareTo(id)
}
ด้านบนเป็น method เอาไว้ compare object ด้วยการใช้ id
ต่อมาก็เป็น Picture.groovy
class Picture implements Comparable {</pre>
User user
Album album
SortedSet images
String file
Integer operation
String caption
String description
String contentType
Integer width
Integer height
Date dateCreated = new Date()
Date lastUpdated = new Date()
Boolean confirmDelete
static belongsTo = [ User, Album ]
static hasMany = [ images : Image ]
static optionals = [ 'caption', 'description' ]
static transients = [ 'file', 'operation', 'confirmDelete' ]
static constraints = {
caption(size: 0..40)
description()
}
static mapping = {
images cascade: 'all-delete-orphan', inverse: true
description type: 'text'
user index: 'pictures_user_index', unique: false
album index: 'pictures_album_index', unique: false
}
static final Integer NoOp = 0
static final Integer RotateClockWise90 = 1
static final Integer RotateAntiClockWise90 = 2
static final Integer Rotate180 = 3
static final Integer Flip = 4
static final Integer Flop = 5
int compareTo(obj) {
obj.id.compareTo(id)
}
}
ซึ่งหน้าตาก็คล้ายๆ กับ domain class อื่นๆ แต่มีส่วนที่ต่างคือส่วนที่เกี่ยวกับการ mapping และการประกาศ final variable
static mapping = {
images cascade: 'all-delete-orphan', inverse: true
description type: 'text'
user index: 'pictures_user_index', unique: false
album index: 'pictures_album_index', unique: false
}
ตรงนี้เป็นเรื่องของ GORM เกี่ยวกับ mapping DSL คือตั้งแต่ grails version 1.0 มาแล้วนั้น domain class สามารถถูก map เป็น schema อื่นๆ ได้โดยผ่าน ORM DSL เช่น เราอาจจะเปลี่ยนชื่อ table name ชื่อ column เรา enable caching ให้กับ mapping closure ได้ด้วย
image cascade น่าจะเป็นการกำหนด cascade ให้กับ image ของ picture object นี้ ให้เป็น all-delete-orphan และให้ inverse ได้ มี description ชนิด text มีการทำ index ไปที่ user และ album ที่เกี่ยวกับกับ picture นี้ ([variable] index)
เพิ่งแอบเห็น พี่ชาญวิทย์แปลเรื่อง mapping ไว้ให้แล้ว อ่านง่ายดี ภาษาเฉพาะทางสำหรับ mapping ใน GORM (ตอน 1)
ต่อไปก็เป็น Class Image ไม่ต้องอธิบายอะไรกันมากแล้ว คล้ายๆ กับที่ผ่านมา
class Image implements Comparable {
User user
Picture picture
Integer size
byte[] data
String contentType
Integer width
Integer height
Date dateCreated = new Date()
Date lastUpdated = new Date()
static belongsTo = [ User, Picture ]
static mapping = {
picture index: 'images_index', unique: true
size index: 'images_index', unique: true
data type: 'binary'
}
static constraints = {
data(maxSize: MAX_SIZE)
}
static final Integer MAX_SIZE = 10 * 1024 * 1024
static final Integer Original = 1
static final Integer Large = 2
static final Integer Medium = 3
static final Integer Small = 4
static final Integer[] Widths = [ 0, 0, 500, 250, 100 ]
static final Integer[] Heights = [ 0, 0, 500, 250, 100 ]
static final String[] Names = [ '', 'Original', 'Large', 'Medium', 'Small' ]
int compareTo(obj) {
size.compareTo(obj.size)
}
String filename() {
Image.filename(picture.id, size)
}
static String filename(long id, int size) {
if (size == Original) {
return "${id}-${Names[size]}.jpg"
}
else {
return "${id}-${Names[size]}.png"
}
}
}
ผ่านมา 2 ตอนแล้วไม่ได้รันซะที (ฮา) พยายามต่ออีกนิด มา generate controller และ view ให้กับ domain class ของเรากันดีกว่า เราจะใช้คำสั่ง grails generate-all กับ User, Album, Picture ส่วนกับ Image ไม่ต้อง เพราะมันจะเป็นแค่รูปๆ ที่แสดงขึ้นมา
grails generate-all User
หลังจากนั้นไปปรับไฟล์ grails-app/conf/DataSource.groovy นิดหน่อย เพื่อให้เก็บ HSQLDB เป็นไฟล์ใน db/devDB
eenvironments {
development {
dataSource {
dbCreate = "update" // one of 'create', 'create-drop','update'
url = "jdbc:hsqldb:file:db/devDB"
}
}
การเปลี่ยน url เพื่อเข้าใช้งาน อันนี้จะทำหรือไม่ทำก็ได้ หากเราไม่อยากเข้าผ่าน http://localhost:8080/WebAlbum แต่อยากเข้าผ่าน http://localhost:8080/natty อะไรแบบนี้ ก็ให้เพิ่ม web-jetty.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd"> <Configure class="org.mortbay.jetty.webapp.WebAppContext"> <Set name="contextPath">/</Set> </Configure>
เฮ้อ เรื่องเยอะ กว่าจะได้รัน ซัดเลยจ้ะ grails run-app แล้วลองเข้าไปเยี่ยมเยียนดูซิ เรากำลังได้ application แล้ว


อีก