Voici un exemple de formulaire d'inscription
Le model :
Il n'y a rien de bien compliqué ou de particulier (du moins j'ai l'impression), du coup je ne commente pas spécialement.
Le contrôleur :
Et la vue :
Et le layout :
Le model :
package models
import org.squeryl.PrimitiveTypeMode._
import org.squeryl.{KeyedEntity, Schema}
import org.squeryl.annotations.Column
import org.squeryl.dsl._
import com.roundeights.hasher.Hasher
import java.util.{Date}
import scala.util.Random
import models.Db._
class User(
val id: Long,
@Column(length=40)
val email: String,
@Column(length=20)
val name: String,
@Column(length=120)
val password: String,
@Column(length=7)
val color: String,
@Column("create_date")
val createDate: Date
) extends KeyedEntity[Long] {
def this(login: String, name: String, password: String, color: String) = this(0, login, name, User.encryptPassword(password), color, new Date())
lazy val squares: OneToMany[Square] = Db.userToSquares.left(this)
}
object User {
def findByEmail(email: String) =
from(users)(u =>
where(u.email === email)
select(u)
).headOption
def encryptPassword(password: String) = Hasher(password).bcrypt
}
Il n'y a rien de bien compliqué ou de particulier (du moins j'ai l'impression), du coup je ne commente pas spécialement.
Le contrôleur :
package controllers
import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.validation.Constraints._
import play.api.data.Forms._
import play.api.i18n._
import org.squeryl.PrimitiveTypeMode._
import models._
object Application extends Controller {
val subscribeForm = Form(
tuple(
"email" -> text
.verifying(nonEmpty)
.verifying(maxLength(40))
.verifying(Messages("constraint.email.invalid"), email => email.matches(".+@.+\\..+"))
.verifying(Messages("constraint.email.used"), email => transaction {
!User.findByEmail(email).isDefined
}
),
"name" -> text
.verifying(nonEmpty)
.verifying(maxLength(20))
.verifying(minLength(2)),
"password" -> text
.verifying(nonEmpty)
.verifying(minLength(4)),
"color" -> text
.verifying(nonEmpty)
.verifying(Messages("constraint.color.invalid"), color => color.matches("#[0-9a-fA-F]{6}"))
)
)
def index = Action {
println(User.encryptPassword("oxman"))
Ok(views.html.application.index(subscribeForm))
}
def subscribe = Action { implicit request =>
subscribeForm.bindFromRequest.fold(
errors => BadRequest(views.html.application.index(errors)),
subscribe => {
subscribe match {
case (email, name, password, color) => {
transaction {
Db.users.insert(new User(email, name, password, color))
}
Ok(views.html.application.welcome())
}
}
}
)
}
}
Et la vue :
@(subscribeForm: Form[(String, String, String, String)])
@import helper._
@implicitField = @{ FieldConstructor(helpers.twitterBootstrapHorizontalInput.f) }
@scripts = {
<script src="@routes.Assets.at("javascripts/bootstrap-colorpicker.js")" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
$('.colorpicker').colorpicker().on("changeColor", function(ev) {
color = ev.color.toRGB()
$(this).val(ev.color.toHex())
$(this).css({
"background-color": "rgb(" + color.r + "," + color.g + "," + color.b + ")",
"color": "rgb(" + (color.r-80) + "," + (color.g-80) + "," + (color.b-80) + ")"
})
})
})
</script>
}
@styles = {
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/colorpicker.css")">
}
@main("Delta... plane", "/", scripts, styles) {
<div class="span8">
@helper.form(action = routes.Application.subscribe, 'class -> "form-horizontal") {
<fieldset>
<legend>Inscription</legend>
@inputText(
subscribeForm("email"),
'_label -> "Email"
)
@inputText(
subscribeForm("name"),
'_label -> "Nom"
)
@inputPassword(
subscribeForm("password"),
'_label -> "Mot de passe"
)
@inputText(
subscribeForm("color"),
'_label -> "Couleur",
'class -> "colorpicker"
)
<div class="form-actions">
<button type="submit" class="btn btn-primary">S'inscrire</button>
</div>
</fieldset>
}
</div>
}
Et le layout :
@(title: String, route: String = "/", scripts: Html = Html(""), styles: Html = Html(""))(content: Html)
<!DOCTYPE html>
<html>
<head>
<title>@title</title>
<link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/bootstrap.css")">
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/bootstrap-responsive.css")">
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
@styles
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="brand" href="/">Delta</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li class="@if(route == "/") { active }">
<a href="/">Principe</a>
</li>
<li class="@if(route == "/play") { active }">
<a href="/play">Jouer</a>
</li>
<li class="@if(route == "/scoreboard") { active }">
<a href="/scoreboard">Classement</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
@content
</div>
<script src="@routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
<script src="@routes.Assets.at("javascripts/raphael-min.js")" type="text/javascript"></script>
@scripts
</body>
</html>