Sample Play Framework controller forms (form mappings)

Just some quick notes on Play Framework (v2.3) controller form field mappings today ... I’ll add more here if/when I have time, but right now this is a reference page for my Cato CRUD Generator application.

1) The ResearchLink form from my “Finance” application:

// code from my ResearchLinks controller

// note: skipping `id` field
// note: skipping `datetime` field
val researchLinkForm: Form[ResearchLink] = Form(
  mapping(
    "symbol" -> nonEmptyText,
    "url" -> nonEmptyText,
    "notes" -> optional(text)
  )
  // researchLinkForm -> ResearchLink
  ((symbol, url, notes) => ResearchLink(0, symbol, url, Calendar.getInstance.getTime, notes))
  // ResearchLink -> researchLinkForm
  ((rl: ResearchLink) => Some(rl.symbol, rl.url, rl.notes))
)

2) My Stock form from the same application (from the Stocks controller):

  val stockForm: Form[Stock] = Form(
    // defines a mapping that will handle Stock values.
    // the names you use in this mapping (such as 'companyName') must match the names that will be
    // POSTed to your methods in JSON.
    mapping(
      // verifying here creates a field-level error.
      // if your test returns false, the error is shown.
      "symbol" -> nonEmptyText.verifying("Doh - Stock already exists (1)!", Stock.findBySymbol(_) == 0),
      "companyName" -> nonEmptyText)
      ((symbol, companyName) => Stock(0, symbol, companyName))
      ((s: Stock) => Some((s.symbol, s.companyName)))
//      verifying("Doh - Stock already exists (2)!", fields => fields match {
//        // this block creates a 'form' error
//        // this only gets called if all field validations are okay
//        case Stock(id, symbol, company) =>  Stock.findBySymbol(symbol) == 0
//      })
  )

3) My transactionForm from the same application:

val transactionForm: Form[Transaction] = Form(
  // the names you use in this mapping (such as 'symbol') must match the names that will be
  // POSTed to your methods in JSON.
  // note: skipping `id` field
  // note: skipping `datetime` field
  mapping(
    // verifying here creates a field-level error; if your test returns false, the error is shown
    "symbol" -> nonEmptyText,
    "ttype" -> nonEmptyText,
    "price" -> bigDecimal,
    "quantity" -> number,
    "notes" -> text
    )
    // transactionForm -> Transaction
    ((symbol, ttype, price, quantity, notes) => Transaction(0, symbol, ttype, price, quantity, Calendar.getInstance.getTime, notes))
    // Transaction -> TransactionForm
    ((t: Transaction) => Some(t.symbol, t.ttype, t.price.toDouble, t.quantity, t.notes))
)

4) A Notes form from a different application:

object Notes extends Controller with Secured {

  val noteForm: Form[Note] = Form(
    mapping(
      "id" -> longNumber,
      "title" -> text,
      "note" -> text,
      "sortOrder" -> longNumber
    )((id, title, note, sortOrder) => Note(id, title, note, 1, currentDate, currentDate))  // from Form to Note (add process)
     ((note: Note) => Some(note.id, note.title, note.note, note.sortOrder))                // from Note to Form (edit process)
  )

  // more controller code here ...

5) And finally a Urls Play form mapping from a different application:

object Urls extends Controller with Secured {

  /**
   * Problems:
   * 
   * - don't need to see 'id'
   * - don't need/want to see 'user_id'
   * - 'shortUrl' really can be empty
   * - don't need to see 'created'
   * - 'clicks' isn't needed on an input form, just output
   */
  val urlForm = Form(
    tuple(
      "id" -> longNumber,
      "url" -> nonEmptyText,
      "shortUrl" -> text,
      "note" -> optional(text)
    )
    verifying ("Short URL is already in the database", result => result match {
      // TODO this works, but it could be more clear
      case (id, url, shortUrl, note) => {
        val uriIsAlreadyInDb = Url.uriIsInDatabase(shortUrl)
        !uriIsAlreadyInDb
      }
    })
  )
  
  def list = IsAuthenticated { username => implicit request =>
    val urls = Url.findAll()
    Ok(html.urls.list(urls, urlForm))
  }

  def listDetails(shortUri: String) = IsAuthenticated { username => implicit request =>
    val details = BrowserHeaders.findAll(shortUri)
    Ok(html.urls.listUrlHits(details))
  }  

Other form mapping information

I’ll describe this more when I have more time, but I also have these notes on creating Play Framework form field mappings:

//--------------------------------------
// OTHER PLAY FRAMEWORK EXAMPLES & TYPES
//--------------------------------------
"date" -> optional(date("yyyy-MM-dd")),   // java.util.Date
"date" -> date("yyyy-MM-dd"),             // java.util.Date
"username" -> nonEmptyText,
"username" -> nonEmptyText(6),            // requires a minimum of six characters
"notes" -> text,
"password" -> text(minLength = 10),
"count" -> number,
"addToMailingList" -> boolean,
"email" -> email,
"company" -> optional(text),
"numberOfShares" -> optional(number),
"stocks" -> list(text),
"emailAddresses" -> list(email),
"id" -> ignored(1234),

boolean
checked
date
email
ignored
list
longNumber
nonEmptyText
number
optional
seq
single
sqlDate
text

That’s all for today, gotta run.