Play Framework / Зависимая будущая композиция

Я пытаюсь сделать несколько зависимых вызовов Slick/DB, а затем отобразить полученные данные в шаблоне twirl.

def show(slug: String) = Action.async { implicit rs =>

    for {

      f <- fooDAO.findBySlug(slug)  // f is of type Option[foo]
      fid <- f.flatMap(a => a.id.map(b => b)) // fid is of type Long
      b <- barDAO.findByFooId(fid) // b is of type Seq[bar] 

    } yield {

        f.map {
          case Some(f) => Ok(views.html.foobar(f, b))
          case _ => NotFound
        }

      }
  }

Сначала мне нужно получить «ID», чтобы затем иметь возможность запрашивать другие соответствующие данные. Теперь компилятор выдает эту ошибку:

play.sbt.PlayExceptions$CompilationException: Compilation error[type mismatch;
 found   : scala.concurrent.Future[Option[play.api.mvc.Result]]
 required: Option[?]]

Любая помощь будет принята с благодарностью.


person krausfm    schedule 02.06.2015    source источник
comment
Есть несколько вещей, которые я не понимаю в вашем коде. 1. почему f.flatMap(a => a.id.map(b => b)) вместо f.flatMap(_.id); 2. почему в комментариях используются имена переменных, отличные от ссылочного кода; 3. что вы ожидаете от последнего сопоставления прецедентов в блоке yield: знаете ли вы, что альтернатива NotFound никогда не достигается? case f => будет соответствовать каждому значению и свяжет его с переменной с именем f.   -  person pagoda_5b    schedule 03.06.2015
comment
Извините за путаницу с именами переменных. Я исправил это сейчас.   -  person krausfm    schedule 03.06.2015


Ответы (2)


В вашем коде есть фундаментальный недостаток, заключающийся в том, что вы смешиваете в одном и том же понимании Option и Seq.

Ожидается, что for-comprehension будет работать с тем же типом «контейнера», который будет результирующим представлением yield.

например если вы объедините несколько Option, вы получите Option, если вы объедините Seq, вы получите Seq.

В этом случае вы можете решить проблему, преобразовав Option (foo) в Seq (который будет пустым, если foo равен None, и иметь 1 элемент, если нет).

Конечным результатом будет

val results: Seq[(Foo, Bar)] =
  for {
    f <- fooDAO.findBySlug(slug).toSeq  // f is of type Seq[Foo]
    b <- barDAO.findByFooId(f.id) // b is of type Seq[Bar] 
  } yield (f, b)

Но я думаю, это не то, что вам нужно. Я полагаю, вы хотите получить все Bar, связанные с полученным Foo, если таковые имеются, и представить их в своем шаблоне. Если для slug нет Foo, вам нужен NotFound.

Мы можем сделать это так

def show(slug: String) = Action.async { implicit rs =>

  val f = fooDAO.findBySlug(slug)  // f is of type Option[Foo]

  f.fold(
    NotFound,
    foo => Ok(views.html.foobar(foo, barDAO.findByFooId(foo.id))
  )

}

Вы можете сделать это более явным, определив вспомогательный метод

def show(slug: String) = Action.async { implicit rs =>

  def barsOf(f: Foo): Seq[Bar] = barDAO.findByFooId(f.id)

  val f = fooDAO.findBySlug(slug)  // f is of type Option[Foo]

  f.fold(
    NotFound,
    foo => Ok(views.html.foobar(foo, barsOf(foo))
  )

}
person pagoda_5b    schedule 03.06.2015

Немного сложно понять, чего вы пытаетесь достичь здесь, но если все это основано на том, что findbySlug возвращает Future[Option[Foo]], а конечным результатом является NotFound, если Option является None, то ваш yield, вероятно, должен быть просто:

   ...
 } yield {
  f.fold(NotFound)(foo => Ok(views.html.foobar(foo, b)))
}

Option[T] — это фантастический тип для извлечения данных и управления потоком, но сопоставление с образцом это почти никогда не бывает правильным подходом. Использование fold кажется лаконичным для этой задачи.

person millhouse    schedule 03.06.2015