Custom mapping to nested case class structure in Slick (more than 22 columns)

As Izongren answered it works fine, but it can be hard to write such code as it's annoying to work with very long tuples... I decided to do it "step by step" and always providing types explicitly to avoid type inference problemes and now it works fine.

case class Patient(
                    id: String = java.util.UUID.randomUUID().toString,
                    companyScopeId: String,
                    assignedToUserId: Option[String] = None,
                    info: PatientInfo
                    ) extends ModelWithId



case class PatientInfo(
                        firstName: Option[String] = None,
                        lastName: Option[String] = None,
                        gender: Option[Gender.Value] = None,
                        alias: Option[String] = None,
                        street: Option[String] = None,
                        city: Option[String] = None,
                        postalCode: Option[String] = None,
                        phone: Option[String] = None,
                        mobilePhone: Option[String] = None,
                        email: Option[String] = None,
                        age: Option[AgeRange.Value] = None,
                        companySeniority: Option[CompanySeniorityRange.Value] = None,
                        employmentContract: Option[EmploymentContract.Value] = None,
                        socialStatus: Option[SocialStatus.Value] = None,
                        jobTitle: Option[String] = None
                        )

class PatientTable(tag: Tag) extends TableWithId[Patient](tag,"PATIENT") {
  override def id = column[String]("id", O.PrimaryKey)
  def companyScopeId = column[String]("company_scope_id", O.NotNull)
  def assignedToUserId = column[Option[String]]("assigned_to_user_id", O.Nullable)

  def firstName = column[Option[String]]("first_name", O.Nullable)
  def lastName = column[Option[String]]("last_name", O.Nullable)
  def gender = column[Option[Gender.Value]]("gender", O.Nullable)
  def alias = column[Option[String]]("alias", O.Nullable)
  def street = column[Option[String]]("street", O.Nullable)
  def city = column[Option[String]]("city", O.Nullable)
  def postalCode = column[Option[String]]("postal_code", O.Nullable)
  def phone = column[Option[String]]("phone", O.Nullable)
  def mobilePhone = column[Option[String]]("mobile_phone", O.Nullable)
  def email = column[Option[String]]("email", O.Nullable)
  def age = column[Option[AgeRange.Value]]("age", O.Nullable)
  def companySeniority = column[Option[CompanySeniorityRange.Value]]("company_seniority", O.Nullable)
  def employmentContract = column[Option[EmploymentContract.Value]]("employment_contract", O.Nullable)
  def socialStatus = column[Option[SocialStatus.Value]]("social_status", O.Nullable)
  def jobTitle = column[Option[String]]("job_title", O.Nullable)
  def role = column[Option[String]]("role", O.Nullable)



  private type PatientInfoTupleType = (Option[String], Option[String], Option[Gender.Value], Option[String], Option[String], Option[String], Option[String], Option[String], Option[String], Option[String], Option[AgeRange.Value], Option[CompanySeniorityRange.Value], Option[EmploymentContract.Value], Option[SocialStatus.Value], Option[String])
  private type PatientTupleType = (String, String, Option[String], PatientInfoTupleType)
  //
  private val patientShapedValue = (id, companyScopeId, assignedToUserId,
    (
      firstName, lastName, gender, alias, street, city, postalCode,
      phone, mobilePhone,email, age, companySeniority, employmentContract, socialStatus, jobTitle
      )
    ).shaped[PatientTupleType]
  //
  private val toModel: PatientTupleType => Patient = { patientTuple =>
    Patient(
      id = patientTuple._1,
      companyScopeId = patientTuple._2,
      assignedToUserId = patientTuple._3,
      info = PatientInfo.tupled.apply(patientTuple._4)
    )
  }
  private val toTuple: Patient => Option[PatientTupleType] = { patient =>
    Some {
      (
        patient.id,
        patient.companyScopeId,
        patient.assignedToUserId,
        (PatientInfo.unapply(patient.info).get)
        )
    }
  }

  def * = patientShapedValue <> (toModel,toTuple)
}

Also, have the same problem with the 22 columns limitation, the test case helps very much. Not sure why the example code not working for you, the following code works fine for me,

case class UserRole(var role: String, var extra: String)
case class UserInfo(var login: String, var password: String, var firstName: String, var lastName: String)

case class User(id: Option[String], var info: UserInfo, var role: UserRole)

class UserTable(tag: Tag) extends Table[User](tag, "USER") {

  def id = column[String]("id", O.PrimaryKey)
  def role = column[String]("role", O.NotNull)
  def extra = column[String]("extra", O.NotNull)
  def login = column[String]("login", O.NotNull)
  def password = column[String]("password", O.NotNull)
  def firstName = column[String]("first_name", O.NotNull)
  def lastName = column[String]("last_name", O.NotNull)

  /** Projection */
  def * = (
    id,
    (login, password, firstName, lastName),
    (role, extra)
  ).shaped <> (

  { case (id, userInfo, userRole) =>
    User(Option[id], UserInfo.tupled.apply(userInfo), UserRole.tupled.apply(userRole))
  },
  { u: User =>
      def f1(p: UserInfo) = UserInfo.unapply(p).get
      def f2(p: UserRole) = UserRole.unapply(p).get
      Some((u.id.get, f1(u.info), f2(u.role)))
  })

  def login_index = index("id_user_login", login, unique = true)
}

Tags:

Scala

Slick