2016-06-12 5 views
0

Я использую play-slick для своей scala Play! API фиктивного отдыха.Scala Play Будущая взаимозависимость

Итак, мне нужно получить записи из нескольких таблиц. Но, они взаимозависимы, то есть

Table_1 Table_2 
id1  id2 
id2 

Чтобы извлечь запись из Table_2 я должен принести запись из Table_1, а затем с помощью id2 выборки из Table_2.

Мой контроллер:

def getEntity(id : Long) = Action.async { 
    table1DAO.findById(id) map { t1 => 
    t1 map { t1Entity => 
     table2DAO.findById(t1Entity.id2) map { t2 => 
     t2 map { t2Entity => 
      Ok(Json.toJson(CombiningClass(t1Entity, t2Entity))) 
     } getOrElse { Ok(Json.toJson(t1Entity)) } 
     } 
    } getOrElse { NoContent } 
    } 
} 

После компиляции, я получаю:

[error] DummyController.scala:17: overloaded method value async with alternatives: 
[error] [A](bodyParser: play.api.mvc.BodyParser[A])(block: play.api.mvc.Request[A] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[A] <and> 
[error] (block: play.api.mvc.Request[play.api.mvc.AnyContent] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent] <and> 
[error] (block: => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent] 
[error] cannot be applied to (scala.concurrent.Future[Object]) 
[error] def getEntity(id : Long) = Action.async { 
[error]        ^
[error] one error found 

Вот мой метод DAO:

def findById(id : Long): Future[Option[A]] = { 
    db.run(tableQ.filter(_.id === id).result.headOption) 
} 

PS: Я очень новой для функциональной парадигмы и scala, так что если вы можете, простите мое невежество.

+0

Попробуйте аннотировать верхний 'map':' table1DAO.findById (id) .map [Result] {t1 => ...} ', чтобы убедиться, что вы даете' Future [Result] '. Постскриптум Читаемость вашего кода принесет пользу из-за понимания. – cchantep

ответ

1

Чтобы просто решить вашу проблему:

def getEntity(id : Long) = Action.async { 
    findById(id) flatMap { 
    case Some(t1Entity) => 
     findById(t1Entity.id2) map { t2Opt => 
     t2Opt map { t2Entity => 
      Ok(Json.toJson(t1Entity, t2Entity)) 
     } getOrElse { Ok(Json.toJson(t1Entity)) } 
     } 
    case None => Future.successful(NoContent) 
    } 
} 

Проблема здесь состоит в том, что вы не можете flatMapOption и Future вместе в Скале. Here - это фантастическая и простая статья, посвященная этой теме (с обычным исполнением монады FutureO в качестве решения). Короче говоря, я бы использовал библиотеку cats (или даже библиотеку скалаза) и функцию OptionT. Я немного упростил ваш код.

def getEntity(id : Long) = Action.async { 
    (for { 
    t1 <- daoFindById(id) 
    t2 <- daoFindById(t1.id2) 
    } yield (t1, t2)).map{ 
    result => Ok(Json.toJson(result)) 
    }.getOrElse(NoContent) 
} 

case class T(id2: Long) 
def daoFindById(id : Long): OptionT[Future, T] = { 
    OptionT[Future, T](db.run(tableQ.filter(_.id === id).result.headOption)) 
} 

Теперь вы можете легко flatMap над этим OptionT монада, и не волнует, если вы имеете дело с Option или Future (для понимания в Скале лишь синтаксический сахар).

+0

Это хороший ответ, хотя, возможно, немного продвинутый для тех, кто начал работу с Scala. Это не даст вам первого объекта, если второй объект не может быть найден. У кошек также есть [учебник о 'OptionT'] (http://typelevel.org/cats/tut/optiont.html). –

+1

Ваше обновление решает оба моих комментария. Это немного более дружелюбный к новичкам и вернет результат, ожидаемый OP, так хорошая работа! –

+1

Да, не вся бизнес-логика сохраняется (я хотел только продемонстрировать полезность библиотеки кошек). Я добавил простое решение, написанное без помощи внешних библиотек. – liosedhel