本家サイトの Tutorial をかいつまんで。
このチュートリアルでは、'Yet Another Blog Engine' 略して yabe というプロジェクトにてブログエンジンを作成していく。
プロジェクトの作成
yabeというプロジェクトを新規作成。アプリケーション名は'Yet Another Blog Engine'とする。
> play new yabe
> play eclipsify yabe
app/views/Application/index.html にある welcome メッセージを編集し以下のようにする。
#{extends 'main.html' /} #{set title:'Home' /} <h1>A blog will be there</h1>
動作確認
> play run yabe
データベースの設定
Play には HSQLDB が添付されている。最初は HSQLDB をインメモリで使用する。conf/application.conf の Database configuration の項目を編集し、以下の定義を有効にする。
# To quickly set up a development database, use either: # - mem : for a transient in memory database (HSQL in memory) # - fs : for a simple file written database (HSQL file stored) db=mem
再起動は不要で、ブラウザのリロードでDB接続が有効になる。
モデルとなる User クラス作成
Play での OR マッピングには JPA を利用する。app/models/User.java を以下の内容で新規作成。
package models; import java.util.*; import javax.persistence.*; import play.db.jpa.*; @Entity public class User extends Model { public String email; public String password; public String fullname; public boolean isAdmin; public User(String email, String password, String fullname) { this.email = email; this.password = password; this.fullname = fullname; } }
@Entity によるアノテートにより全てのフィールドが自動的に永続化対象となる。@Id が無いが、親クラスで定義されている。
User クラスのテスト作成
/test/BasicTest.java に定義されている既存のテストメソッドを削除し以下に変更する。
@Test public void createAndRetrieveUser() { new User("bob@gmail.com", "secret", "Bob").save(); User bob = User.find("byEmail", "bob@gmail.com").first(); assertNotNull(bob); assertEquals("Bob", bob.fullname); }
テストの実行
> play test yabe
http://localhost:9000/@tests でアクセスしてBasicTest.javaを選択→Start!でグリーンを確認。
Userクラスに email password でユーザを認証するメソッド追加。
public static User connect(String email, String password) { return find("byEmailAndPassword", email, password).first(); }
BasicTest.java にテストメソッドを追加。
@Test public void tryConnectAsUser() { new User("bob@gmail.com", "secret", "Bob").save(); assertNotNull(User.connect("bob@gmail.com", "secret")); assertNull(User.connect("bob@gmail.com", "badpassword")); assertNull(User.connect("tom@gmail.com", "secret")); }
テスト実施してグリーンを確認。失敗させてもおもしろいです。
Postクラスの作成
ブログへのポスト内容を表す Post クラスを作成。
package models; import java.util.*; import javax.persistence.*; import play.db.jpa.*; @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(); } }
@ManyToOne にて先程のUserと多対一の関係を定義。
テストケースの追加
テスト実施前にデータベースの内容を削除するよう事前処理を追加。
public class BasicTest extends UnitTest { @Before public void setup() { Fixtures.deleteAll(); } ・・・
BasicTest に以下のテストメソッド追加。
@Test public void createPost() { User bob = new User("bob@gmail.com", "secret", "Bob").save(); new Post(bob, "My first post", "Hello world").save(); assertEquals(1, Post.count()); List<Post> bobPosts = Post.find("byAuthor", bob).fetch(); assertEquals(1, bobPosts.size()); Post firstPost = bobPosts.get(0); assertNotNull(firstPost); assertEquals(bob, firstPost.author); assertEquals("My first post", firstPost.title); assertEquals("Hello world", firstPost.content); assertNotNull(firstPost.postedAt); }
最後にコメントクラスの追加
ユーザのポストに対するコメントを表すクラスを追加。
package models; import java.util.*; import javax.persistence.*; import play.db.jpa.*; @Entity public class Comment extends Model { public String author; public Date postedAt; @Lob public String content; @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(); } }
Postとの関係は@ManyToOneで定義。
@Test public void postComments() { User bob = new User("bob@gmail.com", "secret", "Bob").save(); Post bobPost = new Post(bob, "My first post", "Hello world").save(); // Post a first comment new Comment(bobPost, "Jeff", "Nice post").save(); new Comment(bobPost, "Tom", "I knew that !").save(); List<Comment> bobPostComments = Comment.find("byPost", bobPost).fetch(); assertEquals(2, bobPostComments.size()); Comment firstComment = bobPostComments.get(0); assertNotNull(firstComment); assertEquals("Jeff", firstComment.author); assertEquals("Nice post", firstComment.content); assertNotNull(firstComment.postedAt); Comment secondComment = bobPostComments.get(1); assertNotNull(secondComment); assertEquals("Tom", secondComment.author); assertEquals("I knew that !", secondComment.content); assertNotNull(secondComment.postedAt); }
PostクラスとCommentクラスのリレーション
Post から Comment を導けるように Post へ以下のフィールドを追加。
@OneToMany(mappedBy="post", cascade=CascadeType.ALL) public List<Comment> comments = new ArrayList<Comment>();
CascadeType.ALL にてPostが消えたらコメントも削除されるように定義。
また、Post へコメント追加のためのメソッドを追加。
public Post addComment(String author, String content) { Comment newComment = new Comment(this, author, content).save(); this.comments.add(newComment); return this; }
最後にテストケース追加
追加した addComment のテストケースを追加
@Test public void useTheCommentsRelation() { User bob = new User("bob@gmail.com", "secret", "Bob").save(); Post bobPost = new Post(bob, "My first post", "Hello world").save(); // Post a first comment bobPost.addComment("Jeff", "Nice post"); bobPost.addComment("Tom", "I knew that !"); assertEquals(1, User.count()); assertEquals(1, Post.count()); assertEquals(2, Comment.count()); bobPost = Post.find("byAuthor", bob).first(); assertNotNull(bobPost); assertEquals(2, bobPost.comments.size()); assertEquals("Jeff", bobPost.comments.get(0).author); bobPost.delete(); assertEquals(1, User.count()); assertEquals(0, Post.count()); assertEquals(0, Comment.count()); }