Closed sedovalx closed 9 years ago
@v1pka давай все же посмотрим в сторону generic подхода к фильтрации с использованием рефлексии?
Можно наверное просто генерировать sql-запрос как тут http://slick.typesafe.com/doc/2.1.0/sql.html
Типа
val q = Q.queryNA[Account]("select * from account where ?")
val results = q("сгенерированное на основе json условие")
Это уже представляется гораздо более простым чем рефлексия.
А вот так я сегодня упоролся, если интересно:
import java.sql.Timestamp
import models.generated.Tables
import models.generated.Tables.AccountTable
import play.api.libs.json._
import reflect.runtime.universe._
import scala.slick.lifted.Column
import play.api.db.slick.Config.driver.simple._
case class PropertyInfo(name: String, tpe: reflect.runtime.universe.Type, isOption: Boolean)
val tableQuery = models.generated.Tables.AccountTable
val filter = Json.obj(
"login" -> "admin"
)
// все столбцы таблицы и их типы
val properties = typeOf[models.generated.Tables.AccountTable].members.view
.filter { p => !p.isMethod && p.typeSignature.baseClasses.count { c => c.name.toString == "Column" } > 0}
.map { i =>
val propertyName = i.name.toString
var propertyType = (i.typeSignature match { case TypeRef(_, _, args) => args }).head
val isOption = propertyType.baseClasses.count { c => c.name.toString == "Option" } > 0
if (isOption)
propertyType = (propertyType match { case TypeRef(_, _, args) => args }).head
PropertyInfo(propertyName.trim, propertyType, isOption)
}.toList
// для каждого столбца смотрим, есть ли соответствие по имени в json
val criterias = properties.map { p =>
val filterValue = filter \ p.name match {
case x: JsString => Some(x)
case _ => p.name match {
case x: String if x.endsWith("Id") => filter \ x.substring(0, x.length - 2) match {
case x: JsString => Some(x)
case _ => None
}
case _ => None
}
}
filterValue match {
case None => None// ничего не делаем
case Some(x) =>
print(p.name + ": " + x + " of type ")
// если соответствие есть, то для каждого
// используемого в модели примитивного типа столбца
// генерируем функцию, которую потом
// можно будет передать в tableQuery.filter(...)
p.tpe match {
// case t if t <:< typeTag[Int].tpe =>
// println("Int")
case t if t <:< typeTag[String].tpe =>
println("String")
val realX = x.toString()
Some((row: Tables.AccountTable) => {
val m = runtimeMirror(row.getClass.getClassLoader)
val propTermSymb = typeOf[AccountTable].decl(TermName(p.name)).asTerm
val im = m.reflect(row)
val propFieldMirror = im.reflectField(propTermSymb)
val rowPropValue: Any = propFieldMirror.get
if (p.isOption){
val col = rowPropValue.asInstanceOf[Column[Option[String]]]
col === realX
} else {
val col = rowPropValue.asInstanceOf[Column[String]]
col === realX
}
})
// case t if t <:< typeTag[BigDecimal].tpe =>
// println("BigDecimal")
// case t if t <:< typeTag[Timestamp].tpe =>
// println("Timestamp")
// case t if t <:< typeTag[models.entities.Role.Role].tpe =>
// println("Role")
case t =>
println(t)
None
}
}
} filter { _.isDefined } map { _.get }
val a = tableQuery.filter(criterias.head).run
{ "car": "1" }
для типа RentTable, то в типе RentTable ищем столбец rent
, а если не найден, то rentId
.Для пользователей базовый функционал фильтрации работает.
У контроллеров доменных объектов есть метод read, который возвращает список объектов данного типа. Сейчас он не принимает никаких параметров. Нужна возможность задавать параметры фильтрации, которые будут приходить в виде queryParams GET-запроса. Фильтр может прийти на любое поле доменного типа, причем обязательных значений в фильтре нет. Например, для типа Пользователь:
В будущем возможно появится необходимость в скоупах параметров GET-запросов, если кроме параметров фильтрации появятся другие. Гарантируется соответствие имен параметров фильтрации именам свойств запрашиваемого доменного типа. Т.е. можно попробовать сделать generic-алгоритм, если в Scala можно получить значение свойства по его строковому имени.