07 A basic admin area using CRUD

簡介

目前,我們沒有辦法運用BLOG UI介面來建立新的BLOG文章或評論。Play提供了CRUD模組將有助於快速產生一組基本的管理介面。


Enabling the CRUD module

Play 應用程式可以運用多個模組(Modules)來組合而成。這讓您可以重覆使用模組在多個應用程式上或大型應用程式中的小功能。應用程式內附一組通用CRUD module,可以建立簡單的清單及表單。若要啟用CRUD module,則在/yabe/conf/application.conf須要增加這一行:

# Import the crud module module.crud=${play.path}/modules/crud

接著,該模組配置一組URL路徑,則必須在 /yabe/conf/routes檔案中加入此行:

# Import CRUD routes * /admin module:crud

這將導入所有CRUD相關路徑並使用/admin來導航URL路徑。接著,必須重新啟動應用程式。


Declaring the CRUD controllers

對於每個物件,我們希望能整合到管理介面,我們必須聲明controller必須繼承controllers.CRUD super controller。這非常簡單。以下範例必須建立 /yabe/app/controllers/Posts.java

package controllers; import play.*; import play.mvc.*; public class Posts extends CRUD { }

注意,規則必須使用複數形式來定義controller名稱。這樣,Play會自動尋找相關的controller物件。如果你需要使用不同的名字,你仍然可以使用@CRUD.For annotationCheck the manual page,以下開始建立其它物件

package controllers;
 
import play.*;
import play.mvc.*;
 
public class Users extends CRUD {    
}
package controllers;
 
import play.*;
import play.mvc.*;
 
public class Comments extends CRUD {    
}
package controllers;
 
import play.*;
import play.mvc.*;
 
public class Tags extends CRUD {    
}
現在只要在瀏覽器輸入http://localhost:9000/admin/ 就可以看到管理者介面。


如果你有瀏覽一些你會發現物件名稱列表顯示名稱的方式不是很好,因為它預設使用簡易的toString()來讀取一組model物件。我們可以很容易的修改這個問題,並正確實作 toString()方法用於所有的models。舉例來說,針對User class:

... public String toString() { return email; } ...


Adding validation

產生管理者介面最主要的問題是沒有包含任何驗證規則。但實際上CRUD模組能提供驗證規則但必須從validation annotations來運用。讓我們新增一些適合的annotations在User class:

package models; import java.util.*; import javax.persistence.*; import play.db.jpa.*; import play.data.validation.*; @Entity public class User extends Model { @Email @Required public String email; @Required public String password; public String fullname; public boolean isAdmin; ...

現在,如果你去編輯或新增一組表單在User model物件,你將會看到驗證規則會自動顯示在表單上。


Post class也是一樣的做法,並檢查其結果:

package models; import java.util.*; import javax.persistence.*; import play.db.jpa.*; import play.data.validation.*; @Entity public class Post extends Model { @Required public String title; @Required public Date postedAt; @Lob @Required @MaxSize(10000) public String content; @Required @ManyToOne public User author; @OneToMany(mappedBy="post", cascade=CascadeType.ALL) public List<Comment> comments; @ManyToMany(cascade=CascadeType.PERSIST) public Set<Tag> tags; ...





這裡你會看到很有趣的功能:@MaxSize驗證規則已經改變了Play顯示Post表單的方式。它現在使用一個large text area在該欄位上。最後,我們可以新增驗證規則在Comment and Tag classes:

package models;
 
import java.util.*;
import javax.persistence.*;
 
import play.db.jpa.*;
import play.data.validation.*;
 
@Entity
public class Tag extends Model implements Comparable<Tag> {
 
    @Required
    public String name;
 
...
package models;
 
import java.util.*;
import javax.persistence.*;
 
import play.db.jpa.*;
import play.data.validation.*;
 
@Entity
public class Comment extends Model {
 
    @Required
    public String author;
    
    @Required
    public Date postedAt;
     
    @Lob
    @Required
    @MaxSize(10000)
    public String content;
    
    @ManyToOne
    @Required
    public Post post;
 
...

Better form labels

正如你所看到的表單標籤名稱有點不好。Play使用Java欄位名稱形式來定義表單標籤名稱。要自定義它,我們只需要提供更好的標籤名稱在/yabe/conf/messages檔案。事實上,你可以在應用程式上支援各種多國語言。例如:你可以放入法國語言在/yabe/conf/messages.fr檔案。你將會看到如何新增多國語系在Internationalisation and localisation。新增這些標籤名稱在messages檔案上,然後重新整理頁面,你將會看到新的自定義標籤名稱::

title=Title content=Content postedAt=Posted at author=Author post=Related post tags=Tags set name=Common name email=Email password=Password fullname=Full name isAdmin=User is admin



Customizing the ‘Comments’ data list

CRUD模組也可以被完全自定義。舉例來說,如果你看到評論清單頁面,資料顯示的方式不是很多。我們若要新增更多顯示資料,特別是"相關文章"欄位,以幫助我們方便過濾。

事實上,你的應用程式保留了master,你可以override any action或模版提供CRUD module。舉例來說,如果我們自定義"評論清單"頁面,我們只須要提供另一個模版在/yabe/app/views/Comments/list.html上。

CRUD模組提供了更多Play指令。crud:ov指令幫助你override任何模版。你可以鍵入:

$ play crud:ov --template Comments/list


現在,你會看到新的模版建立在/yabe/app/views/Comments/list.html中:

#{extends 'CRUD/layout.html' /} <div id="crudList" class="${type.name}"> <h2 id="crudListTitle">&{'crud.list.title', type.name}</h2> <div id="crudListSearch"> #{crud.search /} </div> <div id="crudListTable"> #{crud.table /} </div> <div id="crudListPagination"> #{crud.pagination /} </div> <p id="crudListAdd"> <a href="@{blank()}">&{'crud.add', type.modelName}</a> </p> </div>

 #{crud.table /}實際產生的資料表。我們可以自定義欲使用的欄位參數來增加更多列。現在,我們有3組欄位在資料表上:

#{crud.table fields:['content', 'post', 'author'] /}

問題是,內容欄位可能有很長的一段評論。我們將使用的方式在 #{crud.table /} 處理它並能夠截斷部分內容。我們可以指定自定義的方式來顯示每個欄位並使用#{crud.custom /} 

#{crud.table fields:['content', 'post', 'author']} #{crud.custom 'content'} <a href="@{Comments.show(object.id)}"> ${object.content.length() > 50 ? object.content[0..50] + '…' : object.content} </a> #{/crud.custom} #{/crud.table}

Yes, there is some Groovy syntactic sugar at work here.


Customizing the ‘Post’ form

我們可以自定義產生表單。舉例來說,我們輸入tags在Post表單很不容易。我們可以建立一些更好的模版。讓我們重新覆寫Posts/show模版:

$ play crud:ov --template Posts/show

現在,你會看到新的模版建立在/yabe/app/views/Posts/show.html中:

#{extends 'CRUD/layout.html' /} <div id="crudShow" class="${type.name}"> <h2 id="crudShowTitle">&{'crud.show.title', type.modelName}</h2> <div class="objectForm"> #{form action:@save(object.id), enctype:'multipart/form-data'} #{crud.form /} <p class="crudButtons"> <input type="submit" name="_save" value="&{'crud.save', type.modelName}" /> <input type="submit" name="_saveAndContinue" value="&{'crud.saveAndContinue', type.modelName}" /> </p> #{/form} </div> #{form @delete(object.id)} <p class="crudDelete"> <input type="submit" value="&{'crud.delete', type.modelName}" /> </p> #{/form} </div>

你可以破解#{crud.form /}標籤來自定義tags欄位:

#{crud.form} #{crud.custom 'tags'} <label for="tags"> &{'tags'} </label> <style type="text/css"> .tags-list .tag { cursor: pointer; padding: 1px 4px; } .tags-list .selected { background: #222; color: #fff; } </style> <script type="text/javascript"> var toggle = function(tagEl) { var input = document.getElementById('h'+tagEl.id); if(tagEl.className.indexOf('selected') > -1) { tagEl.className = 'tag'; input.value = ''; } else { tagEl.className = 'tag selected'; input.value = tagEl.id; } } </script> <div class="tags-list"> #{list items:models.Tag.findAll(), as:'tag'} <span id="${tag.id}" onclick="toggle(this)" class="tag ${object.tags.contains(tag) ? 'selected' : ''}"> ${tag} </span> <input id="h${tag.id}" type="hidden" name="${fieldName}" value="${object.tags.contains(tag) ? tag.id : ''}" /> #{/list} </div> #{/crud.custom} #{/crud.form}

這裡運用了一點JavaScript來呈現tags選擇器



This is a good start for the administration area!


Comments