Search code examples
scalaslickslick-2.0play-slick

How to create projection class for complex case class in slick?


For example I have this case class:

case class User(
  var identityId: IdentityId, //Its a user created class
  var firstName: String,
  var lastName: String,
  var fullName: String,
  var email: Option[String],
  var avatarUrl: Option[String],
  var authMethod: AuthenticationMethod,
  var oAuth1Info: Option[OAuth1Info] = None,
  var oAuth2Info: Option[OAuth2Info] = None,
  var passwordInfo: Option[PasswordInfo] = None) extends Identity {

  def this() = this(null, "", "", "", None, None, null, None, None, None)

}

Its actually a securesocial identity, now identityId is an object of case class:

case class IdentityId(
var userId:String,
var providerId:String
)

So how do I create a projection class for situations like, this?? If I had to create a projection class datatypes like String,Int it would have been no problem but what about user defined objects and classes??


Solution

  • I did some research and found some very useful answers here Link1 and Link2

    The second link didn't have implementations for PasswordInfo so, here I have provided my own implementation, though this approach seems a bit more code, can anyone suggest a more efficient approach that is if there is one:

    *Note: And the second link was very helpful, its actually whole working project example Based on Play, Slick and SecureSocial thanks to Lunatech and others.

    class Users(tag: Tag) extends Table[User](tag, "user") {
    
      implicit def string2AuthenticationMethod = MappedColumnType.base[AuthenticationMethod, String](
        authenticationMethod => authenticationMethod.method,
        string => AuthenticationMethod(string))
    
      implicit def tuple2OAuth1Info(tuple: (Option[String], Option[String])): Option[OAuth1Info] = tuple match {
        case (Some(token), Some(secret)) => Some(OAuth1Info(token, secret))
        case _ => None
      }
    
      implicit def tuple2OAuth2Info(tuple: (Option[String], Option[String], Option[Int], Option[String])): Option[OAuth2Info] = tuple match {
        case (Some(token), tokenType, expiresIn, refreshToken) => Some(OAuth2Info(token, tokenType, expiresIn, refreshToken))
        case _ => None
      }
    
      implicit def tuple2PasswordInfo(tuple: (Option[String], Option[String], Option[String])) = tuple match {
        case (Some(hasher), Some(password), salt) =>
          Some(PasswordInfo(hasher, password, salt))
        case _ => None
      }
    
      implicit def tuple2IdentityId(tuple: (String, String)): IdentityId = tuple match {
        case (userId, providerId) => IdentityId(userId, providerId)
      }
    
      def uid = column[Long]("id", O.PrimaryKey, O.AutoInc)
      def userId = column[String]("userId")
      def providerId = column[String]("providerId")
      def email = column[Option[String]]("email")
      def firstName = column[String]("firstName")
      def lastName = column[String]("lastName")
      def fullName = column[String]("fullName")
      def authMethod = column[AuthenticationMethod]("authMethod")
      def avatarUrl = column[Option[String]]("avatarUrl")
      // oAuth 1
      def token = column[Option[String]]("token")
      def secret = column[Option[String]]("secret")
      // oAuth 2
      def accessToken = column[Option[String]]("accessToken")
      def tokenType = column[Option[String]]("tokenType")
      def expiresIn = column[Option[Int]]("expiresIn")
      def refreshToken = column[Option[String]]("refreshToken")
      //PasswordInfo
      def hasher = column[Option[String]]("hasher")
      def password = column[Option[String]]("password")
      def salt = column[Option[String]]("salt")
    
      def * : ProvenShape[User] = {
        val shapedValue = (
          userId,
          providerId,
          firstName,
          lastName,
          fullName,
          email,
          avatarUrl,
          authMethod,
          token,
          secret,
          accessToken,
          tokenType,
          expiresIn,
          refreshToken,
          hasher,
          password,
          salt).shaped
        shapedValue.<>({
          tuple =>
            User.apply(
              identityId = tuple2IdentityId(tuple._1, tuple._2),
              firstName = tuple._3,
              lastName = tuple._4,
              fullName = tuple._5,
              email = tuple._6,
              avatarUrl = tuple._7,
              authMethod = tuple._8,
              oAuth1Info = (tuple._9, tuple._10),
              oAuth2Info = (tuple._11, tuple._12, tuple._13, tuple._14),
              passwordInfo = (tuple._15, tuple._16, tuple._17))
        }, {
          (u: User) =>
            Some {
              (u.identityId.userId,
                u.identityId.providerId,
                u.firstName,
                u.lastName,
                u.fullName,
                u.email,
                u.avatarUrl,
                u.authMethod,
                u.oAuth1Info.map(_.token),
                u.oAuth1Info.map(_.secret),
                u.oAuth2Info.map(_.accessToken),
                u.oAuth2Info.flatMap(_.tokenType),
                u.oAuth2Info.flatMap(_.expiresIn),
                u.oAuth2Info.flatMap(_.refreshToken),
                u.passwordInfo.map(_.hasher),
                u.passwordInfo.map(_.password),
                u.passwordInfo.flatMap(_.salt))
            }
        })
      }
    }