Create model class in Scala with Play 2 Framework - scala

I have a Play project, who contains a User model. Before, I used case class declaration with object declaration to access the model in my application. It works.
But now I want to declare a User class, to have multiple instances ; because I have understand that when you have a model with an object, it'as a singleton, so one instance.
And my project is a website which will have many users.
When I declare my class, as example :
class User(val username: String, var firstname: String, var lastname: String, var company: String, var password: String){}
The model isn't identified by my application (Application.scala)
How can I realise what I want ?
Old code :
case class User(username: String, password: String, firstname: String, lastname: String, company: String)
object User {
val simple = {
get[String]("username") ~
get[String]("password") ~
get[String]("firstname") ~
get[String]("lastname") ~
get[String]("company") map {
case username~password~firstname~lastname~company =>
User(username, password, firstname, lastname, company)
}
}
def findAll(): Seq[User] = {
DB.withConnection { implicit connection =>
SQL("SELECT * FROM user").as(User.simple *)
}
}
def create(user: User): Unit = {
DB.withConnection { implicit connection =>
SQL("INSERT INTO user(username, password, firstname, lastname, company) VALUES ({username}, {password}, {firstname}, {lastname}, {company})").on(
'username -> user.username,
'password -> user.password,
'firstname -> user.firstname,
'lastname -> user.lastname,
'company -> user.company
).executeUpdate()
}
}
def getOne(username: String): Option[User] = {
DB.withConnection { implicit connection =>
val selectUser = SQL("SELECT username, password, firstname, lastname, company FROM user where username={username}")
.on('username -> "azuken")()
.collectFirst {
case Row(username: String, password: String, firstname: String, lastname: String, company: String) =>
User(username, password, firstname, lastname, company)
case Row(username:String, password:String, firstname: String) =>
User(username, password, "bobby", "lqsdast", "sdf")
case _ =>
User("username", "password", "firstname", "lastname", "company")
}
selectUser
}
}
def authentication(username: String, password: String): Long = {
DB.withConnection { implicit connection =>
val firstRow = SQL("SELECT COUNT(*) AS NbRep FROM user WHERE username = {username} AND password = {password}").on(
'username -> username,
'password -> password
).apply().head
firstRow[Long]("NbRep")
}
}
}

Related

mapping a Map object to an Option[Iterable[Row]] to derive an Option[List[Address]]

I have the following case classes:
case class Address (
val addressLine1: String,
val addressLine2: String,
val city: String,
val provinceCode: String,
val country: String,
val addressTypeDesc: String)
and
case class ClientData(
val title: String,
val firstName: String,
val lastName: String,
val addrList: Option[List[Address]]
)
I also have the following object:
import org.apache.spark.sql.Row
object ClientBuilder {
def build(client: Row, addr: Option[Iterable[Row]], addrType: Map[String, String]): ClientData = {
// The object validates that the field “ClientTitle” is empty or one of the following values only:
// "Mr.", "Ms.", "Mrs." - Otherwise the build will throw an IllegalArgumentException
val title: String =
client.getAs[String]("Title") match {
case "Mr." => "Mr."
case "Ms." => "Ms."
case "Mrs." => "Mrs."
case "" => ""
case _ => throwException("Client Title is not as expected")
}
val firstName: String = client.getAs[String]("FirstName")
val lastName: String = client.getAs[String]("LastName")
val addrList: Option[List[Address]] = // having some problem figuring this part out
ClientData(title, firstName, lastName, addrList)
}
def throwException(exceptionReason: String) = {
throw new IllegalArgumentException(s"Exception thrown due to: ${exceptionReason}")
}
}
The addr: Option[Iterable[Row]]
has the following columns:
AddressID,AddressLine1,AddressLine2,City,ProvinceName,ProvinceCode,Country,ClientID,AddressTypeCode
and the addrType: Map[String, String])
is as follows:
Map(1 -> "Billing", 2 -> "Home", 3 -> "Main Office", 4 -> "Primary", 5 -> "Shipping", 6 -> "Archive")
I would like to Join the addr: Option[Iterable[Row]] with the addrType: Map[String, String]) on the addr column AddressTypeCode and addrType key to get the addressTypeDesc and create a list of type Address.
Ok so I managed to solve it by iterating through the Option[Iterable[Row]] and using the Option .get() method to map the addrType to the list of addresses for each client within the Option[Iterable[Row]]. Here is how I achieved this:
object ClientBuilder {
def build(client: Row, addr: Option[Iterable[Row]], addrType: Map[String, String]): ClientData = {
// The object validates that the field “ClientTitle” is empty or one of the following values only:
// "Mr.", "Ms.", "Mrs." - Otherwise the build will throw an IllegalArgumentException
val title: String =
client.getAs[String]("Title") match {
case "Mr." => "Mr."
case "Ms." => "Ms."
case "Mrs." => "Mrs."
case "" => ""
case _ => throw new IllegalArgumentException(s"Exception thrown due to: Invalid Title. Title must be: Mr., Ms., Mrs., or empty.")
}
val firstName: String = client.getAs[String]("FirstName")
val lastName: String = client.getAs[String]("LastName")
// iterate through the Option[Iterable[Row]] and write each row to Option[Iterable[Address]].
val addrList = Option(
for(address <- addr.getOrElse(List()).toList) yield {
Address(
address.getAs("AddressLine1"),
address.getAs("AddressLine2"),
address.getAs("City"),
address.getAs("ProvinceCode"),
address.getAs("Country"),
// using the getAs() method of the addrType Map object map get the AddressTypeDescription
addrType.getOrElse(address.getAs("AddressTypeCode").toString,"No such address type")
)
}
)
ClientData(title, firstName, lastName, addrList)
}
}
However this might not be the most efficient approach. After speaking with one of my colleagues they suggested using the following approach:
object ClientBuilder {
def build(client: Row, addr: Option[Iterable[Row]], addrType: Map[String,String]): ClientData = {
val addrList: Option[List[Address]] =
if (addr.isEmpty) None
else
Some(
addr.get.map( (addrRow: Row) =>
Address(
addrRow.getAs[String]("AddressLine1")
, addrRow.getAs[String]("AddressLine2")
, addrRow.getAs[String]("City")
, addrRow.getAs[String]("ProvinceCode")
, addrRow.getAs[String]("Country")
, addrType.get(addrRow.getAs[String]("AddressTypeCode")).getOrElse("")
)
).toList
)
val firstName: String = DFAsist.safeGeteString(client, "FirstName").getOrElse("")
val lastName: String = DFAsist.safeGeteString(client, "LastName").getOrElse("")
val title: String = DFAsist.safeGeteString(client, "Title") match {
case None => ""
case Some(s) if List("Mr.", "Ms.", "Mrs.").contains(s) => s
case _ => throw new IllegalArgumentException("Title is wrong!!!")
}
ClientData(title,firstName,lastName, addrList)
}

how to write insert query for table that contains reference column to another table in play-slick with scala?

I am using slick 3.1, Play framework 2.5 and scala 2.11. My application have 'User' table with reference column 'addressId' which refers to 'id' column of 'Address' table. I am not able to insert data in these both tables. Here is code for this
This is the UserForm:
val registerForm: Form[RegisterForm] = Form {
mapping(
"id" -> optional(number),
"firstname" -> nonEmptyText.verifying("letters only", name => name.isEmpty || name.matches("[A-z\\s]+")),
"lastname" -> nonEmptyText.verifying("letters only", name => name.isEmpty || name.matches("[A-z\\s]+")),
"email" -> text.verifying(nonEmpty),
"password" -> text.verifying(nonEmpty),
"address" -> mapping(
"id" -> optional(number),
"city" -> nonEmptyText.verifying("letters only", name => name.isEmpty || name.matches("[A-z\\s]+")),
"state" -> text,
"country" -> text,
"pin" -> number
)(AddressForm.apply)(AddressForm.unapply)
)(RegisterForm.apply)(RegisterForm.unapply)
}
These are two model classes:
case class Address(id: Option[Int], city: String, state: String, country: String, pin: Int)
case class User(id: Option[Int], firstname: String, lastname: String, email: String, password: String, addressId: Option[Int])
Here is both classes mapped to corresponding database tables with tables query:
private class UserDB(tag: Tag) extends Table[User](tag, "user") {
def id = column[Option[Int]]("id", O.PrimaryKey, O.AutoInc)
def firstname = column[String]("firstname")
def lastname = column[String]("lastname")
def email = column[String]("email")
def password = column[String]("password")
def addressId = column[Option[Int]]("address_id")
override def * = (id, firstname, lastname, email, password, addressId) <> ((User.apply _).tupled, User.unapply)
def address_fk= foreignKey("fk_user_address", addressId, addressTable)(_.id)
}
private class AddressDB(tag: Tag) extends Table[Address](tag, "address") {
def id = column[Option[Int]]("id", O.PrimaryKey, O.AutoInc)
def city = column[String]("city")
def state = column[String]("state")
def country = column[String]("country")
def pin = column[Int]("pin")
override def * = (id, city, state, country, pin) <> ((Address.apply _).tupled, Address.unapply)
}
private val userTable = TableQuery[UserDB]
private val addressTable = TableQuery[AddressDB]
Please, tell how can i insert data to both the related tables with one query using slick.
Thank you very much in advance.
You should use id returning insert for address and an insert to usertable in single transaction, example:
val query = for {
adressId <- addressTable returning addressTable.map(_.id) += Address(....)
userTable += User(..., Some(adressId), ....)
} yield ()
db.run(query.transactionally)
edit: Query above not returning anything, if you need created User, use:
val query = for {
adressId <- addressTable returning addressTable.map(_.id) += Address(....)
user <- userTable returning userTable += User(..., Some(adressId), ....)
} yield user
db.run(query.transactionally)

Slick: how to get an object attribute in a result set?

Given the following Scala class enhanced with Slick:
class Users(tag: Tag) extends Table[(Int, String, String)](tag, "users") {
def id: Rep[Int] = column[Int]("sk", O.PrimaryKey)
def firstName: Rep[String] = column[String]("first_name")
def lastName: Rep[String] = column[String]("last_name")
def * : ProvenShape[(Int, String, String)] = (id, firstName, lastName)
}
I need to print the last names in a query loop:
val db = Database.forConfig("dbconfig")
try {
val users: TableQuery[Users] = TableQuery[Users]
val action = users.result
val future = db.run(action)
future onComplete {
case Success(u) => u.foreach { user => println("last name : " + **user.lastName**) }
case Failure(t) => println("An error has occured: " + t.getMessage)
}
} finally db.close
But Scala doesn't recognize user.lastName (I get an error saying that "Scala doesn't recognize the symbol"). How to print the last names ?
The problem is you're using Table[(Int, String, String)]. user in your case is therefore an instance of type (Int, String, String), so it doesn't have a lastName. Use user._3 to get at the tuple's third element (the last name). Even better might be to use a case class instead of a tuple:
case class DBUser(id: Int, firstName: String, lastName: String)
class Users(tag: Tag) extends Table[DBUser](tag, "users") {
def id: Rep[Int] = column[Int]("sk", O.PrimaryKey)
def firstName: Rep[String] = column[String]("first_name")
def lastName: Rep[String] = column[String]("last_name")
def * = (id, firstName, lastName) <> (DBUser.tupled, DBUser.unapply)
}

Using multiple projection in slick for the same table

I have a user table for which I will like to have multiple projections. For example, Can I have something like
class Users(tag: Tag) extends Table [User] (tag, "user") {
def * = (id.?, emailId, firstName, lastName, gender) <> (User.tupled, User.unapply)
def allDetails = (id.?, emailId, firstName, lastName, gender, createdTime, modifiedTime)
...
}
I searched on Google but could not find anything. Can somebody tell me how can I use allDetails?
I will like to do
object Users {
val users = TableQuery[Users]
def getAllDetails = ??? // How can I use allDetails here
}
Had the same need recently, and started to use something like that:
abstract class AnyUserTable[T](tag: Tag) extends Table[T](tag, "user") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def emailId = column[String]("email_id")
def firstName = column[String]("firstname")
def lastName = column[String]("lastname")
def gender = column[String]("gender")
}
class Users(tag: Tag) extends AnyUserTable[User](tag) {
def * = (emailId, firstName, lastName, gender, id.?) <> (User.tupled, User.unapply)
}
class UserDetails(tag: Tag) extends AnyUserTable[UserDetail](tag) {
def createdTime = column[Option[Timestamp]]("created_time")
def modifiedTime = column[Option[Timestamp]]("modified_time")
def * = (emailId, firstName, lastName, gender, createdTime, modifiedTime, id.?) <> (UserDetail.tupled, UserDetail.unapply)
}
object Users {
val users = TableQuery[Users]
val getAllDetails = TableQuery[UserDetails] // That is how I propose to get all the details
}
borrowing the case classes from Sky's answer.
case class User(
emailId: String, firstName: String, lastName: String,
gender: String, id: Option[Int] = None)
case class UserDetail(
emailId: String,
firstName: String, lastName: String, gender: String,
createdTime: Option[Timestamp],
modifiedTime: Option[Timestamp], id: Option[Int] = None)
I guess that is pretty close from what I would be tempted to do using updatable views in straight sql.
Do like this:
class UserTable(tag: Tag) extends Table[UserInfo](tag, "user") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def emailId = column[String]("email_id", O.NotNull)
def firstName = column[String]("firstname")
def lastName = column[String]("lastname")
def gender = column[String]("gender")
def createdTime = column[Option[Timestamp]]("created_time")
def modifiedTime = column[Option[Timestamp]]("modified_time")
def userInfo = (emailId, firstName, lastName, gender, createdTime, modifiedTime, id.?) <> (User.tupled, User.unapply)
def * = (emailId, firstName, lastName, gender, id.?) <> (UserInfo.tupled, UserInfo.unapply)
}
case class User(emailId: String,
firstName: String, lastName: String, gender: String,
createdTime: Option[Timestamp],
modifiedTime: Option[Timestamp], id: Option[Int] = None)
case class UserInfo(emailId: String, firstName: String, lastName: String,
gender: String, id: Option[Int] = None)
for extra projection:
val userTable = TableQuery[UserTable]
def insert(userInfo: UserInfo) (implicit session: Session): Int = {
//return auto incremeted id
userTable returning (userTable.map(_.id)) += userInfo
}
def update(userInfo: UserInfo)(implicit session: Session) = {
userTable.filter(_.id === userInfo.id).update(userInfo)
}
def getUserInfo()(implicit session: Session): List[UserInfo] = {
userTable.list
}

Using a function in an insert statement in Slick

Is it possible to use a user defined function in an insert statement in slick? This is what my tables look like, but I'm getting an error when I try to run it.
case class User(username: String, email: String)
class Users(tag: Tag)
extends Table[User](tag, "users") {
def username = column[String]("username", O.PrimaryKey)
def email = column[String]("email", O.NotNull)
def password = column[String]("password", O.NotNull)
def preferences = column[JsValue]("preferences")
def idx = index("idx_users_email", email, unique = true)
def group = foreignKey("fk_group", username, groups)(_.groupname)
def * = (username, email) <> (User.tupled, User.unapply)
def withPassword = (username, email, password) <>[(User, String), (String, String, String)](
{ case (u: String, e: String, p: String) => (User(u, e), p)},
{ case (r: User, p: String) => Some((r.username, r.email, p))}
)
}
object users extends TableQuery(new Users(_)) {
val crypt = SimpleFunction.binary[String, String, String]("crypt")
val gen_salt = SimpleFunction.binary[String, Option[Int], String]("gen_salt")
def createUser(u: String, e: String, p: String)(implicit session: Session) = {
val user = User(u, e)
val s = this.map {c =>(c.username, c.password, users.crypt(c.password, users.gen_salt(LiteralColumn("bf"), LiteralColumn(7))))}
s insertExpr (u, e, p)
}
}
When I run this code, I get the error
SlickException: : Cannot use node
scala.slick.lifted.SimpleFunction$$anon$2#69ade8b0 crypt, false for
inserting data
I've tried several different ways of trying to get my inserts to work. Including this:
def withPassword = (username, email, users.crypt(password, users.gen_salt(LiteralColumn("bf"), LiteralColumn(7)))) <>[(User, String), (String, String, String)](
{ case (u: String, e: String, p: String) => (User(u, e), p)},
{ case (r: User, p: String) => Some((r.username, r.email, p))}
)
Am I just expecting too much for slick to be able to do what I want to and just break down and use raw SQL?
Not supported, but we have a ticket for it with unclear future: https://github.com/slick/slick/issues/928
Besides, encryption is almost always better handled on the client-level. Database-level encryption has very limited security guarantees, because you are sending the keys to the place where the encrypted data is stored. More info in the above ticket.