简体   繁体   中英

Scala - Play! 2 - Generic Crud

I'm trying to create a generic crud controller, but I'm falling into a json problem.

I have an implicit read and write for each of my domain objects and since my crud controller is "generic", I'm passing T for it, which causes no implicit converter found for type T.

Here is my TestController

object AddressController extends Controller with CrudController[Address, AddressServiceModule] {

}

and this is my CrudController

trait CrudController[T, K <: GenericServiceModule[T]] extends Controller {

  var service: K = _

  def get(uuid: String) = Action.async {
    val future = service.genericService.getById(UUID.fromString(uuid))
    future.map {
      case Some(t) => Ok(Json.toJson(t))
      case None => NotFound
    }.recover {
      case t =>
        Logger.error("Something went wrong", t)
        InternalServerError(t.getMessage)
    }
  }
}

then I got

No Json serializer found for type T.

As I said, I have a read and write for my Address model

implicit val addressReads = Json.reads[Address]
implicit val addressWrites = Json.writes[Address]

But since converters are verified at compile time, I have no idea how to solve this scenario. No chance to write 10, 20 crud controllers with the same logic.

Any idea?

UPDATE

Just for test, I changed the Ok(Json.toJson(t)) for Ok(t.toString) then I got another problem...

a NullPointer on my service

var service: K = _

My GenericServiceModule is the following:

trait GenericServiceModule[T] {

  def genericService: GenericCommonService[T]

  /**
   * Common Services
   * @tparam T
   */
  trait GenericCommonService[T] {
    def getById(uuid: UUID): Future[Option[T]]
    def deleteById(uuid: UUID): Future[ResultSet]
    def insert(t: T): (UUID, Future[ResultSet])
    def update(uuid: UUID, t: T): (UUID, Future[ResultSet])
  }

}

and the AddressServiceModule which I'm passing to the AddressController is the following:

trait AddressServiceModule extends GenericServiceModule[Address] with CassandraService {

  object genericService extends GenericCommonService[Address] {

    override def getById(uuid: UUID): Future[Option[Address]] = {
      Address.select.where(_.id eqs uuid).one()
    }
}

is there a way to inject anyhow this Service through my CrudController?

What about using some sort of trait for each companion object to require the Reads and Writes ?

trait CrudObject[T] {
    val reads: Reads[T]
    val writes: Writes[T]
}

For example:

object Address extends CrudObject[Address] {
    implicit val reads: Reads[Address] = Json.reads[Address]
    implicit val writes: Writes[Address] = Json.writes[Address]

    // other code ..
}

Then in your generic controller, require references to the companion object (marked by the CrudObject trait) and the GenericServiceModule . This won't quite make the implicit resolution work, but once you have that reference, you can then rely on inheritance to make the Reads and Writes available within the controller.

trait CrudController[T] extends Controller {

  def service: GenericServiceModule[T]

  def companion: CrudObject[T]

  implicit def reads: Reads[T] = companion.reads

  implicit def writes: Reads[T] = companion.writes

  def get(uuid: String) = Action.async {
    val future = service.genericService.getById(UUID.fromString(uuid))
    future.map {
      case Some(t) => Ok(Json.toJson(t))
      case None => NotFound
    }.recover {
      case t =>
        Logger.error("Something went wrong", t)
        InternalServerError(t.getMessage)
    }
  }
}

Then your implementation could look like:

object AddressController extends CrudController[Address] {

   def service = AddressServiceModule

   def companion = Address

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM