簡介 由於BLOG涵蓋各種不同類型的文章,導致會越來越難以尋找。為了加快速度查詢,我們將按照分類來排序,這個時候,必須增加標籤(Tagging)選項功能。 The Tag model object 我們將新增Tag Class model,這個方式很容易: package models;
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
@Entity
public class Tag extends Model implements Comparable<Tag> {
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) 的工廠方法(factory method)。因此,試著新增到Tag Class: 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 model與Post做多對多關聯。讓我們增加正確的關聯模式在Post class中: ...
@ManyToMany(cascade=CascadeType.PERSIST)
public Set<Tag> tags;
public Post(User author, String title, String content) {
this.comments = new ArrayList<Comment>();
this.tags = new TreeSet<Tag>();
this.author = author;
this.title = title;
this.content = content;
this.postedAt = new Date();
}
... 注意,我們在這裡使用一個TreeSet是為了保持在有序的排列方式(按字母順序排序,事實上,之前已經實作compareTo)。 我們將保持這種單向關係。為此,增加了一些輔助方法,使標籤管理更容易。首先,標記文章一組標籤: ...
public Post tagItWith(String name) {
tags.add(Tag.findOrCreateByName(name));
return this;
}
... 然後,可以獲取所有特定文章標籤: ...
public static List<Post> findTaggedWith(String tag) {
return Post.find(
"select distinct p from Post p join p.tags as t where t.name = ?", tag
).fetch();
}
... A little more difficult now 我們現在還不會在BLOG使用,但是如果我們想要檢索文章有幾組標籤,它看起來更加困難。試著嘗試JPQL查詢,因為你可能會用到它在多個Web專案中: ...
public static List<Post> 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.t itle, p.content,p.postedAt having count(t.id) = :size"
).bind("tags", tags).bind("size", tags.length).fetch();
}
... 最棘手的部分是我們必須使用一個計數過濾器(count statement filter)可過濾出文章所有標籤並加入至網頁。 注意,我們不能使用Post.find(“…”, tags, tags.count)。這是因為標籤已經是一個vararg。 The tag cloud 我們必須要一個標籤雲(tag cloud)。讓我們增加一個方法在Tag Class來產生標籤雲。 public static List<Map> getCloud() {
List<Map> 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;
} Adding tags to the Blog UI 我們可以使用標籤的方式來瀏覽BLOG。一如往常,為了提高工作效率,我們需要增加一個標籤,修改 /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檔案上方,因為他們需要建立在Post引用它們之前被產生出來做關聯。你需要重新啟動你的應用程式載入YAML檔案並初始化。注意,Play會告訴你有關YAML檔案的相關問題: 然後修改#{display /}來設定文章標籤為full時顯示。編輯/yabe/app/views/tags/display.html: ...
#{if _as != 'full'}
<span class="post-comments">
| ${_post.comments.size() ?: 'no'}
comment${_post.comments.size().pluralize()}
#{if _post.comments}
, latest by ${_post.comments[0].author}
#{/if}
</span>
#{/if}
#{elseif _post.tags}
<span class="post-tags">
- Tagged
#{list items:_post.tags, as:'tag'}
<a href="#">${tag}</a>${tag_isLast ? '' : ', '}
#{/list}
</span>
#{/elseif}
... The new ‘tagged with’ page 現在我們可以增加新的方式來依照標籤列出BLOG相關文章。我們將更改超連結來對應listTagged action: ...
- Tagged
#{list items:_post.tags, as:'tag'}
<a href="@{Application.listTagged(tag.name)}">${tag}</a>${tag_isLast ? '' : ', '}
#{/list}
... 在Application controller建立一組action方法: ...
public static void listTagged(String tag) {
List<Post> posts = Post.findTaggedWith(tag);
render(tag, posts);
}
... 一如往常,我們必須建立一個具體的URL路徑: GET /posts/{tag} Application.listTagged 接著,這裡出現一個問題,因為現有的URL路徑發生衝突,這兩條路徑已匹配相同的URL路徑: 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}
<h3>There are ${posts.size()} posts tagged '${tag}'</h3>
#{/if}
#{elseif posts}
<h3>There is 1 post tagged '${tag}'</h3>
#{/elseif}
#{else}
<h3>No post tagged '${tag}'</h3>
#{/else}
*{********* Posts list *********}*
<div class="older-posts">
#{list items:posts, as:'post'}
#{display post:post, as:'teaser' /}
#{/list}
</div> |