12.7.1 开启CRUD模块#
Play提供了很多实用的模块,可以很方便地集成到应用中。模块的好处是提高了应用中组件的重用性,以及将大的应用拆分成多个小应用进行管理。CRUD模块是Play提供便于原型管理的组件,该模块会检查Model类的内部结构,并自动创建简单的管理列表和表单。
在/yabe/conf/application.conf文件中进行如下配置,开启CRUD模块:
# Import the crud module module.crud=${play.path}/modules/crud
CRUD模块自带了一些通用的路由规则,需要在/yabe/conf/routes文件中进行配置:
# Import CRUD routes * /admin module:crud
重启应用后CRUD模块生效。当用户以http://localhost:9000/admin访问应用时,框架会自动导入所有CRUD路由规则。
12.7.2 声明CRUD控制器#
为了使模型对象集成到CRUD管理域中,我们需要为每个模型对象定义继承于controllers.CRUD的控制器。例如在/yabe/app/controllers/目录下创建Posts.java文件,并使之继承于controllers.CRUD,就完成了Post模型的CRUD管理:
package controllers; import play.*; import play.mvc.*; public class Posts extends CRUD { }
推荐使用模型对象的复数形式为CRUD控制器命名,这样做便于Play自动为每个控制器找到关联的模型对象。如果我们希望使用其他名字命名,可以使用@CRUD.For注解。
接下来分别为每个模型创建CRUD控制器类:
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/访问管理域,如图12.19所示。
(图12.19 访问管理域)
管理域列表中的对象名称很晦涩,不方便管理,这是因为CRUD模块默认使用toString()方法返回模型对象的名称。为所有模型覆盖toString()方法,优化模型对象在管理域中的显示。
以User类为例,重写toString()方法:
... public String toString() { return email; } ...
12.7.3 添加验证#
CRUD模块提供的管理域只是原型产品,不是非常完善,并没有提供任何表单的验证规则。我们只需在定义模型对象时添加验证注解,CRUD模块就能够从注解中获得验证规则,对表单数据进行验证。为User类添加验证注解:
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对象的编辑或者创建页面,就会发现验证规则已经自动添加到表单中了,如图12.20所示:
(图12.20 添加验证规则)
为Post类添加验证注解:
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; ...
刷新浏览器,Post编辑界面如图12.21所示:
(图12.21 添加验证规则)
细心的读者可能会发现表单中微妙的变化,@MaxSize验证注解改变了Post表单的呈现方式,将content的输入框换成了文本域。
最后为Comment和Tag类增加验证规则:
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; ...
12.7.4 使用更友好的表单label#
CURD模块默认使用模型的属性名作为表单label。因为我们在定义模型属性时经常使用简写,所以管理域的表单label并不是非常直观。我们可以配置/yabe/conf/messages文件,自定义表单label,提供更友好的管理域界面。
事实上,我们可以为不同的语言提供独立的messages文件,比如将法语的本地化操作配置在/yabe/conf/messages.fr文件中。前面的章节已经介绍如何添加本地化语言。
将以下表单label配置到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
刷新浏览器,就可以看到新的表单label,如图12.22所示:
(图12.22 更改表单label)
12.7.5 自定义显示评论列表页面#
CRUD模块具有可定制化的特性。管理域中查看默认文章评论列表页面时,只会显示一列数据,这显然不能满足我们的需求。我们可能会要求增加更多的列,特别是增加“文章关联列”来帮助我们筛选相关的post。
我们通常采用覆盖CRUD模块的Action或者模板来实现页面定制功能。以定制文章评论列表页面为例:只需在/yabe/app/views/Comments/目录下定义list.html模版,就能覆盖管理域的默认list.html界面。
CRUD模块引入后为框架提供了一些额外的Play命令。crud:ov命令可以帮助我们覆盖任何模版。在命令行中输入:
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/}标签的功能是生成管理域的列表,我们可以修改#{crud.table/}标签的fields参数来增加更多的列:
#{crud.table fields:['content', 'post', 'author'] /}
这样管理域的列表中就会显示三列属性了,如图12.23所示:
(图12.23 增加更多列的table)
按照以上方式定义,在使用时可能会出现问题。因为评论中content字段可能会非常长,直接在列表中显示会破坏管理域的页面布局,我们需要将较长的content内容进行截断处理。
继续修改#{crud.custom/}标签的内容,定义content字段的显示方式:
#{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}
12.7.6 自定义添加文章页面#
我们也可以定制添加文章页面。该页面主要由输入表单构成,其中为文章添加标签的操作非常繁琐,我们可以定制更简洁的标签输入功能。使用crud:ov命令覆盖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>
修改/yabe/app/views/Posts/show.html模版,将#{crud.custom /}标签内嵌至#{crud.form/}标签体中,实现文章标签选择的定制:
#{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}
在show.html模版的定制中,我们使用JavaScript实现了简单的标签选择器,使页面的用户体验变得更友好,如图12.24所示。
(图12.24 JavaScript实现的标签选择器)