Play Scala ViewからDBレコード追加
モデル追加に続いて、ViewからDBレコードのCLUDができるようにする。
前回に引き続き、こちらを参考に。 qiita.com
routesの設定
/conf/routes
1 # Routes 2 # This file defines all application routes (Higher priority routes first) 3 4 GET / controllers.HomeController.index 5 GET /article controllers.ArticleController.index # added 6 POST /article controllers.ArticleController.create # added 7 8 # Map static resources from the /public folder to the /assets URL path 9 GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
今回はArticlesController
ではなくArticleController
と命名した。
Ruby on RailsではController名は複数形で書くことを推奨されている。
「推奨されている」というのはどういうことかというと、Controllerが複数形で書かれていれば、resources
で{resource}s_{action}_path
のように、Railsがパスを自動生成してくれる。
が、この「Controller名は複数形で書くべき」という設計(思想?)は間違っているのでは?と思う。
Railsの開発者であるDHHは、Controllerのアクションは全部resourcesで間に合わせるべきで、それ以外のアクションが必要になったらController自体を分けることを推奨している。
でも、全部resources
で書くべきならば、ControllerによってはORM(Railsの場合はActiveRecord)を継承しないクラスを扱うこともあるだろうし、Controller内でindex
アクションを使わないならば、それ以外のresourcesのアクションに関してはresourceが複数であることを意識しないので、どちらかというと単数形で統一するのが正しいのではないかと思う。
Play Scalaでは、今のところ「Controllerは複数形を使ってね」と言われていないので、
単数形で書くことにした。
evolutionsの修正
テーブル名が複数形になっていないのが気持ち悪いので、全部複数形に直す。
/conf/evolutions/default/1.sql
1 # --- !Ups 2 3 CREATE TABLE "users" ( # user -> users 4 "username" TEXT NOT NULL, 5 "password" TEXT NOT NULL, 6 "name" TEXT NOT NULL 7 ); 8 9 CREATE TABLE "articles" ( # article -> articles 10 "user_id" INT NOT NULL, 11 "content" TEXT NOT NULL 12 ); 13 14 CREATE TABLE "comments" ( # comment -> comments 15 "user_id" INT NOT NULL, 16 "article_id" INT NOT NULL, 17 "content" TEXT NOT NULL 18 ); 19 20 # --- !Downs 21 22 DROP TABLE "users"; # user -> users 23 DROP TABLE "articles"; # article -> articles 24 DROP TABLE "comments"; # comment -> comments
DAOの修正
dao
ディレクトリが複数形になっていなかったので直す。- evolutionsの修正にて、複数形に直したテーブルを参照するように修正する。
/app/daos/articleDAO.scala
1 package daos // dao -> daos 2 3 import scala.concurrent.Future 4 5 import javax.inject.Inject 6 import models.Article 7 import play.api.db.slick.DatabaseConfigProvider 8 import play.api.db.slick.HasDatabaseConfigProvider 9 import play.api.libs.concurrent.Execution.Implicits.defaultContext 10 import slick.driver.JdbcProfile 11 12 class ArticleDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] { 13 import driver.api._ 14 15 private val Articles = TableQuery[ArticlesTable] 16 17 def all(): Future[Seq[Article]] = db.run(Articles.result) 18 19 def insert(article: Article): Future[Unit] = db.run(Articles += article).map { _ => () } 20 21 private class ArticlesTable(tag: Tag) extends Table[Article](tag, "articles") { // article -> articles 22 23 def user_id = column[Int]("user_id") 24 def content = column[String]("content") 25 26 def * = (user_id, content) <> (Article.tupled, Article.unapply _) 27 } 28 }
uerDAO
, commentDAO
も同様に修正(省略)。
Controllerの設定
例に倣って書く。
/app/ArticleController.scala
1 package controllers 2 3 import javax.inject._ 4 import play.api._ 5 import play.api.mvc._ 6 import play.api.db._ 7 8 import daos.ArticleDAO 9 import models.Article 10 import play.api.data.Form 11 import play.api.data.Forms.mapping 12 import play.api.data.Forms.number 13 import play.api.data.Forms.text 14 import play.api.libs.concurrent.Execution.Implicits.defaultContext 15 16 @Singleton 17 class ArticleController @Inject() (articleDao: ArticleDAO) extends Controller { 18 def index = Action.async { 19 articleDao.all().map { 20 articles => Ok(views.html.article.index(articles)) 21 } 22 } 23 24 def create = Action.async { implicit request => 25 val article: Article = articleForm.bindFromRequest.get 26 articleDao.insert(article).map(_ => Redirect(routes.ArticleController.index)) 27 } 28 29 val articleForm = Form( 30 mapping( 31 "user_id" -> number, 32 "content" -> text() 33 )(Article.apply)(Article.unapply) 34 ) 35 }
ちなみに31行目、user_id
はDB上ではINT
、Modelとしてもint
として扱っているので、formでtext()
にmappingするとエラーになる。
Playのドキュメントを漁ってみると、int型を扱うにはnumber
が適切っぽいのでnumber
を設定した。
Viewの設定
これも例に倣う。
/app/views/article/index.html.scala
1 @(articles: Seq[Article]) 2 @main("Article database") { 3 <div> 4 <div id="articles"> 5 <h2>Insert an article here:</h2> 6 7 <form action="/article" method="POST"> 8 <input name="user_id" type="text" placeholder="user_id"/> 9 <input name="content" type="text" placeholder="content"/> 10 <input type="submit"/> 11 </form> 12 13 <h2>Previously created articles:</h2> 14 <table> 15 <tr><th>UserId</th><th>Content</th></tr> 16 @for(a <- articles){ 17 <tr><td>@a.user_id</td><td>@a.content</td></tr> 18 } 19 </table> 20 </div> 21 </div> 22 }
実行してみる
sbt run
してhttp://localhost:9000/article
にアクセスしたら問題なく動作しました。
レコードのcreate
もちゃんとできた。
@playdb, PostgreSQL
playdb=> select * from articles; user_id | content ---------+------------------ 1 | hogehoge 2 | 日本語 3 | Play Scala 4 | ぜったいってなに (4 rows)
次はModelにビジネスロジックの実装かな。
Controllerに関しては他のresourcesアクションを追加するくらいしかやることないだろうし、
Viewに関してはControllerから値が渡せればサーバーサイドエンジニアとしてはやること終わりだと思うし。