I have a Firebase Database
which looks like :
I have a node named Products
Products
|
|--------prod1_id
| |
| |--name : "prod1"
| --state: "free"
|
|
|---------prod2_id
|
|--name : "prod2"
--price: "not_free"
Then I have the node for users
Users
|
|--------user1_id
| |
| |--name : "user1"
| --e-mail: "user1@email.com"
|
|
|---------user2_id
|
|--name : "user2"
--e-mail: "user2@email.com"
And on my application user can purchase, and only when he/she does, I create a new node called PurchasedItems
where I store the User
and Product
Purchased items
|
|--------user1_id
| |
| |--prod1: "any_value"
| --prod2: "any_value"
|
|
|---------user2_id
|
|--prod1: "any_value"
The thing is that on my query for objects, I do this :
products_ref= FirebaseDatabase.getInstance().getReference().child("products");
WHAT IS THE PROBLEM?
User that is logged in to the app, even if he has bought a product it will show the productNotFree layout because on products I have not any reference to what user has bought this item, but I have something similar on my “TryToPurchase” method because I have a query to the PurchasedProducts
to see if user has this product or not to make the purchase or not.
On my onClick()
method I have this to check if user has paid for it or not
root_ref.child("PurchasedProducts").child(currentuser).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.hasChild(model.getProduct_id())){
showToast("He has that product");
}
else{
showToast("Purchasing");
//Purchase for it
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
So far I had this switch()
statement to setImageResource
but from the query to products
NOTE:layoutFree is for example a box that has not a lock on it, and layoutNotFree is the same box but with a lock
switch (model.getState()) {
case "free":
holder.card_image.setImageResource(layoutFree);
break;
case "not_free":
holder.card_image.setImageResource(layoutNotFree));
break;
default:
break;
}
Now I’ve faced that if user purchase an item, he/she won’t be able to see the layoutFree even if he purchased it, he will see the layout with a lock even if he/she has paid for it.
What I’ve thought to do?
On same onBindViewHolder
where I have the switch()
statement I could make another query to Firebase
asking if currentUser
has this product and if he has that product I put the freeLayout otherwise the NotFreeLayout.
But I think it’s kinda dirty, so I put it here to see if there’s another way to make it efficient.
What I’d like to see?
Well, if I’m logged as a user that has paid for one product that has the state “not_free” I’d like to see the product with layout without lock item and also if I pay for an item I create a table purchasedItems
and I do something like this :
root_ref.child("PurchasedProducts").child(currentuser).child(productId).setValue("paid");
And also I’d like to see the RecyclerView
changes the layoutNotFree to layoutFree, is any way to do it instead of making a refresh of the Activity
? Refresh of Activity
means that I have to re-do the adapter, create the RecyclerView
etc…
I hope I’ve explained it correctly and step by step to avoid confusion, if I did something wrong on question feel free to edit it or question it to me and I’ll edit.
Answer
In this example, all products table children is saved in one ArrayList
with only one request to Firebase Database
.
According to your database schema.
First, Define a Arraylist
of Products.class
to hold all products details
ArrayList<Products> productsArrayList = new ArrayList<>();
Second, define the Products.class
in new class
public class Products {
public String id;
public String name;
public String state;
public Products() {
}
public Products(String name, String state) {
this.name = name;
this.state = state;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getState() {
return state;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setState(String state) {
this.state = state;
}
}
I only add id
(Necessary for position search), name
, state
You can add other values to Products.class
Third, make the Firebase query to get all products children
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Products");
ref.addListenerForSingleValueEvent(
new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// for loop will be executed for each children
for(DataSnapshot userSnapshot : dataSnapshot.getChildren()){
// put snapShot in Products.class ...... only children added
// in This case Children Will be price, quantity
Products products = userSnapshot.getValue(Products.class);
// set id manually to tha same instance of the class
products.setId(userSnapshot.getKey());
// add the Post class to ArrayList
productsArrayList.add(products);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
//handle databaseError
}
});
Now after products details are saved in productsArrayList
you can use them anywhere.
A helper method to search in ArrayList<Products>
and get specific position
public int getArrayListProductsPosition(String id){
for(int i = 0; i < productsArrayList.size() ; i++){
// if the passed id equals some id in arrayList return arrayList position
if(Objects.equals(productsArrayList.get(i).getId(), id)){
return i;
}
}
return 0;
}
Fourth, Now anywhere the class if you need to access specific product details, to update UI
for example;
// pass the id of the product you want to get details about
// I suppose this id will probably be retrived from another database tabel
int position = getArrayListProductsPosition(id);
//update your UI TextView quantity for example;
String name = productsArrayList.get(position).getName()
textQuantitiy.setText(name);
// or update you UI ImageView for example;
For more efficiency
— in the search method (getArrayListProductsPosition
), save products id's
in database as numbers and change in Products.class
.
String id
to Long id
because String-String
comparison is slower than Long-Long
comparison
— if Products table is to be changed (not static) use the productsArrayList
once and immediately after retrieve query (you don’t want the data to be old)