3
10

More than 3 years have passed since last update.

Play Framework入門(2.8.8) 掲示板の作成まで

Last updated at Posted at 2021-05-27

先日、Java&ScalaのフレームワークであるPlay Framework(以下Play)を使って掲示板サイトを作ったので、それの備忘録を残したいと思います。

この記事ではPlayFrameworkの公式とgithubにある公式サンプルを多数引用します。

実行環境
PC: Ubuntu20.04
java: 11.0.11
sbt: 1.4.9
Play: 2.8.8

Play! Play Framework

Playとは、MVCでWebサイトを構築するフレームワークです。
MVCとは、Model、View、Controllerの頭文字を取ったものです。

  • Model: データの管理、取得などを担当
  • View: データの表示を担当
  • Controller: ModelとViewの制御を担当

Javaの導入

Playをインストールするには、まずJavaが必要です。
ubuntuであればこちらの記事を参考にしましょう

Javaをインストールする最も簡単なオプションは、Ubuntuに含まれているバージョンを使用することです。デフォルトでは、Ubuntu 20.04には、JREとJDKのオープンソースバージョンであるOpen JDK 11が含まれています。

Javaのインストールコマンド
sudo apt update
java -version
sudo apt install default-jdk

sbtの導入

sbtとは Java版のnpm,composer,pipのようなものです。 Scalaのビルドツールです

sbt is a build tool for Scala, Java, and more. It requires Java 1.6 or later.

こちらの記事を参考に導入しましょう。

Ubuntuであれば、書かれている通りに行えば導入できます。

Ubuntu 及びその他の Debian ベースのディストリビューションは DEB フォーマットを用いるが、 ローカルの DEB ファイルからソフトウェアをインストールすることは稀だ。 これらのディストロは通常コマンドラインや GUI 上から使えるパッケージ・マネージャがあって (例: apt-get、aptitude、Synaptic など)、インストールはそれらから行う。 ターミナル上から以下を実行すると sbt をインストールできる (superuser 権限を必要とするため、sudo を使っている)。

sbtの導入
echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list
echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | sudo tee /etc/apt/sources.list.d/sbt_old.list
curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo apt-key add
sudo apt-get update
sudo apt-get install sbt

ほかの方は下記の記事を参考に頑張ってください!

Playの作成

こちらの記事を参考に、Java版のPlayを構築します。

Playを作成
sbt new playframework/play-java-seed.g8

Playのファイル構造はこのような形になっています。

app                      → Application sources
 └ assets                → Compiled asset sources
    └ stylesheets        → Typically LESS CSS sources
    └ javascripts        → Typically CoffeeScript sources
 └ controllers           → Application controllers
 └ models                → Application business layer
 └ views                 → Templates
build.sbt                → Application build script
conf                     → Configurations files and other non-compiled resources (on classpath)
 └ application.conf      → Main configuration file
 └ routes                → Routes definition
dist                     → Arbitrary files to be included in your projects distribution
public                   → Public assets
 └ stylesheets           → CSS files
 └ javascripts           → Javascript files
 └ images                → Image files
project                  → sbt configuration files
 └ build.properties      → Marker for sbt project
 └ plugins.sbt           → sbt plugins including the declaration for Play itself
lib                      → Unmanaged libraries dependencieswot.dannbo@gmail.com

logs                     → Logs folder
 └ application.log       → Default log file
target                   → Generated stuff
 └ resolution-cache      → Info about dependencies
 └ scala-2.13
    └ api                → Generated API docs
    └ classes            → Compiled class files
    └ routes             → Sources generated from routes
    └ twirl              → Sources generated from templates
 └ universal             → Application packaging
 └ web                   → Compiled web assets
test                     → source folder for unit or functional tests

引用

Welcom to Play!

さて、実際にPlayを動かしてみましょう。
Playのプロジェクトを開いてsbt runします。

PLayの実行
cd Playのプロジェクトを作成したディレクトリ
sbt run

しばらく待ってlocalhost:9000をブラウザで開くとPlayのページが開きます。
これで導入成功です!

もろもろのライブラリの導入

今回使うライブラリを導入していきます

  • ebean
  • evolutions
  • guice

ebeanとは、Javaで使えるORマッパーです。

O/Rマッピングとは、オブジェクト指向プログラミング言語におけるオブジェクトとリレーショナルデータベース(RDB)の間でデータ形式の相互変換を行うこと。そのための機能やソフトウェアを「O/Rマッパー」(O/R mapper)という。

evolutionはマイグレーションに使えるライブラリです。
データベースに初回に登録しておきたいデータや、データを移行させたい時にsqlのスクリプトを定義しておき、それが初回起動時に実行されます。

移動、移住、移転」を意味する英語の「migration」が語源。IT分野では、ソフトウェアやハードウェア、システム、データ、開発言語などを別のプラットフォームに移行したり、新しいシステムに切り替えたりすることを意味する。似た言葉に「リプレース」があるが、これは古くなったり破損したりしたハードウェアやソフトウェア、システムなどを、新しいものや同等の機能を持ったものに置き換えることを指す。

guiceはテストで使うライブラリです。アプリケーションに依存性を注入する際などに使用します。

これらを導入するには、sbtのbuildファイルを編集します。

build.sbt
name := """sample"""
organization := "com.example"

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayJava, PlayEbean)

scalaVersion := "2.13.5"

libraryDependencies ++= Seq(
  guice,
  jdbc,
  evolutions,
  "javax.xml.bind" % "jaxb-api" % "2.3.1",
  "javax.activation" % "activation" % "1.1.1",
  "org.glassfish.jaxb" % "jaxb-runtime" % "2.3.2",
  "com.h2database" % "h2" % "1.4.192",
)
plugins.sbt
// The Play plugin
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.8")

addSbtPlugin("com.typesafe.sbt" % "sbt-play-ebean" % "5.0.2")
// Defines scaffolding (found under .g8 folder)
// http://www.foundweekends.org/giter8/scaffolding.html
// sbt "g8Scaffold form"

参考にしたコード
build.sbt

plugins.sbt

また、ついでにPlayの設定ファイルも書き換えましょう

application.conf
# This is the main configuration file for the application.
# https://www.playframework.com/documentation/latest/ConfigFile

# Default database configuration using H2 database engine in an in-memory mode
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play;"

ebean.default="models.*"

play.evolutions.enabled=true

参考

こちらではインメモリーデータベースであるh2の設定とebeanの設定とevolutionの設定を行っています。

Evolutionのスクリプトの作成

1.sql
# --- First database schema

# --- !Ups

create table user (
    id int auto_increment primary key,
    name varchar(255) not null,
    text varchar(500) not null,
);

insert into user (id,name,text) values (default, '名無し', 'サンプルテキストです。');
insert into user (id,name,text) values (default, '名無し', 'サンプルテキスト2です。');

# --- !Downs

drop table users;

今回作ったのは最低限の設定です。
Userテーブルを一つ作り、id、name、textが入っています。
evolutionのファイルスクリプトの置き場は
conf/evolutions/default/1.sqlになります。フォルダとファイルを作成しましょう。

参考

これでライブラリが使用出来るようになります。

Modelの作成

アプリケーションで使うModelを作成しましょう。
appの中にmodelsディレクトリを作成し、その中に作成していきます。
2つのModelを作っていきます
User.javaがデータベースのデータを参照できます。
今回はModelでデータベースに接続するので、User.javaにebeanのFinderを使用してデータベースの値を取得するメソッドを作りました。
また、Playのバリデーションライブラリを使用して、値を必須にしました。

User.java
package models;

import io.ebean.Model;
import io.ebean.Finder;

import play.data.validation.Constraints;

import javax.persistence.Id;
import javax.persistence.Entity;

@Entity
public class User extends Model {
    public static Finder<Long,User> finder = new Finder<>(User.class);

    @Id
    private Long id;

    @Constraints.Required
    private String name;

    @Constraints.Required
    private String text;

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
}

参考

UserForm.javaはFormで送れるデータを定義しています
こちらもバリデーションで値を必須、また値を255に制限しています。
idは、sqlの設定でdefaultの値が入るようになっていますので、今回のケースでは必要無いです。

UserForm.java
package models;

import play.data.validation.Constraints;

public class UserForm {

    @Constraints.Required(message="必須入力です")
    @Constraints.MaxLength(value=255, message="255文字以下にしてください。")
    private String name;

    @Constraints.Required(message="必須入力です")
    @Constraints.MaxLength(value=255, message="255文字以下にしてください。")
    private String text;

    public UserForm() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
}

参考

Controllerの作成

次にコントローラーを作成します。

行っていることは、FormFactoryでFormを作り、showFormdでViewへ渡しています。
View側でFormがcreateメソッドに送られた時には、Userクラスを用いてデータを定義し、Ebaenでデータベースにデータを保存しています。

HomeController
package controllers;

import models.User;
import models.UserForm;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.data.Form;
import play.data.FormFactory;
import play.i18n.MessagesApi;
import play.mvc.*;

import io.ebean.*;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;

@Singleton
public class FormController extends Controller {
    private final Form<UserForm> form;

    private MessagesApi messagesApi;
    private List<User> users;

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Inject
    public FormController(FormFactory formFactory, MessagesApi messagesApi) {
        this.users = User.finder.all();
        this.messagesApi = messagesApi;

        this.form = formFactory.form(UserForm.class);
    }

    public Result showForm(Http.Request request) {
        this.users = User.finder.all();
        return ok(views.html.board.render(users, form, request, messagesApi.preferred(request)));
    }

    public Result create(Http.Request request) {
        final Form<UserForm> boundForm = boardCreateForm.bindFromRequest(request);

        if (boundForm.hasErrors()) {
            logger.error("errors = {}", boundForm.errors());
            return badRequest(views.html.board.render(users, boundForm, request, messagesApi.preferred(request)));
        } else {
            UserForm data = boundForm.get();
            User addUser = new User();
            addUser.setName(data.getName());
            addUser.setText(data.getText());
            Ebean.save(addUser);
            return redirect(routes.FormController.showForm()).flashing("info", "書き込みました");
        }
    }
}

Viewの作成

Viewでは、Scalaの記法を使って、Controllerから送られてくるデータを定義できます。
@を使うことで、ScalaでifやforなどをHTML内に書くことができます。
formの表示には@helperを使います。

index.scala.html
@import play.mvc.Http.Request
@(users: List[User], form: Form[UserForm])(implicit request: Request, messages: play.i18n.Messages)

@main("掲示板") {
  <h1>掲示板</h1>

  @request.flash.asScala().data.map { case (name, value) =>
    <div class="@name">@value</div>
  }

  <table>
      @for(u <- users) {
          <tr><td>@u.name</td><td>@u.text</td></tr>
      }
  </table>

  <hr/>

  @if(form.hasGlobalErrors) {
      @form.globalErrors.asScala.map { error: play.data.validation.ValidationError =>
          <div>
              @error.key: @error.message
          </div>
      }
  }

  @helper.form(routes.FormController.create) {
      @helper.CSRF.formField

      @helper.inputText(form("name"))

      @helper.inputText(form("text"))

      <button type="submit">投稿</button>
  }
}

これで完成です!

localhost:9000を見てみると、FormとUserの名前とテキストが表示されていると思います。

3
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
10