play手把手教你创建一个博客项目06.增加特征(tagging)支持由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“如何创建一个博客”。
06.增加特征(tagging)支持
我们的博客上线运行后将包含非常多的博文,那么,要想快速找到某篇博文将越来越困难。为了能按主题进行分类,我们将为博客增加tagging支持。
创建Tag(以下简称特征)模型对象
Tag模型类自身其实非常简单:
package models;
import java.util.*;import javax.persistence.*;
import play.db.jpa.*;
@Entity public cla Tag extends Model implements Comparable {
public String name;
private Tag(String name){ this.name = name;}
public String toString(){ return name;}
public int compareTo(Tag otherTag){ return name.compareTo(otherTag.name);} } 由于我们打算用findOrCreateByName(String name)工厂方法来实现懒散式特征创建或得到这些特征。那么,让我们把它添加到Tag类里: public static Tag findOrCreateByName(String name){ Tag tag = Tag.find(“byName”, name).first();if(tag == null){ tag = new Tag(name);} return tag;} Tagging posts 是时候创建Tag模型与Post模型的关系了,让我们在Post类里创建正确的关系: …
@ManyToMany(cascade=CascadeType.PERSIST)public Set tags;
public Post(User author, String title, String content){ this.comments = new ArrayList();this.tags = new TreeSet();this.author = author;this.title = title;this.content = content;this.postedAt = new Date();} …
请注意,在这里我们使用了TreeSet以可预知的顺序来保存特征列表(事实上是按字母顺序的,这是基于我们之前compareTo实现)。在这里,我们仅使用单向关系。
同时,我们将添加很多帮助方法来使特征管理简单化,第一个用于tag一个Post: …
public Post tagItWith(String name){ tags.add(Tag.findOrCreateByName(name));return this;} …
下一个用于通过指定的tag找回所有的博文:
…
public static List findTaggedWith(String tag){ return Post.find(“select distinct p from Post p join p.tags as t where t.name = ?”, tag).fetch();} … 是时候测试这些新代码了,让我们重新启动服务器到测试模式: $ play test 在basicTest类里添加@Test:
@Test public void testTags(){ // Create a new user and save it User bob = new User(“bob@gmail.com”, “secret”, “Bob”).save();
// Create a new post Post bobPost = new Post(bob, “My first post”, “Hello world”).save();Post anotherBobPost = new Post(bob, “Hop”, “Hello world”).save();
// Well aertEquals(0, Post.findTaggedWith(“Red”).size());
// Tag it now bobPost.tagItWith(“Red”).tagItWith(“Blue”).save();anotherBobPost.tagItWith(“Red”).tagItWith(“Green”).save();
// Check aertEquals(2, Post.findTaggedWith(“Red”).size());aertEquals(1, Post.findTaggedWith(“Blue”).size());aertEquals(1, Post.findTaggedWith(“Green”).size());} 检查其是否正常工作。
下面的内容比上面的要难一点
好了,如果打算用多个tag来找回博文又该怎么做?看起来,这样难一点!下面我将给你一些非常有用的JPQL查询,这些查询可能对你的web项目有用: …
public static List findTaggedWith(String...tags){ return Post.find(“select distinct p from Post p join p.tags as t where t.name in(:tags)group by p.id, p.author, p.title, p.content,p.postedAt having count(t.id)= :size”).bind(“tags”, tags).bind(“size”, tags.length).fetch();} …
最棘手的部分就是我们必须使用having count语句来对联合查询视图进行过滤,仅让完全拥有全部tag的博文通过。
请注意,在这里我们不能使用Post.find(“…”, tags, tags.count)签名进行查询。这是因为tags已经是变量参数(vararg)了。测试程序如下: …
aertEquals(1, Post.findTaggedWith(“Red”, “Blue”).size());aertEquals(1, Post.findTaggedWith(“Red”, “Green”).size());aertEquals(0, Post.findTaggedWith(“Red”, “Green”, “Blue”).size());aertEquals(0, Post.findTaggedWith(“Green”, “Blue”).size());…
特征(tag)云
哪里有tags,哪里就需要一个tag云。让我们在Tag类里添加一个方法来生成tag云:
public static List getCloud(){ List result = Tag.find(“select new map(t.name as tag, count(p.id)as pound)from Post p join p.tags as t group by t.name order by t.name”).fetch();return result;} 在这里,我们使用了hibernate的一个现成特性,这个特性允许我们从一个JPA查询里返回一个定制对象。这个在List里的结果包含了一个Map,Map中的每个tag都带有两个key:tag用作tag名称,pound用作tag统计结果。测试代码为:
…
List cloud = Tag.getCloud();aertEquals(“[{tag=Blue, pound=1}, {tag=Green, pound=1}, {tag=Red, pound=2}]”, cloud.toString());为博客UI添加tags(特征)现在,我们就可以用新的特征填充物来为浏览博客增加一个新的途径。和以前一样,为了高效工作,我们需要在最初的数据集里添加许多tags。修改/yabe/conf/initial-data.yml文件,添加一些tags用于测试: …
Tag(play): name: Play
Tag(architecture): name: Architecture
Tag(test): name: Test
Tag(mvc): name: MVC …
把这些tag添加到post的数据描述里: …
Post(jeffPost): title: The MVC application postedAt: 2009-06-06 author: jeff tags: architecture Tagged #{list items:_post.tags, as:'tag'} ${tag}${tag_isLast ? '' : ', '} #{/list} #{/elseif} …
创建‘特征相关(tagged with)’页面
现在,我们就可以通过tags(特征)来列出发表的博文。在上面的#{display /} 标签里,我们把链接暂时留为空链接(用#),下面我们将通过一个链接到新创建的listTagged action替换他们: …
-Tagged #{list items:_post.tags, as:'tag'} ${tag}${tag_isLast ? '' : ', '} #{/list} …
在Application控制器里创建listTagged(String tag)action方法: …
public static void listTagged(String tag){ List posts = Post.findTaggedWith(tag);render(tag, posts);} …
和以前一样,让我们创建一条指定路由以保持URI清晰:
GET /posts/{tag} Application.listTagged 在这里存在一个问题,之前我们创建的路由和上面这条路由有冲突,这两条路由都能匹配同一个URI: GET /posts/{id} Application.show GET /posts/{tag} Application.listTagged 然后,由于我们假定id是数字型的,而tag不是,那么我们就可以很容易的使用正则表达式来限制第1条路由,从而解决这个问题:
GET /posts/{id} Application.show GET /posts/{tag} Application.listTagged 最后,我们只需要创建/yabe/app/views/Application/listTagged.html模板,以用于新的listTagged action:
#{extends 'main.html' /} #{set title:'Posts tagged with ' + tag /}
*{********* Title ********* }*
#{if posts.size()> 1} There are ${posts.size()} posts tagged '${tag}' #{/if} #{elseif posts} There is 1 post tagged '${tag}' #{/elseif} #{else} No post tagged '${tag}' #{/else}
*{********* Posts list *********}*
#{list items:posts, as:'post'} #{display post:post, as:'teaser' /} #{/list}