class LoginUsers{
static hasMany = [roleLink : RoleUsers]
String userid
static constraints = {
userid(unique:true)
}
List roles(){
return roleLink.collect{it.role}
}
List addRole(Roles r){
RoleUsers.link( this , r )
return roles()
}
List addRoles(List roleids){
for(rid in roleids){
RoleUsers.link( this , Roles.get(rid))
}
return roles()
}
List removeRole(Roles r){
RoleUsers.unlink(this,r)
return roles()
}
void removeAllRoles(){
RoleUsers.unlinkAllRolesFromUser(this)
}
}
稍微解釋一下。還記得測試的程式嗎, aska.roles() 就可以得到角色,背後怎麼做呢?使用者的類別中我們這樣定義過…
static hasMany = [roleLink : RoleUsers]
不用宣告一個 Set roleLink 在上面,GORM 自動幫我們加入了。所以呼叫 aska.roleLink() 就會馬上得到屬於aska的List<RoleUsers>。
但我說過 RoleUsers 不算是一個真正的「物件」,我們並不關心它( 雖然它要做很多事),我們要的是群組。所以這一部份,我們要再加上collect後,封裝在 roles() 的方法裡丟出Roles。
如果你還不知道collect的話,請看前面的介紹,這裡很簡潔的把 RoleUsers裡屬於 aska 的群組蒐集起來丟出去。
class LoginUsers {
static hasMany = [roleLink : RoleUsers]
}
class RoleUsers {
LoginUsers user
Roles role
static constraints = {
}
static belongsTo = [user:LoginUsers , role:Roles]
}
這樣夠清楚吧。有這樣的宣告後,aska只要呼叫 roleLink.collect{it.role} 一行就完事了,非常給力。
可想而知 Roles的部份也是 userLink.collect{it.user} 來得到一個群組下的使用者們。這邊我就不再解釋一次了。
aska.addRole ,因為我們要新增一個 RoleUsers的物件,所以責任上我們就給RoleUsers去做。先定義方法叫做link。加多數的群組,就是呼叫link 多次。removeRole就先定義叫 unlink。 Roles的類別,可想而知也定義了類似的方法,不再解釋
class Roles {
String name
static constraints = {
name(unique:true)
}
static hasMany = [ userLink:RoleUsers ]
List users(){
return userLink.collect{it.user}
}
List addUser(LoginUsers u){
RoleUsers.link( u , this)
return users()
}
List addUsers(List uids){
for(uid in uids){
RoleUsers.link(LoginUsers.get(uid) , this)
}
return users()
}
List removeUser(LoginUsers u){
RoleUsers.unlink( u , this)
return users()
}
void removeAllUsers(){
RoleUsers.unlinkAllUsersFromRole(this)
}
}
重頭戲在 RoleUsers 裡。
class RoleUsers {
LoginUsers user
Roles role
static constraints = {
}
static belongsTo = [user:LoginUsers , role:Roles]
static RoleUsers link(LoginUsers u , Roles r){
def lk = RoleUsers.findByUserAndRole(u,r)
if(!lk){
//if not in db then we start to link
lk = new RoleUsers()
u.addToRoleLink(lk)
r.addToUserLink(lk)
lk.save(flush:true)
}
return lk
}
static void unlink(LoginUsers u , Roles r ){
def lk = RoleUsers.findByUserAndRole(u,r)
if(lk){
u.removeFromRoleLink(lk)
r.removeFromUserLink(lk)
lk.delete(flush:true)
}
}
static void unlinkAllRolesFromUser(LoginUsers u){
def lks = RoleUsers.findAllByUser(u);
if(lks){
for(lk in lks){
lk.user.removeFromRoleLink(lk)
lk.role.removeFromUserLink(lk)
lk.delete(flush:true)
}
}
}
static void unlinkAllUsersFromRole(Roles r){
def lks = RoleUsers.findAllByRole(r);
if(lks){
for(lk in lks){
lk.role.removeFromUserLink(lk)
lk.user.removeFromRoleLink(lk)
lk.delete(flush:true)
}
}
}
}
我假設你已經會基本的GORM了,所以只說重點。在做鏈結與非鏈結時,記得先「解除關係」。
link 這個方法中透過
- LoginUsers 的 hasMany 裡定義的變數名稱為roleLink,
- 並且RoleUsers屬於 LoginUsers
- 於是Grails就會在LoginUsers中,自動幫你合成方法 addToRoleLink。
addToRoleLink 如此就變成是內建的方法了,不用再實作了。
記得要從「一」方去加「多」方,因為有主權的一方擁有責任。
aska.addToRoleLink(link),把自己加到鏈結當中。Roles也要做一樣的事。最後再save 那個 link (也就是RoleUsers) 則大功告成。
相反的,非鏈結要做的步驟也是一樣。把二邊的關聯去除後,再把 link 刪除。若你忘了先把關聯解除,就會得到exception。
再來是一個使用者想要把所有的群組刪掉。首先得到屬於他的 List<RoleUsers>後,一樣是要先解除關聯。要從「一」方來呼叫removeFrom,所以還得要先連回「一」方再分別解除,這也就是 lk.role.remove... 與 lk.user.remove....的意義。
程式是否少到令你難以置信?這中間沒有任何的 xml 任何的 annotation。類別的關聯性也不用再做文件說明,非常的明白易懂。定義好 RoleUsers裡的方法實作後,再來就沒有什麼事可以做了。呃,對,你也不用再管DB裡的表格了,Grails會自動幫你處理。
快速的做一些ui 的假想
有一個使用者的列表頁,點進去某個使用者之後,會出現基本資料維護,下方是他可以使用的群組。
在controller 裡我們快速的呼叫這二個方法塞成params丟到gsp中馬上就可以使用。
[role_list : Roles.list() , checked_role_list : aska.roles() ]
在維護頁裡,按下確認後回到controller,我們會先刪除aska的所有群組,然後再加有勾選的部份。
aska.removeAllRoles()
aska.addRoles(params.checkedRoleId)
二行搞定…
若前端頁面變成奇怪的新式UI,使用者點一下某個群組就會動態的Ajax往後端呼叫做更新,再按一下就刪除這個群組,那也是
aska.addRole(Roles.get(rid)) 與 aska.removeRole(Roles.get(rid)) 而已…
花了蠻多時間寫的,希望對大家有幫助。