摘要:对添加标签的支持随着博客的文章越来越多,找到要找的文章越来越难。如果能够添加标签,那就能更好地分类文章了。要在模式下启动应用,输入添加新的到类确保它能工作。为了保持高效,我们需要向初始数据集添加标签测试数据。修改来添加一些标签数据。
对添加标签的支持
随着博客的文章越来越多,找到要找的文章越来越难。如果能够添加标签,那就能更好地分类文章了。
Tag模型我们将给博客的模型们添加新成员。对Tag类的定义是非常简单的:
package models; import java.util.*; import javax.persistence.*; import play.db.jpa.*; @Entity public class 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; }给文章打标签
现在是时候连接Tag和Post模型。让我们给Post类添加相应的关系:
… @ManyToMany(cascade=CascadeType.PERSIST) public Settags; 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实现,按的是字母顺序)
我们将保持该关系为单向关系。
我们将给Post类添加一系列辅助函数。第一个用于给Post打标签:
… public Post tagItWith(String name) { tags.add(Tag.findOrCreateByName(name)); return this; } …
下一个是根据指定标签查找所有文章:
… public static ListfindTaggedWith(String tag) { return Post.find( "select distinct p from Post p join p.tags as t where t.name = ?", tag ).fetch(); } …
是时候写一个新的测试用例了。要在test模式下启动应用,输入:
$ play test
添加新的@Test到BasicTest类:
@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 assertEquals(0, Post.findTaggedWith("Red").size()); // Tag it now bobPost.tagItWith("Red").tagItWith("Blue").save(); anotherBobPost.tagItWith("Red").tagItWith("Green").save(); // Check assertEquals(2, Post.findTaggedWith("Red").size()); assertEquals(1, Post.findTaggedWith("Blue").size()); assertEquals(1, Post.findTaggedWith("Green").size()); }
确保它能工作。
解决难题如果你想要根据多个标签来查找相应的文章,该怎么办?这个问题比表面看上去要难。
我将直接给你所需的JPQL查询语句:
… public static ListfindTaggedWith(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语句来从连结产生的视图中过滤出拥有全部标签的文章。
注意这里我们不能用Post.find("...", tags, tags.count)语法,因为tags是一个变长参数。
你可以在前一个测试中加入更多的检查来测试它:
… assertEquals(1, Post.findTaggedWith("Red", "Blue").size()); assertEquals(1, Post.findTaggedWith("Red", "Green").size()); assertEquals(0, Post.findTaggedWith("Red", "Green", "Blue").size()); assertEquals(0, Post.findTaggedWith("Green", "Blue").size()); …标签集合
我们最好有一个标签集合来看看有哪些标签。让我们给Tag类添加一个方法来生成标签集合:
public static List
这里,我们使用Hibernate的一个允许我们从JPA查询中返回自定义对象的特性。返回的结果是一个Map列表,其中每个Map包括标签名(键)和标签个数(值)。
在我们前面的测试中加多一个检查:
… List向博客界面添加标签cloud = Tag.getCloud(); assertEquals( "[{tag=Blue, pound=1}, {tag=Green, pound=1}, {tag=Red, pound=2}]", cloud.toString() );
现在我们又多了一个浏览文章的方式了。为了保持高效,我们需要向初始数据集添加标签测试数据。
修改/yabe/conf/initial-data.yml来添加一些标签数据。比如:
… Tag(play): name: Play Tag(architecture): name: Architecture Tag(test): name: Test Tag(mvc): name: MVC …
然后添加它们到文章的声明中:
… Post(jeffPost): title: The MVC application postedAt: 2009-06-06 author: jeff tags: - play - architecture - mvc content: > A Play …
在YAML文件的顶部添加Tags的定义,因为它们会被Post用到。
你需要重启应用来加载新的初始数据。注意Play甚至会告诉你出现在YAML文件中的问题:
然后修改#{display /}标签,在全文模式下展示标签集合。修改/yabe/app/views/tags/display.html:
… #{if _as != "full"} | ${_post.comments.size() ?: "no"} comment${_post.comments.size().pluralize()} #{if _post.comments} , latest by ${_post.comments[0].author} #{/if} #{/if} #{elseif _post.tags} #{/elseif} …标签页面
现在我们可以实现通过标签浏览文章了。在#{display /}标签,之前,我们留下一个空链接;现在终于可以用listTagged action补完它(修改yabe/app/views/tags/display.html):
… - Tagged #{list items:post.tags, as:"tag"} ${tag}${tag_isLast ? "" : ", "} #{/list} …
在Application控制器创建action方法:
… public static void listTagged(String tag) { Listposts = 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不是,我们可以简单粗暴地用正则表达式解决问题:
GET /posts/{<[0-9]+>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}
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/64099.html
摘要:通过来实现一个基本的管理面板目前,我们还没法使用博客的来写新的文章,或修改评论。提供了一个即开即用的模块,可以快速生成一个基本的管理面板。这是因为默认是以的输出来得到一个模型对象的表示。在本教程的最后一章,你会学到关于本地化信息的更多东西。 通过CRUD来实现一个基本的管理面板 目前,我们还没法使用博客的UI来写新的文章,或修改评论。Play提供了一个即开即用的CRUD模块,可以快速...
摘要:国际化和本地化完成了博客引擎后,我们来考虑额外的一件事应用的国际化和语言的本地化。国际化和本地化我们将分两步讨论,先是国际化,再是本地化。实际上,两者是同步进行的你在国际化的同时,往往也是在本地化。 国际化和本地化 完成了博客引擎后,我们来考虑额外的一件事:Web应用的国际化和语言的本地化。虽然我们可以一开始就做这件事,但是最好还是先完成该应用的单一语言版本,然后再添加其他语言的支持...
摘要:完成应用测试我们已经完成了我们想要创建的博客引擎。当然我们已经完成了测试所有模型层的功能。评估代码覆盖率当然我们还没有完成应用所需的所有测试用例。如你所见,我们远远没有完成对应用的全面测试。 完成应用测试 我们已经完成了我们想要创建的博客引擎。不过这个项目尚未完全结束。为了保证代码的质量,我们需要添加更多的测试。 当然我们已经完成了测试所有模型层的功能。所以博客引擎的核心功能已经被...
摘要:确保你的文本编辑器已经做了相应的配置。第一个,会自动监测源代码的改变并在运行时自动重载。检查下面的一行是否出现在应用日志中使用版本控制系统来追踪变化当你开发一个项目时,最好使用版本控制系统来存储你的源代码。 Play是一个Java Web敏捷开发的框架http://www.playframework.com/documentation/1.2.7/home 之所以要翻译这个教程,是因...
摘要:浏览和提交评论博客主页现在已经完成,接下来要完成博客正文页面。整个页面将展示当前文章的所有评论,还包括一个用于提交新的评论的表单。刷新浏览器,检查这次是否使用了正确的。给模板添加表单在后面试下提交新的评论。 浏览和提交评论 博客主页现在已经完成,接下来要完成博客正文页面。整个页面将展示当前文章的所有评论,还包括一个用于提交新的评论的表单。 创建show action 要显示文章内...
阅读 3318·2023-04-25 16:25
阅读 3823·2021-11-15 18:01
阅读 1600·2021-09-10 11:21
阅读 3007·2021-08-02 16:53
阅读 3081·2019-08-30 15:55
阅读 2489·2019-08-29 16:24
阅读 2098·2019-08-29 13:14
阅读 1027·2019-08-29 13:00