Hkt
Hkt

Reputation: 65

Cannot insert values from form using Play for Scala and Anorm

When I enter data into the form fields and click on "Add book" button it happens nothing... The entered data into fields it's not added into the database, I receive no error and I don't know what I'm doing wrong...

I have the following controller

package controllers
import play.api.data.Form
import play.api.data.Forms._
import play.api.mvc.{Action, Controller}
import anorm.NotAssigned
import play.api.Play.current
import play.api.i18n.Messages.Implicits._
import models.Buch

object Book extends Controller{

 val addBookForm = Form(
     mapping(
         "name" -> nonEmptyText,
         "author" -> nonEmptyText,
         "category" -> nonEmptyText,
         "read" -> boolean,
         "amount" -> number
     )(Buch.apply)(Buch.unapply)
 )  

 def add = Action {
     Ok(views.html.book(addBookForm))
 }


 def addBuch() = Action { implicit request =>
  addBookForm.bindFromRequest.fold(
    errors => BadRequest,
    {
      case Buch(name, author, category, read, amount) =>
      Buch.addBook(Buch(name, author, category, read, amount))
      Ok("Book successfully added!")

     }
    )
   }
 }

The following model

 package models

 import play.api.db._
 import play.api.Play.current

 import anorm._
 import anorm.SqlParser._

 case class Buch(name:String, author:String, category:String, read:Boolean, amount: Int)

 object Buch{

     val simple = {
         get[String]("buch.name") ~
         get[String]("buch.auhtor") ~
         get[String]("buch.category") ~
         get[Boolean]("buch.read") ~
         get[Int]("buch.amount") map {
       case name~author~category~read~amount => Buch(name, author, category, read, amount)
       }
     }

def findAll(): Seq[Buch] = {
   DB.withConnection { implicit connection =>
     SQL("select * from buch").as(Buch.simple *)
   }
}

def addBook(buch:Buch): Unit = {
   DB.withConnection { implicit connection =>
     SQL("insert into buch(name, author, category, read, amount) values ({name}, {author}, {category}, {read}, {amount})").on(
       'name -> buch.name,
       'author -> buch.author,
       'category -> buch.category,
       'read -> buch.read,
       'amount -> buch.amount
    ).executeUpdate()
  }
 }
}

The following sql script

  # Buch schema

  # --- !Ups

  CREATE TABLE Buch (
      id bigint(20) NOT NULL AUTO_INCREMENT,
      name varchar(255) NOT NULL,
      author varchar(255) NOT NULL,
      category varchar(255) NOT NULL,
      read boolean NOT NULL DEFAULT '0',
      amount int(11) NOT NULL,
      PRIMARY KEY (id)
  );


  # --- !Downs

  DROP TABLE Buch;

The following view

  @(form: Form[Buch])(implicit messages: Messages)

  @main("Welcome to Play 2.0") {

  @helper.form(action = routes.Book.addBuch) {
       @helper.inputText(form("name"))
       @helper.inputText(form("author"))
       @helper.inputText(form("category"))
       @helper.inputText(form("read"))
       @helper.inputText(form("amount"))
       <input type="submit" value="Add book"/>
   }

 }

If I modify the model, for example creating it with only 3 parameters, name, author and category the method addBook will insert the data into my database.

Can anyone please tell what am I doing wrong?

Thank you!

Upvotes: 2

Views: 656

Answers (1)

Shyamendra Solanki
Shyamendra Solanki

Reputation: 8851

In mysql, read is a reserved word. See docs.

CREATE TABLE Buch (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  name varchar(255) NOT NULL,
  author varchar(255) NOT NULL,
  category varchar(255) NOT NULL,
  read boolean NOT NULL DEFAULT '0',  
  amount int(11) NOT NULL,
  PRIMARY KEY (id)
);

You should replace it with some non-reserved word (e.g. book_read) or escape it by putting in backticks like this:

CREATE TABLE Buch (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  name varchar(255) NOT NULL,
  author varchar(255) NOT NULL,
  category varchar(255) NOT NULL,
  `read` boolean NOT NULL DEFAULT '0',
  amount int(11) NOT NULL,
  PRIMARY KEY (id)
);

Edit:

Anorm does not provide default mapping from mysql boolean to scala boolean. From its documentation:

It’s possible to add custom mapping, for example if underlying DB doesn’t support boolean datatype and returns integer instead. To do so, you have to provide a new implicit conversion for Column[T], where T is the target Scala type:

import anorm.Column

// Custom conversion from JDBC column to Boolean
implicit def columnToBoolean: Column[Boolean] = 
  Column.nonNull1 { (value, meta) =>
    val MetaDataItem(qualified, nullable, clazz) = meta
    value match {
      case bool: Boolean => Right(bool) // Provided-default case
      case bit: Int      => Right(bit == 1) // Custom conversion
      case _             => 
       Left(TypeDoesNotMatch(s"Cannot convert $value: ${value.asInstanceOf[AnyRef].getClass} to Boolean 
 for column $qualified"))
    }
  }

Also see this discussion on Anorm - Mysql Boolean.

Upvotes: 1

Related Questions