Имея приложение с Room и Compose, я работаю над компоновкой, которой нужно получить данные из базы данных для ее отрисовки. Это список избранных рейсов, и избранные содержат только идентификаторы (iata) аэропортов отправления и назначения. У моего компонуемого объекта есть любимый вариант, но ему нужно сделать выборку из таблицы аэропортов, чтобы восстановить все данные для каждого аэропорта и иметь возможность их раскрасить. Это список, поэтому его нужно сделать для большого количества элементов.
Какой подход правильный? Я думал, что создаю функцию для viewmodel, которая возвращает результат запроса, но потом я заметил, что она должна возвращать Flow, а не экземпляр объекта, кроме того, в составной части нет viewmodel, поэтому я не могу вызывать функции модели представления. Какой лучший способ решить эту проблему?
Сущности базы данных:
@Entity
data class Airport(
@PrimaryKey
val id: Int,
@ColumnInfo(name = "iata_code")
val iataCode: String,
val name: String,
val passengers: Int
)
@Entity
data class Favorite(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "departure_code")
val departureCode: String,
@ColumnInfo(name = "destination_code")
val destinationCode: String
)
Составное:
LazyColumn(
modifier = Modifier.padding(8.dp)
) {
items(uiState.favorites) { favorite ->
//TODO here i need to get two airports using the ids (iata), so I need to query room, once I have them, I can use them to paint the details of both airports of the favorite flight.
}
}
Функция viewmodel для восстановления аэропорта по iata:
fun getAirportByIata(iata: String): Flow<Airport> {
return flightRepository.getAirport(iata)
}
Пожалуйста, ознакомьтесь с документацией о Взаимоотношениях комнат.
Вместо того, чтобы сначала извлекать избранные, а затем во второй операции с базой данных получать принадлежащие им аэропорты, вы можете создать новый класс данных, например FavoritesAndAirports, и позволить Room автоматически объединить две таблицы для вас. Тогда у вас уже будут все необходимые данные в одном месте:
items(uiState.favoritesAndAirports) { favoriteAndAirports ->
// access favorites with their airports
}
В вашем случае вы можете смоделировать два аэропорта, принадлежащие одному фавориту, используя два отношения «один к одному». Создайте такой класс данных:
data class FavoriteAndAirports(
@Embedded val favorite: Favorite,
@Relation(
parentColumn = "departure_code",
entityColumn = "iata_code"
)
val departureAirport: Airport
@Relation(
parentColumn = "destination_code",
entityColumn = "iata_code"
)
val destinationAirport: Airport
)
Я предполагаю, что departure_code и destination_code указывают на iata_code сущности Airport.
Затем вы можете получить к нему доступ в своем DAO следующим образом:
@Transaction
@Query("SELECT * FROM Favorite")
fun getFavorites(): Flow<List<FavoriteAndAirports>>
Теперь Room загрузит все Favorite, получит по два Airport для каждого Favorite и поместит все данные в экземпляры FavoriteAndAirports.
К этому потоку вы сможете получить доступ из вашей ViewModel:
class FlightsVM(
private val flightRepository: FlightRepository
) : ViewModel() {
val favorites = flightRepository
.getFavorites()
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
}
Наконец, используйте его в своем Composable:
@Composable
fun FavoritesComposable(flightViewModel: FlightVM = viewModel()) {
val favorites = flightViewModel.favorites.collectAsStateWithLifecycle()
LazyColumn(
//...
)
}
Я надеюсь, что этого достаточно, чтобы вы могли начать, в противном случае, пожалуйста, взгляните повнимательнее на документацию, на которую я ссылаюсь, там есть полезная информация.
Привет, спасибо, мне кажется это немного сложно. Что вы думаете о создании потока со списком нового класса данных, содержащего по два аэропорта каждого фаворита, и все это на модели представления? под капотом та же работа? каждый раз, когда избранное изменяется (например, вставляется новое), будет ли вся работа по созданию этого списка выполняться даже без отображения этого компонента с избранным? Та же проблема с вашим решением?
Думаю не стоит вручную собирать Избранное с Аэропортами. При подходе, описанном в моем ответе, Room все сделает за вас. Он сам получит данные аэропорта для каждого избранного. Я использовал этот подход несколько раз без каких-либо проблем с производительностью. Как также показано в документации, это рекомендуемый способ реализации.
ну наконец-то я понял ваше решение и приму его, предполагая, что это лучше, чем создание потока, который вручную выполняет работу, спасибо
Вы можете преобразовать данные вашего аэропорта в
StateFlowв своей ViewModel. Затем вызовитеStateFlowиз ViewModel и преобразуйте его вStateв вашем Composable.