0

I'm using the Play Framework and Squeryl to make a fairly basic front end for a database, but I know I'm rewriting too much code. I have different models to represent data in my db, and they all do the same six functions

object ModelType{
    def add(model:ModelType):Option[ModelType] = Option(AppDB.tablename.insert(model))
    def remove(id: Long) = AppDB.tablename.delete(id)
    def getAll():List[ModelType] = from(AppDB.tablename)(model => select(model) orderBy(model.aDifferentFieldForEachModel)) toList
    def toJson(model:ModelType):JsValue ={
      Json.toJson(
      Map("field" -> Json.toJson(model.field))
      )
    }
    def allToJson() = {
      val json:List[JsValue] = getAll.map{toJson(_)}
      Json.toJson(json.toSeq)
    }
    def validate(different values for each model) = // is fairly different for each one. Validates the submitted fields from a user
}

So I'm using case classes for each of the models, and using an accompanying object for these commands. How can I use generics or traits in scala to make my life easier and not type all of these methods out every time?

EDIT: Mostly solved with gzm0's answer, but the problem is now how would I implement getAll in the trait? I want to be able to save some variable for each model that resembles the model.aDifferentFieldForEachModel as above.

damian
  • 1,419
  • 1
  • 22
  • 41

1 Answers1

2

You could try the following:

trait ModelOps[T] {
  def table: AppDB.Table // not sure about type
  def order: AppDB.OrderByPredicate // not sure about type
  def toJson(model: T): JsValue

  def add(model: T): Option[T] = Option(AppDB.categories.insert(model))
  def remove(id: Long) = AppDB.categories.delete(id)
  def getAll(): List[T] = from(table)(model => select(model) orderBy(order)) toList
  def allToJson() = {
    val json:List[JsValue] = getAll.map{toJson(_)}
    Json.toJson(json.toSeq)
  }

}

Then you can for each model type:

object ModelType extends ModelOps[ModelType] {
  def table = AppDB.examples
  def order = yourPredicate
  def toJson(model:ModelType):JsValue = {
    Json.toJson(Map("field" -> Json.toJson(model.field)))
  }
  def validate(different values for each model) = // is fairly different for each one. Validates the submitted fields from a user
}

UPDATE About the true type of AppDB.OrderByPredicate:

Calling select on PrimitiveTypeMode returns a SelectState. On this SelectState, you will call orderBy which takes a List[BaseQueryYield#O] (or multiple of those in the same argument list). Hence you should define:

def order(model: T): List[BaseQueryYield#O]

and

def getAll() = from(table)(model => select(model) orderBy(order(model))) toList

By the way, BaseQueryYield#O resolves to ExpressionNode.

gzm0
  • 14,752
  • 1
  • 36
  • 64
  • So I have two problems, 1: The more important is that each of these case classes extend KeyedEntitiy[Long] and I'm not sure how to make ModelType extend ModelOps[T:KeyedEntity[Long]], it doesn't like that syntax. And 2: orderBy I guess does NOT take a predicate and I can't find in the api exactly what it takes but it's something along the line of `...)(model => select(model) orderBy(model.id))` and I have no idea how to put that into a generic context – damian Jun 11 '13 at 19:30
  • And 1 is fixed with this thread: http://stackoverflow.com/questions/8576229/scala-by-example-trait-type-parameter-with-context-bounds-mistake – damian Jun 11 '13 at 19:36
  • @kingdamian42 Updated post with (anticipated) type of order by predicate. – gzm0 Jun 11 '13 at 20:16
  • Ended up just making a mySort method and calling it in the getAll method on the list. `def mySort(catList:List[Category]) = catList.sortBy(c => c.name)` for example. – damian Jun 12 '13 at 13:07
  • @kingdamian42 I do not advise you to do that. If you sort like this, the sorting is done on the application side, which means it cannot take advantage of indexes made in the database. (i.e. it will likely be slower for bigger datasets). – gzm0 Jun 12 '13 at 15:28
  • it's a fairly small db and I just couldn't figure out how to do it with orderBy. I'd be happy to change it though. – damian Jun 12 '13 at 15:53