Reputation: 39018
I want to include a rich type (case class) as a member of another whilst using SLICK lifted mapping. I'm not sure how.
This is what I have so far. (It follows the cake pattern for multiple DB support).
trait ChallengeComponent { this: Profile with UserComponent with GameComponent =>
import profile.simple._
class Challenges(tag: Tag) extends Table[Challenge](tag, "challenges") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def challengerId = column[String]("challenger_id")
def * = (id, challengerId) <> (constructChallenge, extractChallenge)
def challenger = foreignKey("challenger_fk", challengerId, users)(_.gPlusId)
}
private def constructChallenge(id: Option[Int], challengerId: String) =
Challenge(id, user(challengerId).get)
private def extractChallenge(c: Challenge) = (c.id, c.challenger.id)
...
}
case class Challenge(id: Option[Int], challenger: User)
where the UsersComponent trait is defined as:
trait UserComponent { this: Profile =>
import profile.simple._
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[String]("id", O.PrimaryKey)
def displayName = column[String]("display_name", O.NotNull)
def * = (gPlusId, displayName)
}
val users = TableQuery[Users]
def user(gPlusId: String)(implicit session: Session): Option[User] =
users.filter(_.id === id).firstOption
The problem is that there is no implicit Session
available when trying to construct the User
.
What is a workable pattern for embedding one type in another? Preferably in a lazy fashion, but without polluting the case class with persistence code.
Upvotes: 0
Views: 363
Reputation: 119
You can't do that using the * projection. You want case class that map directly to your tables:
case class User(id: String, displayName: String)
case class Challenge(id: Option[Int], challengerId: String)
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[String]("id", O.PrimaryKey)
def displayName = column[String]("display_name", O.NotNull)
def * = (id, displayName) <> (User.tupled, User.unapply)
}
class Challenges(tag: Tag) extends Table[Challenge](tag, "challenges") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def challengerId = column[String]("challenger_id")
def * = (id, challengerId) <> (Challenge.tupled, Challenge.unapply)
}
Then in your example, what you're really after is a query that joins 2 tables:
def getChallengeAndChallenger(id: Int): Option[(Challenge, User)] = db withSession {
implicit session =>
val q = for {
c <- challenges if c.id === id
u <- users if u.id === c.challengerId
} yield (c, u)
q.firstOption
}
Of course you can then replace (map) the tuple to another case class if you wish.
Upvotes: 2