I'm teaching myself some Scala and am currently getting my feet wet with slick (3.1) + play framework, so maybe the answer is simple here and I'm missing something obvious. I have the following model and Table
case class User(id: Long = -1,
username: String,
passwordHash: String,
email: Option[String] = None)
class Users(tag: Tag) extends Table[User](tag, "USERS") {
def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
def username = column[String]("USERNAME")
def email = column[Option[String]]("EMAIL")
def passwordHash = column[String]("PASSWD_HASH")
def * = (id, username, passwordHash, email) <>((User.apply _).tupled, User.unapply)
}
Now this above works just fine as it is, but I'd like to add some fields to the User
case class that aren't saved in the USER table, namely permissions and roles, like this:
case class User(id: Long = -1,
username: String,
passwordHash: String,
email: Option[String] = None,
permissions: Seq[String] = Seq.empty,
roles: Seq[String] = Seq.empty)
Those are supposed to go into their own respective tables as userid -> permission/role mappings (simple one to many relationships).
Ultimately those should be queried too, but for now I'd just like to ignore the additional fields (purely as an exercise). How do I adjust the original projection in the table to omit/ignore those new fields? Obviously the original mapping
def * = (id, username, passwordHash, email) <>((User.apply _).tupled, User.unapply)
doesn't work anymore since the touple doesnt match the case class. As far as I can tell it shouldnt be too hard since <>
just takes two functions that convert from a touple to a User instance and vice versa and those functions should just ignore the new fields (or fill them with their default values). But I can't figure out how to express that.
I tried adding a new apply()
with a shorter signature to the User
companion object, but then I get an error that basically tells me that slick doesnt know which apply()
to use. Makes sense, but I dont know how to reference one or the other. I did the same thing with an additional constructor for User
, the result is the same problem. And I tried writing basic conversion functions like this:
class Users(tag: Tag) extends Table[User](tag, "USERS") {
def constructUser = (id: Long, username: String, passwordHash: String, email: Option[String]) =>
User(id, username, passwordHash, email)
def extractUser = (user: User) => user match {
case User(id, username, passwordHash, email, permissions, roles) =>
Some((id, username, passwordHash, email))
}
def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
def username = column[String]("USERNAME")
def email = column[Option[String]]("EMAIL")
def passwordHash = column[String]("PASSWD_HASH")
def * = (id, username, passwordHash, email) <>(constructUser, extractUser)
}
Sadly that also leads to an error:
[error] found : (Long, String, String, Option[String]) => models.User
[error] required: ? => ?
[error] def * = (id, username, passwordHash, email) <>(constructUser, deconstructUser)