Search code examples
scalalifttraits

How do I make a trait to mix in with an object that extends MappedLongForeignKey, that will override def asHtml and def validSelectValues?


I have defined my model as follows:

object Curr extends Curr with LongKeyedMetaMapper[Curr] with CRUDify[Long, Curr] {

}
class Curr extends LongKeyedMapper[Curr] with IdPK with CreatedUpdated {
    def getSingleton = Curr 
    object code extends MappedString(this, 100)
    object name extends MappedString(this, 100)
}

object Country extends Country with LongKeyedMetaMapper[Country] with CRUDify[Long, Country] {
}
class Country extends LongKeyedMapper[Country] with IdPK with CreatedUpdated {
    def getSingleton = Country 
    object name extends MappedString(this, 100)
    object currid extends MappedLongForeignKey(this, Curr) {
       override def asHtml = { 
           <span>{Curr.find(By(Curr.id, this)).map(c => (c.name + " " + c.code)).openOr(Text(""))}</span> 
       } 
       override def validSelectValues: Box[List[(Long, String)]] = 
        Full(Curr.findAll(OrderBy(Curr.name, Ascending)).map(c => (c.id.is, c.code.is))) 
    }
}

I will have many such models, and I want to remove the redundancy of defining asHtml and validSelectValues for the many models that will have foreign keys. I figured I could do this with a trait MyField that would mix in to my model as follows:

object currid extends {val MyModel = Curr } MappedLongForeignKey(this, Curr) with MyField[Curr] {

with the trait being defined something like:

trait MyField[T <: LongKeyedMetaMapper[T] with IdPK] {
  val MyModel: T
  override def asHtml = { 
    <span>{MyModel.find(By(MyModel.id, this)).map(c => (c.name + " " + c.name)).openOr(Text(""))}</span> 
  } 
  override def validSelectValues: Box[List[(Long, String)]] = 
    Full(MyModel.findAll(OrderBy(MyModel.name, Ascending)).map(c => (c.id.is, c.name.is))) 
}

My trait, as written above, does not work. Here is the error that the compiler generates:

No implicit view available from net.liftweb.mapper.MyField[T] => Long.
[error]     <span>{MyModel.find(By(MyModel.id, this)).map(c => (c.name + " " + c.name)).openOr(Text(""))}</span> 
[error]                           ^
value name is not a member of type parameter T
[error]     Full(MyModel.findAll(OrderBy(MyModel.name, Ascending)).map(c => (c.id.is, c.name.is))) 
[error]                                          ^

I will make sure that each MyModel will have a name member. Can anyone advise on how to implement this trait?

Thanks!


Solution

  • I have found a solution. I created my own KeyedMapper, MetaMapper and MappedForeignKey. In the example MyData and MyForeignData are two database tables and MyData contains a foreign key to MyForeignData. CRUDify is included and in the browser you can see and edit the foreign key, displayed with the table column override def primaryKeyDisplayField = table_column_field

    import scala.xml._
    import net.liftweb.common._
    import net.liftweb.mapper._
    import net.liftweb.util._
    
    trait MyMapper[OwnerType <: MyMapper[OwnerType]]
        extends LongKeyedMapper[OwnerType] with IdPK {
      self: OwnerType =>
    }
    
    trait MyMetaMapper[A <: MyMapper[A]]
        extends LongKeyedMetaMapper[A]
        with LongCRUDify[A] {
      self: A =>
      def primaryKeyDisplayField: BaseOwnedMappedField[A] = null
    }
    
    abstract class MyMappedForeignKey[T<:MyMapper[T], O<:MyMapper[O]](theOwner: T, _foreignMeta: => MyMetaMapper[O])
        extends MappedLongForeignKey[T, O](theOwner, _foreignMeta) {
      override def foreignMeta = _foreignMeta
      override def dbIndexed_? = true
      override def asHtml = {
        <span>{foreignMeta.findByKey(this.get)
          .map(_.fieldByName(foreignMeta.primaryKeyDisplayField.name).openOr(Text("Error"))).openOr(Text("ERROR MyMappedForeignKey"))}</span>
      }
      override def validSelectValues/*: Box[List[(Long, String)]]*/ = {
        Full(foreignMeta.findAll(/*OrderBy(foreignMeta.primaryKeyField, Ascending)*/)
            .map(i => (i.id.get,
                i.fieldByName(foreignMeta.primaryKeyDisplayField.name).openOr(Text("ERROR")).toString())))
      }
    }
    
    class MyData extends MyMapper[MyData] {
      def getSingleton = MyData
      object myForeignData extends MyMappedForeignKey(this, MyForeignData)
      object name extends MappedString(this, 30)
    }
    
    object MyData extends MyData with MyMetaMapper[MyData] { }
    
    class MyForeignData extends MyMapper[MyForeignData] {
      def getSingleton = MyForeignData
      object name extends MappedString(this, 30)
    }
    
    object MyForeignData extends MyForeignData with MyMetaMapper[MyForeignData] {
      override def primaryKeyDisplayField = name
    }