Can't see how to do this and getting rather confused!
I am saving 'site' objects to firestore, but I want to add a list of users associated to each site.
I have added a Map of users to my JSON object as below:
#IgnoreExtraProperties
data class SiteObject(
var siteReference: String,
var siteAddress: String,
var sitePhoneNumber: String,
var siteEmail: String,
var invoiceAddress: String,
var invoicePhoneNumber: String,
var invoiceEmail: String,
var website: String,
var companyNumber: String,
var vatNumber: String,
var recentProjectsText: String,
//not set up yet:
var sitePriority: Boolean,
var siteRating: Int,
var plusCode: String,
var users: Map<String, Boolean>?, // This is the map I have added
#ServerTimestamp
var dateCreatedTimestamp: Date?,
#ServerTimestamp
var dateEditedTimestamp: Date?,
#Exclude
var siteID: String?
) : Serializable {
private constructor() : this(
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
false,
1,
"",
null,
null,
null,
null
)
override fun toString(): String {
return "$siteReference"
}
}
And in my respository I am trying to add the current user to this list of users as below:
// save sites to firebase
fun saveSite(site: SiteObject) {
site.users?.plus(Pair(firebaseUser?.uid.toString(), true)) // This is where I expected the user Id to be added to Map of users..
val documentReference = firestore.collection("sites").document().set(site)
.addOnCompleteListener {
if(it.isSuccessful){
Log.d(TAG, "${site.toString()} saved")
lastOperationText.value = "New site, ${site.siteReference}, saved!"
} else {
Log.d(TAG, "${site.toString()} saved")
lastOperationText.value = "Save new site failed"
}
}
}
However, I still seeing null for users in the Firestore console.
Your code never gives an initial value to users. It starts off null. Since it doesn't get assigned a value, this code will not make a change to it, because it's first checking to see if users is null using the ?. operator:
site.users?.plus(Pair(firebaseUser?.uid.toString(), true))
You will need to assign it an initial value before trying to modify it. It should probably never be null and just start empty.
var users = HashMap<String, Boolean>()
For completeness, below is my updated data class. This initialises the values when it is created and also includes #Exclude #set:Exclude #get:Exclude on siteID to prevent this being saved to Firestore (used to store generated id when read from Firestore):
#IgnoreExtraProperties
data class SiteObject(
var siteReference: String = "",
var siteAddress: String = "",
var sitePhoneNumber: String = "",
var siteEmail: String = "",
var invoiceAddress: String = "",
var invoicePhoneNumber: String = "",
var invoiceEmail: String = "",
var website: String = "",
var companyNumber: String = "",
var vatNumber: String = "",
var recentProjectsText: String = "",
var sitePriority: Boolean = false,
var siteRating: Int = 1,
var plusCode: String = "",
var users: HashMap<String, Boolean> = hashMapOf(),
#ServerTimestamp
var dateCreatedTimestamp: Date? = null,
#ServerTimestamp
var dateEditedTimestamp: Date? = null,
#Exclude #set:Exclude #get:Exclude
var siteID: String = ""
) : Serializable {
override fun toString(): String {
return "$siteReference, $siteAddress, $siteRating, $siteID"
}
fun siteDetailsText(): String {
var siteDetailsText = siteAddress
if (sitePhoneNumber.isNotEmpty()) siteDetailsText.plus("\n\n$sitePhoneNumber")
if (website.isNotEmpty()) siteDetailsText.plus("\n\n$website")
return siteDetailsText
}
fun invoiceDetailsText(): String {
var invoiceDetailsText = invoiceAddress
if (invoicePhoneNumber.isNotEmpty()) invoiceDetailsText.plus("\n\n$invoicePhoneNumber")
if (companyNumber.isNotEmpty()) invoiceDetailsText.plus("\n\n$companyNumber")
if (vatNumber.isNotEmpty()) invoiceDetailsText.plus("\n\n$vatNumber")
return invoiceDetailsText
}
}
Related
I'm trying to pass a map from Firestore into a data class but I'm getting null instead of the data
This is how my data is accessed:
firestoreDb = FirebaseFirestore.getInstance()
val postsRef = firestoreDb
.collection("posts")
.limit(20)
.orderBy("creation_time_ms", Query.Direction.DESCENDING)
.orderBy("date", Query.Direction.DESCENDING)
postsRef.addSnapshotListener { snapshot, exception ->
if (exception != null || snapshot == null) {
Log.e(TAG,"Exception when querying post", exception )
return#addSnapshotListener
}
val postList = snapshot.toObjects(HousePost::class.java)
for (post in postList) {
Log.i(TAG, "Posts $post")
}
}
My model for the data
data class HousePost (
#get:PropertyName("image_url") #set:PropertyName("image_url") var postImage: String = "",
var rent: String = "",
var description: String = "",
#get:PropertyName("creation_time_ms") #set:PropertyName("creation_time_ms") var creationTimeMs: Long = 0L,
var location: String = "",
#get:PropertyName("post_id") #set:PropertyName("post_id")var postId: String? = "",
var rooms: String = "",
var caption: String = "",
var date: String = "",
var owner: Owner? = null
)
My Owner model class
class Owner(
var uid: String = "",
var username: String = "",
var email: String = "",
#get:PropertyName("profile_image") #set:PropertyName("profile_image") var profileImage: String = "",
#get:PropertyName("user_type") #set:PropertyName("user_type")var usertype: String = "owner"
)
Here is my firestore data I know some fields are an empty string but that shouldn't output null.
This is the result from logcat
2022-05-03 11:42:12.313 6581-6581/com.example.moveapplication I/MainActivity: Posts HousePost(postImage=https://firebasestorage.googleapis.com/v0/b/moveapplicationdb.appspot.com/o/Post%20Pictures%2Fian-dooley-_-JR5TxKNSo-unsplash.jpg?alt=media&token=6721ec57-7602-41ee-b7cd-b8b1838b27fc, rent=15000, description=Along Ngong road we have a studio room for you to rent. Located in a moder =n area with nice infrastrucure, creationTimeMs=1651561930185, location=Lenana, postId=, rooms=studio room, caption=studio room along Ngong road, date=03/05/2022, owner=null)
My index from firestore
Looking at your class definitions, all look fine. You're using the correct annotations. However, when you're using the following query:
val postsRef = firestoreDb
.collection("posts")
.limit(20)
You'll need to know that you have to create an index. It can be manually created inside the Firebase Console or if you are using Android Studio, you'll find a message in the logcat that sounds like this:
FAILED_PRECONDITION: The query requires an index. You can create it here: ...
Simply click on that link or copy and paste the URL into a web browser and your index will be created automatically for you.
I m having a databaseView code like this.
#DatabaseView("SELECT * FROM Product")
data class ProductDatabaseView(
var productCode: String? = "",
var productName: String? = "",
var longDescription: String? = "",
var productUom: String? = "",
var customerCode: String? = "",
var price: String? = ""
)
And I wanna pass some value as we do in our DAO like this.
#DatabaseView("SELECT * FROM Product WHERE priceListCode = :priceListCode")
data class ProductDatabaseView(
var productCode: String? = "",
var productName: String? = "",
var longDescription: String? = "",
var productUom: String? = "",
var customerCode: String? = "",
var price: String? = ""
)
Is it able to pass value like this or is there any other option available with the help of View.
To my knowledge, no you can't pass argument to #DatabaseView. They work the same way as in sqlite which doesn't allow parameters in views.
There are two tables, Task and StatisticsTask for Android room, StatisticsTask has foreign key taskId for Task.id. When I update StatisticsTask, how to tell ui query data again?
#Entity
#Parcelize
data class Task(
var name: String,
var important: Boolean = false,
var startTime: String = timeHm(),
var endTime: String = "",
var remind: String = "",
var repeat: String = "",
var repeatInterval: String = "",
var desc: String = "",
var endDate: String = "",
var startDate: String = nowOfString(),
#PrimaryKey(autoGenerate = true) var id: Int = 0
) : Parcelable {
#Ignore
var selected = false
#Ignore
var time: String? = null
#Ignore
var finished: Boolean = false
}
#Entity(foreignKeys = [ForeignKey(entity = Task::class, parentColumns = arrayOf("id"), childColumns = arrayOf("taskId"),
onDelete = CASCADE)])
#Parcelize
data class StatisticsTask(
var taskId: Int,
var date: Calendar?,
var finish: Boolean = false,
#PrimaryKey(autoGenerate = true)
var id: Int = 0
) : Parcelable {
}
//TaskViewModel.kt
fun finishTask(task: Task) {
launch {
statisticsTaskDao.updateById(task.id, !task.finished)
}
}
#Dao
interface StatisticsTaskDao {
#Query("update statisticstask set finish = :finish where taskId=:taskId and date = :date")
fun updateById(taskId: Int, finish: Boolean, date:Calendar = thisDayInitial())
}
//in my fragment
val model = ViewModelProviders.of(this)[TaskViewModel::class.java]
model.getAllOfToday().observe(this, Observer<List<Task>> {
//udpate data to adapter
})
//I want to finish this task here(in adapter)
iv_finished.setOnClickListener {
//task: first item in adapter
model.finishTask(task)
//This not work. when I finish task, how to update data again?
model.getAllOfToday()
}
This is for android-room 2.2.0-rc1, Android studio 3.5.
How to tell db query data again? I tried call this again is not working: model.getAllOfToday().observe(this#TodayFragment, Observer> {
dispatchData(it)
})
I am working on an Android application in Kotlin which integrate Firebase.
Now I want to store my data (Kotlin data class) into Firebase Database.
Data Classes:
#Parcelize
data class Trip(
val fromAddress: String,
val toAddress: String,
val fromLocation: String,
val toLocation: String,
val orderUid: String
) : Parcelable
#Parcelize
data class Order(val trip: Trip, val date: Date, var status: OrderStatus, val userUid: String) : Parcelable {
var pickUpDate: Date? = null
var dropOffDate: Date? = null
var price: Double? = null
}
Fireabase Database write operation:
fun createNewOrder(
fromAddress: String,
toAddress: String,
fromLocation: Location,
toLocation: Location
) {
val fromGeoLocation = fromLocation.convertToGeoLocation()
val toGeoLocation = toLocation.convertToGeoLocation()
val userUid = sharedPreferences[CURRENT_USER_UID_KEY, ""]!!
val orderKey = databaseReference.child(DB_ORDERS_KEY).push().key
val tripKey = databaseReference.child(DB_TRIPS_KEY).push().key
val trip = orderKey?.let { createNewTrip(fromAddress, toAddress, it) }
val order = trip?.let { Order(it, Date(), OrderStatus.PENDING, userUid) }
if (trip != null && order != null && !userUid.isNullOrEmpty()) {
ordersGeoFire.setLocation(trip.fromGeoLocation, fromGeoLocation)
ordersGeoFire.setLocation(trip.toGeoLocation, toGeoLocation)
val allData = mutableMapOf<String, Any>()
allData["/$DB_TRIPS_KEY/$tripKey"] = trip?.convertToMap()
allData["/$DB_ORDERS_KEY/$orderKey"] = order?.convertToMap()
allData["/$DB_USERS_KEY/$userUid/$DB_ORDERS_KEY/$orderKey"] = true
databaseReference.updateChildren(allData)
}
}
I received this error:
com.google.firebase.database.DatabaseException: No properties to serialize found on class kotlin.Unit
Any suggestions?
The problem in your code is that the fileds inside your Trip class are not initialized. A recommended way in which you can create your model class would be:
class Trip(
val displayName: String = "",
val email: String = "",
val photoUrl: String = "",
val userId: String = ""
)
This is only what you need. And a way to create a new object of your Trip class, would be:
val trip = Trip(displayName, email, photoUrl, userId)
It was my mistake, because I was forget to add return type in my extensions convertToMap functions. Now they look like this:
fun Trip.convertToMap(): MutableMap<String, Any> {
val map = mutableMapOf<String, Any>()
map["fromAddress"] = fromAddress
map["toAddress"] = toAddress
map["fromGeoLocation"] = fromGeoLocation
map["toGeoLocation"] = toGeoLocation
map["orderUid"] = orderUid
return map
}
And also thanks to #Alex Mamo for his answer, it helps me in my investigation.
Now my code looks like this and works fine:
#Parcelize
data class Trip(
var fromAddress: String = "",
var toAddress: String = "",
var fromGeoLocation: String = "",
var toGeoLocation: String = "",
var orderUid: String = ""
) : Parcelable
#Parcelize
data class Order(
var trip: Trip? = null,
var date: Date? = null,
var status: OrderStatus? = null,
var userUid: String = ""
) : Parcelable {
var pickUpDate: Date? = null
var dropOffDate: Date? = null
var price: Double? = null
}
fun createNewOrder(
fromAddress: String,
toAddress: String,
fromLocation: Location,
toLocation: Location
): LiveData<Order> {
orderLiveData = MutableLiveData()
orderLiveData.value = null
val userUid = sharedPreferences[CURRENT_USER_UID_KEY, ""]!!
val orderKey = databaseReference.child(DB_ORDERS_KEY).push().key
val tripKey = databaseReference.child(DB_TRIPS_KEY).push().key
val trip = orderKey?.let { createNewTrip(fromAddress, toAddress, fromLocation, toLocation, it) }
val order = trip?.let { Order(it, Date(), OrderStatus.PENDING, userUid) }
if (trip != null && order != null && !userUid.isNullOrEmpty()) {
val allData = mutableMapOf<String, Any>()
allData["/$DB_TRIPS_KEY/$tripKey"] = trip.convertToMap()
allData["/$DB_ORDERS_KEY/$orderKey"] = order.convertToMap()
allData["/$DB_USERS_KEY/$userUid/$DB_ORDERS_KEY/$orderKey"] = true
databaseReference.updateChildren(allData) { databaseError, databaseReference ->
if (databaseError == null) orderLiveData.value = order
}
}
return orderLiveData
}
Hope this will be helpful
I have a data class where I retrieve Firebase information. It looks similar to this:
#IgnoreExtraProperties
data class EventData(var eventEmoji: String = "",
var PlanType: PlanType = PlanType.CUSTOM_TYPE_1,
var accel: Boolean = false,
var latitude: String = "0.0",
var longitude: String = "0.0",
var initialDate: String = "",
var finalDate: String = "",
var initialHour: String = "",
var finalHour: String = "",
var plae: String = "",
var addr: String = "",
var description: String = "",
var adminProfileImageUrl: String = "") : Serializable
The problem that I'm facing now, is that exist a new data information that before was unused.
But this new field can exist or not depending on Business people. That field contains some translations, but... not always are the same or same number of it.
Example how is in Firebase:
The problem is that I don't know how to retrieve all this data in the EventData class.
I tried to add the new attribute as:
var localizable: Map<Any,Any>? = hashMapOf()
var localizable: Map<String,Any>? = hashMapOf()
var localizable: Map<String,String>? = hashMapOf()
var localizable: Map<String,SpecificClass>? = hashMapOf()
But... doesn't work.
Is possible to achieve?