配置play环境
把play的路径添加到系统环境变量的PATH路径中
play
1.进入CMD环境,测试配置是否成功
2.这里我们用samples-and-tests 下的yabe项目来做例子。
3.在cmd中 play new yabe
4.进入创建的目录运行play run命令. 在浏览器中输入http://localhost:9000查看
创建的项目是否成功。
5.使用 play eclipsify 表示把项目转换成一个ECLIPSE项目。
6.输入play test 表示以测试模式启动。在浏览器中输入
http://localhost:9000/@tests 表示进入测试JUNIT页面,并可进行测试
7.创建实体BEAN,play的实体BEAN使用的是JPA的实体
8.把项目导入eclipse中,在models包中创建类,内容如下:
package models;
import javax.persistence.Entity;
import play.db.jpa.Model;
@Entity
public class User extends Model {
public String email;
public String password;
public String fullname;
public String isAdmin;
public User(String email, String password, String fullname) {
this.email = email;
this.password = password;
this.fullname = fullname;
}
}
关于实体中类的注解可以通过查看JPA2.0的相关文档来进行了解.
注意一点,我在创建的实体类中并没有添加ID属性,但是其实ID属性是必须的属性,如果我们在实体类中没有显示的指定ID属性,PLAY会给我们创建一个默认的id属性,这个属性的值为自动增加值。
集成JUNIT单元测试
在 test包目录下新建一个UserTest的测试类,继承UnitTest类,如下:
import https://www.wendangku.net/doc/2512055294.html,er;
import org.junit.Test;
import play.test.UnitTest;
public class UserTest extends UnitTest {
@Test
public void createAndRetrieveUser() {
//添加
User user = new User("yh.sniaw@https://www.wendangku.net/doc/2512055294.html,","123456","小机");
assertNotNull(user.save());
//查询条件下的所有信息,并返回第一个
User search = user.find("byEmailLike", "%gmail%").first();
assertNotNull(search);
assertEquals("123456", search.password);
}
}
运行 play test 在浏览器中输入 http://localhost:9000/@tests 选择UserTest点击 start 按钮测试。
在User实体中编写一个查询的方法,并在测试类中添加一个测试方法,不需要重启浏览器,直接进行测试。查看效果。
public static User connect(String email, String password) {
return User.find("byEmailLikeAndPassword", "%" + email + "%", password) .first();
}
@Test
public void testConnectMethod() {
//添加
User user = new User("yh.sniaw@https://www.wendangku.net/doc/2512055294.html,","123456","小机");
assertNotNull(user.save());
//查询
assertNotNull(User.connect("gmail", "123456"));
assertNull(User.connect("test", "123456"));
assertNull(User.connect("yh.sniaw@https://www.wendangku.net/doc/2512055294.html,", "aa"));
assertNotNull(User.connect("yh.sniaw@https://www.wendangku.net/doc/2512055294.html,", "123456"));
}
新建一个Post实体类,类的关系与User为多对一的关系。
package models;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import play.db.jpa.Model;
@Entity
public class Post extends Model {
public String title;
public Date postedAt;
@Lob
public String content;
@ManyToOne
public User author;
public Post(User author, String title, String content) { this.author = author;
this.title = title;
this.content = content;
this.postedAt = new Date();
}
}
创建一个测试方法:
@Test
public void createPost() {
//添加
User user = new User("yh.sniaw@https://www.wendangku.net/doc/2512055294.html,","123456","小机");
assertNotNull(user.save());
Post post = new Post(user, "title","content");
assertNotNull(post.save());
assertEquals(1, Post.count());
List list = Post.find("byAuthor", user).fetch();
assertEquals(1, list.size());
Post firstPost = list.get(0);
assertNotNull(firstPost);
assertEquals("title",firstPost.title);
assertEquals("content",firstPost.content);
assertNotNull(firstPost.postedAt);
assertEquals(user,firstPost.author);
}
查看测试结果。
新建一个Comment实体类,实体类与Post为多对一的关系。
package models;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import play.db.jpa.Model;
@Entity
public class Comment extends Model {
public String author;
@Lob
public String content;
public Date postedAt;
@ManyToOne
public Post post;
public Comment(Post post, String author, String content) { this.post = post;
this.author = author;
this.content = content;
this.postedAt = new Date();
}
}
新建一个测试Comment方法。
@Test
public void createComments() {
User user = new User("yh.sniaw@https://www.wendangku.net/doc/2512055294.html,","123456","小机");
assertNotNull(user.save());
Post post = new Post(user, "title","content");
assertNotNull(post.save());
new Comment(post,"author1","content1").save();
new Comment(post,"author2","content2").save();
List list = Comment.find("byPost", post).fetch();
assertEquals(2,list.size());
Comment firstComment = list.get(0);
assertEquals("author1",firstComment.author);
assertNotNull(firstComment.postedAt);
}
为Post添加一对多关系
mappedBy表示找到Comment类中的post对象进行反转
@OneToMany(mappedBy="post",cascade=CascadeType.ALL)
Set comments;
public Post(User author, String title, String content) { comments = new LinkedHashSet(0);
this.author = author;
this.title = title;
this.content = content;
this.postedAt = new Date();
}
添加测试方法:
@Test
public void postAddComments() {
User user = new User("yh.sniaw@https://www.wendangku.net/doc/2512055294.html,","123456","小机");
assertNotNull(user.save());
Post post = new Post(user, "title","content");
assertNotNull(post.save());
https://www.wendangku.net/doc/2512055294.html,ments.add(new Comment(post,"author1","content1"));
https://www.wendangku.net/doc/2512055294.html,ments.add(new Comment(post,"author2","content2"));
post.save();
Post firstPost = Post.find("byTitle", "title").first();
assertEquals(2,https://www.wendangku.net/doc/2512055294.html,ments.size());
assertEquals(2,Comment.count());
}
编写视图显示
1.添加在服务器启动时需要处理的事情。如加载一些基础数据:
在models中添加一个Bootstarp类。此类继承Jo b类。
package models;
import play.jobs.Job;
import play.jobs.OnApplicationStart;
import play.test.Fixtures;
@OnApplicationStart
public class Bootstarp extends Job {
public void loadUsers() {
if (User.count() < 1) {
Fixtures.loadModels("init_user.yml");
}
}
}
在conf目录添加init_user.yml文件。文件内容可以从test包下的data.yml文件中复制出来。并做一些修改。
代码格式每一个对象用空行表示结束。值中–表示引用一个对象, ->表示换行
User(bob):
email: bob@https://www.wendangku.net/doc/2512055294.html,
password: secret
fullname: Bob
isAdmin: true
User(jeff):
email: jeff@https://www.wendangku.net/doc/2512055294.html,
password: secret
fullname: Jeff
以上代码为 YAML代码,代码语法见YAML语法说明:
以上代码表示添加一个User对象,对象名称为bob,构造方法中传入了三个参数与值。
创建一个测试方法进行测试,看看USER对象是否已经创建。代码如下:
@Test
public void showUser() {
System.out.println(User.count());
User user = User.find("byEmail", "bob@https://www.wendangku.net/doc/2512055294.html,").first();
assertNotNull(user);
System.out.println(user);
}
通过 play test方式启动服务器,并进行测试。
以上的测试好像没有用。加载完成后我是能查询到。通过运行环境不知道能不能行。
现在我们停止测试环境的运行,启动运行环境。 Play run
现在我们修改下应用的默认首页。
打开 controllers下的Application文件,修改index方法:
public static void index() {
Post firstPost = Post.find(" order by postedAt desc").first();
List postList = Post.find(" order by postedAt desc").from(0).fetch(10);
render(firstPost,postList);
}
打开Application中index对应的视图文件,views/Application/index.html把代码修改成如下内容:
#{extends 'main.html' /}
#{set title:'Home' /}
#{if firstPost}
${firstPost.title}
By ${firstPost.author?.fullname}
class="post-date">${firstPost.postedAt.format('yyyy-MM-dd')}
| ${https://www.wendangku.net/doc/2512055294.html,ments.size()?:"no"}
comment${https://www.wendangku.net/doc/2512055294.html,ments.size().pluralize()}
#{if https://www.wendangku.net/doc/2512055294.html,ments}
,last by ${https://www.wendangku.net/doc/2512055294.html,ments.toArray()[-1].author}
#{/if}
${firstPost.content.nl2br()}
#{if postList}
当前页数据
#{list items:postList, as:'oldPost'}
${oldPost.title}
By ${oldPost.author?.fullname}
class="post-date">${oldPost.postedAt.format('yyyy-MM-dd')}
| ${https://www.wendangku.net/doc/2512055294.html,ments.size()}
comment${https://www.wendangku.net/doc/2512055294.html,ments.size().pluralize()}
#{if https://www.wendangku.net/doc/2512055294.html,ments}
, last by ${https://www.wendangku.net/doc/2512055294.html,ments.toArray()[-1].author}
#{/if}
#{/list}
#{/if}
#{/if}
#{else}
There is currently nothing to read here.
#{/else}
注意上面代码中的红色部分内容,pluralize表示结果是否大于1,如果大于返回一个s, format可以对日期进行格式化,
XXXList.toArray()[-1]表示查询List中最后一个值。
nl2br()表示把\n转换成
nl 很容易看成是n1 正确的是NL
当然上面这样都是groovy的语法
上面提示这些东西是因为官方的tutoral中写的方式不对,找了下groovy的语法后添加正确。
创建自定义标签
自定义标签可以理解为页面模板(可在自定义标签中写HTML信息,并可以定义属性,在引用标签时把属性传入进来)
标签定义:在 views/tags/目录下创建html页面就是标签。如: display.html
标签引用:在页面中使用 #{display 参数:参数值 /} 其中display为标签的HTML名称标签中使用参数:在标签中使用参数以“_”开始,后面是参数名称。
标签使用例子:
定义标签:
*{这是一个注释display post Post and as in ('full','teaser','first')}*
${_post.title}
By ${_post.author?.fullname}
Created
${_post.postedAt.format('yyyy-MM-dd')}
#{if _as != 'full'}
| ${_https://www.wendangku.net/doc/2512055294.html,ments.size()?:'no'}
Comment${_https://www.wendangku.net/doc/2512055294.html,ments.size().pluralize()}
#{if _https://www.wendangku.net/doc/2512055294.html,ments}
,Last By ${_https://www.wendangku.net/doc/2512055294.html,ments.toArray()[-1].author}
#{/if}
#{/if}
#{if _as != 'teaser'}
Detail:
${_post.content.nl2br()}
#{/if}
#{if _as == 'full'}
#{/if}
index.html页面引用标签:
#{extends 'main.html' /}
#{set title:'Home' /}
#{if firstPost}
*{引用自定义标签}*
#{display post:firstPost, as:'first' /}
#{if postList}
当前页数据
#{list items:postList, as:'oldPost'}
*{引用自定义标签}*
#{display post:oldPost,as:'teaser' /}
#{/list}
#{/if}
#{/if}
#{else}
There is currently nothing to read here.
#{/else}
测试查看效果:
修改布局
A.现在我们打开views/main.html页面,修改页面的外观。
#{get 'title' /}href="@{'/public/stylesheets/main.css'}">
#{get 'moreStyles' /}
href="@{'/public/images/favicon.png'}">
#{get 'moreScripts' /}
#{doLayout /}
Yabe is a (not that) powerful blog engine built with the itfuture
as a tutorial application.
表示式语言符号
注意:上面的代码中我们使用了:
可参见https://www.wendangku.net/doc/2512055294.html,/documentation/1.2.4/templates#syntax
# {}: 表示引用模板(标签)
${}: 表示表达式
@{}: 表示引用静态资源
%{}% : 使用一段脚本。
&{}: 表示引用一段消息
注意,上面红色部分引用了两个变量值,这时候我们需要在ACTION中控制这两个参数值。但是由于这是一个布局模块文件,不需要在每一个控制器中重新赋值的情况下,我们可以在Application.java文件中定义一个方法,这个方法用@Before来注解,表示方法在每一个动作执行前调用。
@Before
static void addDefaults() {
renderArgs.put("blogtitle", Play.configuration.getProperty("blog.title"));
renderArgs.put("blogbaseline",
Play.configuration.getProperty("blog.baseline"));
}
上面的代码中我们在renderArgs中存放了两个值:bolgtitle,blogbaseline,那么这两个值在页面中就可以直接去使用。
同时,我在方法使用了Play.configuration.getProperty方法得到配置的两个参数值。Play.configuration表示得到conf/application.conf中配置的参数值。
现在我们打开application.conf文件,配置上面使用到的两个参数值。
blog.title=博客标题
blog.baseline=博客基线
运行 play run,测试下效果:
从上图我们可以看到。信息是出来了,但是还有乱码的存在,现在我们处理下乱码问题:其实这问题相对简单,把application.conf文件修改为UTF-8编码格式,同时打开
application.conf文件时使用eclipse工具打开就不会出现问题。
给模块添加一些样式,让页面看出来更加好看些。
把 main.css文档中的内容复制到你的项目下面 public/stylesheets目录下的main.css
文件中。
通过上面的的邮件列表页面我们学习了一定的知识,现在我们继续添加邮件的查看与更新功能。
A.在Application.java中创建一个show的方法,方法中传入一个Post的ID属性,public static void show(Long id) {
Post post = Post.findById(id);
render(post);
}
B.在views/Application/创建一个与方法同名的HTML文件 show.html。
#{extends 'main.html' /} *{引用一个模板}*
#{set title:'show post' /} *{设置模板中的参数值}*
#{display post:post, as:'full'/} *{调用一个自定义标签}*
C.修改views/tags/display.html的自定义标签,把
${_post.title} 替换为:
${_post.title}
D.修改views/main.html模板页面中
替换为:
运行 play run 查看效果。
自定义URL路由规则:
默认(所有)的URL路由规则都需要在conf/routes 文件中配置,
# Catch all
* /{controller}/{action} {controller}.{action} 上面代码的意思:
* 表示 GET/POST都可以,
/{controller}/{action}表示客户端访问路径。
{controller}.{action} 表示控制器类与方法
具体说明:
https://www.wendangku.net/doc/2512055294.html,/documentation/1.2.4/routes#syntax
那么通过上面的说明我们就可以定义show.html的客户端访问路径。
GET /posts/{id} Application.show
* /{controller}/{action} {controller}.{action}
需要把自定义放到默认的前面,否则找不到。
@Before
static void addDefaults() {
renderArgs.put("blogtitle", Play.configuration.getProperty("blog.title"));
renderArgs.put("blogbaseline",
Play.configuration.getProperty("blog.baseline"));
Map map = params.data; //此处可以得到请求的参数值,并可对值进行处理
}
注意上面的红色部分代码。现在标记出来,在实际的项目中我们可能会用到。
添加一个分页处理的方式:
A . 打开实体BEAN Post 类,添加如下两个方法:
public Post previous() {
Post previous = Post.find(" postedAt < ? order by postedAt desc", postedAt).first();
return previous;
}
public Post next() {
Post next = Post.find(" postedAt > ? order by postedAt desc", postedAt).first();
return next;
}
B.修改show.html页面。在页面后边添加如下信息:
运行查看效果:
给邮件添加回复功能:
A.在Post实体中添加一个方法:
public void addComment(String author, String content) {
Comment comment = new Comment(this,author,content);
https://www.wendangku.net/doc/2512055294.html,ments.add(comment);
this.save();
}
${_https://www.wendangku.net/doc/2512055294.html,ments.size()?:'no'}
Comment${_https://www.wendangku.net/doc/2512055294.html,ments.size().pluralize()}
#{if _https://www.wendangku.net/doc/2512055294.html,ments}
#{list items:_https://www.wendangku.net/doc/2512055294.html,ments, as:'comment'}
${comment?.author}
Created
${comment.postedAt.format('yyyy-MM-dd')}
${comment.content.escape().nl2br()}
#{/list}
#{/if}