Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I omit case class fields in a slick table mapping?

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)
like image 625
user5504273 Avatar asked Oct 29 '15 18:10

user5504273


1 Answers

Your trouble here is your constructUser is function of 4 parameters, while it supposed to be function of single Tuple4

Try to add type signatures.
This sample works for instance ( some simplifications included )

type Data = (Long, String, String, Option[String])

def constructUser: Data => User = {
  case (id, username, passwordHash, email) => User(id, username, passwordHash, email)

}
def extractUser: PartialFunction[User, Data] = {
  case User(id, username, passwordHash, email, _, _) =>
    (id, username, passwordHash, email)
}

def * = (id, username, passwordHash, email) <> (constructUser, extractUser.lift)
like image 105
Odomontois Avatar answered Sep 19 '22 23:09

Odomontois