How to retrieve list of model objects from firebase databse - java

Model Class
public String id;
public String total;
public List<CartModel> orderList;
public String currentDate;
public String orderBy;
I want to fetch these objects but unfortunately can't do this.
I'm accessing this list in my adapter class like the following way but getting null value.
protected void onBindViewHolder(#NonNull final OrderHolder holder, int position, #NonNull Orders model) {
List<CartModel> list = new ArrayList<>();
list = model.getOrderList();
Log.i("Orders", list+"");
holder.dateTime.setText(model.getCurrentDate());
holder.grandTotal.setText("Total "+model.getTotal());
holder.orderBy.setText(model.getOrderBy());
}
Please provide me a valid solution for doing this

The name of your field in the Java code doesn't match the property name in the JSON.
To make them match, change:
public List<CartModel> orderList;
To:
public List<CartModel> orderItems;

String userid="your unique id";
String orderid="your unique id";
DatabaseReference database=FirebaseDatabase.getInstance().getReference()
databse.child("yourroot")
.child("Users")
.child(userid)
.child("Orders")
.child(orderid)
.child("orderItems")
.addValueEventListener(new ValueEventListener {
#Override
void onCancelled(#NonNull DatabaseError error) {
//handle error as per your requirement
}
#Override
void onDataChange(#NonNull DataSnapshot snapshot) {
YourModel model=snapshot.getValue(YourModel.class)
//use the model or add to adapter payload and notify it on mainthread
}
})
Note: you may get DatabaseException if your model class is not an appropriate class to hold values from the DB.

Related

Mapping documents into an object of a class to use with FirestoreRecyclerAdapter

Context
I've been attempting to display data from Firestore in a FirestoreRecyclerAdapter, however this data is basically nested Maps, so a direct approach using the Query function is not ideal. Here is an image of my data structure:
Notice that ServiceOrder, client and vehicle are all Maps. In my Java code, ServiceOrder is made up of a Client and Vehicle objects.
So, if I were to use .setQuery(query, ServiceOrder.class), it would attempt to Map all of the data into ServiceOrder objects. But since my document is structured the way it is, that is not possible.
Issue
I suppose this could be fixed by mapping all documents into an object of a new class, similar to what is done here: https://medium.com/firebase-tips-tricks/how-to-map-an-array-of-objects-from-cloud-firestore-to-a-list-of-objects-122e579eae10.
Even though I can see how it could be done using a normal RecyclerView and using a custom adapter, could the same solution be used in FirestoreRecyclerAdapter? Because I did try to create something akin to the solution in the link, but couldn't get it to work.
My code
Here is where I'm setting up the RecyclerView and Querying the data from Firestore:
private void setupRecyclerView() {
RecyclerView recyclerView = findViewById(R.id.recyclerViewOs);
Query query = osRef.orderBy("ServiceOrder",
Query.Direction.ASCENDING); //This is the issue.
//How could I map the documents here?
FirestoreRecyclerOptions<ServiceOrder> options =
new FirestoreRecyclerOptions.Builder<ServiceOrder>()
.setQuery(query, ServiceOrder.class)
.build();
listAdapter = new FirestoreAdapter(options);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(listAdapter);
}
My FirestoreRecyclerAdapter, where I'm binding my Views. The onBindViewHolder returns NPE for every View. This is the problem with the nested Maps described early.
public class FirestoreAdapter extends FirestoreRecyclerAdapter<ServiceOrder, FirestoreAdapter.ViewHolder> {
#Override
protected void onBindViewHolder(#NonNull ViewHolder holder, int position, #NonNull ServiceOrder model) {
holder.osIdItem.setText(String.valueOf(model.getId()));
holder.osClientItem.setText(model.getPaymentForm());
holder.osDateItem.setText(model.getPaymentForm());
holder.osValueItem.setText(String.valueOf(model.getTotalValue()));
}
And finally, my ServiceOrder class. Getters/Setters were removed to increase readability.
public class ServiceOrder {
public Client client;
public Vehicle vehicle;
private String service;
private String observation;
private String paymentForm;
private String date;
private double totalValue;
private int id;
public ServiceOrder() {
}
private ServiceOrder(ServiceOrderBuilder serviceOrderBuilder){
this.client = serviceOrderBuilder.client;
this.vehicle = serviceOrderBuilder.vehicle;
this.service = serviceOrderBuilder.service;
this.paymentForm = serviceOrderBuilder.paymentForm;
this.observation = serviceOrderBuilder.observation;
this.totalValue = serviceOrderBuilder.value;
this.date = serviceOrderBuilder.date;
}
public static class ServiceOrderBuilder {
private Vehicle vehicle;
private Client client;
private final String service;
private final String paymentForm;
private final int id;
private final double value;
private final String date;
private String observation;
public ServiceOrderBuilder(Client client, Vehicle vehicle,
String service, String paymentForm,
int id, double value, String date) {
this.client = client;
this.vehicle = vehicle;
this.service = service;
this.paymentForm = paymentForm;
this.id = id;
this.value = value;
this.date = date;
}
public ServiceOrder.ServiceOrderBuilder observation(String observation) {
this.observation = observation;
return this;
}
public ServiceOrder build() {
ServiceOrder serviceOrder = new ServiceOrder(this);
return serviceOrder;
}
}
}
My attempt
As suggested in another post, I attempted to create a new ServiceOrderDocument in order to map all documents into an object of this class. The class:
public class ServiceOrderDocument {
ServiceOrder serviceOrder;
public ServiceOrderDocument() {}
public ServiceOrderDocument(ServiceOrder serviceOrder) {
this.serviceOrder = serviceOrder;
}
#PropertyName("ServiceOrder")
public ServiceOrder getServiceOrder() {
return serviceOrder;
}
}
Ànd pass this into the Adapter found in the private void setupRecyclerView(). However, the Adapter expects a QuerySnapshot, so I feel like I'm stuck here.
Reproducing the issue
If you'd like to try it out yourself, the best way would be to have three Classes, with one of them having objects from the other two. A example would be a Sale class having objects from Salesman and Product.
Proceed to write a Sale object into your Firestore database, and see how it creates a nested document. Then, try to display that Sale in a RecyclerView using FirestoreRecyclerAdapter. Your onBindViewHolder should have a Sale model that would get the data from it's getters.
Edit
So using a List to get the content seems to work at a first glance, by using Cast I could pass it as a adapter for the FirestoreRecyclerAdapter, however, it does not work for the startListening() methods. Here's what I did:
private void setupRecyclerView() {
docRef.get().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
services = document.toObject(ServiceOrderDocument.class).services;
RecyclerView recyclerView = findViewById(R.id.recyclerViewOs);
Query query = osRef.orderBy("ServiceOrder",
Query.Direction.ASCENDING);
FirestoreRecyclerOptions<ServiceOrder> options =
new FirestoreRecyclerOptions.Builder<ServiceOrder>()
.setQuery(query, ServiceOrder.class)
.build();
// listAdapter = new FirestoreAdapter(options);
services = (List<ServiceOrder>) new FirestoreAdapter(options);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter((RecyclerView.Adapter) services);
}
}
});
}
However, the following issue is created:
#Override
protected void onStart() {
super.onStart();
listAdapter.startListening();//NPE Error
services.startListening();//Can't use method
}
For those looking for an answer, read Alex Mamo comments and his post at:
https://medium.com/firebase-tips-tricks/how-to-map-an-array-of-objects-from-cloud-firestore-to-a-list-of-objects-122e579eae10.
For my solution, I did need a ServiceDocument class to help me map my documents so I could use it in my Adapter. This is what the class looks like:
public class ServiceDocument {
public ServiceOrder serviceOrder;
public ServiceDocument() {
}
#PropertyName("serviceOrder")
public ServiceOrder getServiceOrder() {
return serviceOrder;
}
}
Then, in your Activity or wherever you are managing your Recyler/Adapter, you would need something like this
Query query = osRef.orderBy("serviceOrder",
Query.Direction.ASCENDING);
FirestoreRecyclerOptions<ServiceDocument> options =
new FirestoreRecyclerOptions.Builder<ServiceDocument>()
.setQuery(query, ServiceDocument.class)
.build();
listAdapter = new FirestoreAdapter(options);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyclerView.setAdapter(listAdapter);
Last but not least, make sure your Adapter class has been adapted for your new class.
public class FirestoreAdapter extends FirestoreRecyclerAdapter<ServiceDocument, FirestoreAdapter.ViewHolder> {
public FirestoreAdapter(#NonNull FirestoreRecyclerOptions<ServiceDocument> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull ViewHolder holder, int position, #NonNull ServiceDocument model) {
holder.osIdItem.setText(String.valueOf(model.getServiceOrder().getId()));
holder.osClientItem.setText(model.getServiceOrder().getClient().getName());
holder.osDateItem.setText(model.getServiceOrder().getDate());
holder.osValueItem.setText(String.valueOf(model.getServiceOrder().getTotalValue()));
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.layout_os_item, parent, false);
return new FirestoreAdapter.ViewHolder(view);
}

Firebase Realtime Database - reading nested data

I am doing a project with firebase, able to save some records on the database, but retrieving it has been an issue for me, I've meddled with other posts from SO but they haven't worked for me. This is how the database looks like (An example):
And my code for retrieving the data:
private void readDataFromDB() {
databaseReference.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user = new User();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
user.setStrName(//Get the Name of the user);
user.setStrScore(//Get the Score of the user));
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
The User class:
public class User {
String strName, strScore;
public String getStrName() {
return strName;
}
public void setStrName(String strName) {
this.strName = strName;
}
public String getStrScore() {
return strScore;
}
public void setStrScore(String strScore) {
this.strScore = strScore;
}
}
How can I get the name and score from each specific user
In your code, you are setting values, you need to be retrieving values using the getters.
Try the following:
databaseReference.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
String name = user.getStrName();
String score = user.getStrScore();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
But, first you need to add the values to the database example:
User user = new User();
user.setStrName("my_name");
user.setStrScore("20");
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("users");
ref.push().setValue(user);
Note setValue():
In addition, you can set instances of your own class into this location, provided they satisfy the following constraints:
The class must have a default constructor that takes no arguments
The class must define public getters for the properties to be assigned. Properties without a public getter will be set to their default value when an instance is deserialized
You need to add a default constructor to the POJO class public User(){} and also the field names in the class should match the ones in the database. So change this String strName, strScore; into this String name, score; and generate the getters and setters again.
Instead of creating profile in every node you can use a global profile node, and in that store the profile data with their UID, which would make it easier for you to fetch detail of single user.
-profile
-UID1
-name
-score
-UID2
-name
-score
While retrieving you can use getCurrentUser.getUid() to retrieve data for each user:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
databaseReference.child("users").child("profile").child(uid).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user = new User();
user = dataSnapshot.getValue(User.class);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});

Android - Class java.util.Map has generic type parameters, please use GenericTypeIndicator instead

I know there's a lot of similar questions like mine, but I've read and tried everything but still unable to solve this issue. What I am trying to achieve is to populate my listview with the data from the firebase. Im following a tutorial on youtube but Ive added some stuff, timestamp in particular. The error is in my for loop and says :
Class java.util.Map has generic type parameters, please use GenericTypeIndicator instead
This is my database looks like :
My Notes.java
public class Notes {
String noteId;
String noteCategory;
String note;
String rating;
public Map timeStamp;
public Notes(){
}
public Notes(String noteId, String noteCategory, String note, String rating, Map timeStamp) {
this.noteId = noteId;
this.noteCategory = noteCategory;
this.note = note;
this.rating = rating;
this.timeStamp = timeStamp;
}
public String getNoteId() {
return noteId;
}
public String getNoteCategory() {
return noteCategory;
}
public String getNote() {
return note;
}
public String getRating() {
return rating;
}
public Map<String,String> getTimeStamp() { return timeStamp;}
}
And below is my NoteList.java
#Override
protected void onStart() {
super.onStart();
databaseNotes.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
notesList.clear();
for(DataSnapshot noteSnapshot : dataSnapshot.getChildren()){
Notes notes = noteSnapshot.getValue(Notes.class);
notesList.add(notes);
}
NoteList adapter = new NoteList(MyNotes.this, notesList);
listViewNotes.setAdapter(adapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Any advice would be greatly appreciated! Thank you in advance.
EDIT : Okay, Ive tried the suggested answer, and after I logged the map it shows my data on the console. However, how do I iterate my map into the listview ?
Map<String, Object> map = (Map<String, Object>) dataSnapshot.getValue();
From looking at your DB, it looks like you should use int or long type instead of the map type, since your timestamp has a number value

Android - unable to add data to arraylist of string - when data retrieved from firebase

I have a custom list adapter that has many methods that query the database and add data to different java objects or lists.
code to consider in adapter as follows: -
static class ViewHolder {
ArrayList<String> imgUrls;
//other variables;
}
then initiated it in getView method as:-
public View getView(#params) {
holder.imgUrls = new ArrayList<>();
}
//i have a method that query and retrieve data from firebase as follows:-
public void getPhoto(final ViewHolder holder) {
Query query = reference
.child(mContext.getString(R.string.dbname_collections))
.child(holder.collection.getCollection_id())
.child("photo_url");
query.keepSynced(true);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot singleSnapshot : dataSnapshot.getChildren()) {
imgUrl = singleSnapshot.getValue(String.class);
Log.d(TAG, "onDataChange: imgUrl: " + imgUrl);
holder.imgUrls.add(imgUrl);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Log.d(TAG, "getPhoto: list with values " + holder.imgUrls);
}
My database in firebase console look like this:-
{
"collections": {
"-L9nNSyF7ZhEqpvmPo0Z": {
"caption": "jakhaha",
"collection_id": "-L9nNSyF7ZhEqpvmPo0Z",
"date_created": "2018-04-11T11:55:57Z",
"photo_url": [
"https://firebasestorage.googleapis.com/v0/b/backbench-72a47.appspot.com/o/collection%2Fusers%2FHb8ORoozQ3WhyxjaSyqazjHypkf2%2F2018-04-11T11%3A55%3A57Z%2Fphoto0?alt=media&token=08fdb740-9b16-455b-83ec-bfcec1455834",
"https://firebasestorage.googleapis.com/v0/b/backbench-72a47.appspot.com/o/collection%2Fusers%2FHb8ORoozQ3WhyxjaSyqazjHypkf2%2F2018-04-11T11%3A55%3A57Z%2Fphoto1?alt=media&token=eeca4b10-0729-4ca9-be18-c31d054add19",
"https://firebasestorage.googleapis.com/v0/b/backbench-72a47.appspot.com/o/collection%2Fusers%2FHb8ORoozQ3WhyxjaSyqazjHypkf2%2F2018-04-11T11%3A55%3A57Z%2Fphoto2?alt=media&token=b33c97b0-c2bf-4199-bef0-b498f505a678"
],
//this are the imageUrls i want to retrieve as a arrylist
"tags": "#NoTags",
"user_id": "Hb8ORoozQ3WhyxjaSyqazjHypkf2"
}
}
}
in my firebase model class :-
public class Collection implements Parcelable {
//properties for photo (*case sensitive*)
private String caption;
private String date_created;
private String collection_id;
private String user_id;
private String tags;
//list of likes
private List<String> photo_url;
private List<Like> likes;
private List<Comment> comments;
public Collection() {
}
//constructor..getter setters etc...
}
in my log console i see that values of imgUrl are fatched
04-11 20:35:03.992 21269-21269/com.backbenchers.administrator.instaclone D/CollectionListAdapter: onDataChange: imgUrl://url of image 1
onDataChange: imgUrl: url of image 2
onDataChange: imgUrl: ...image 3
but it shows list as null or [] :-
D/CollectionListAdapter: getPhoto: list is with values : []
"I want to fetch this firebase data and add it to an arraylist.."
what is wrong ??
Thanks!

Firebase Database data collection

I am using Firebase database for my project. For the last few days I tried to retrieve data from database but without luck. I tried many tutorials and questions from Stack.. Database looks like this:
Database structure
My user class
I would like to retrieve information and store it in one of the strings..
My code is:
Every time I receive value Null or an Error. I am not sure If I am using correct reference("Users").
Is there is easy way to retrieve users name and store it into the string? Thanks
First you need to have same names for the fields in your database as in your model class. Looking at you model class, there are some things that you need to do. So, I'll provide a correct way to model your class.
public class UserModelClass {
private String name, sureName, date, phoneNumber;
public UserModelClass() {}
public UserModelClass(String name, String sureName, String date, String phoneNumber) {
this.name = name;
this.sureName = sureName;
this.date = date;
this.phoneNumber = phoneNumber;
}
public String getName() {return name;}
public String getSureName() {return sureName;}
public String getDate() {return date;}
public String getPhoneNumber() {return phoneNumber;}
}
In order to make it work, you need to remove all data from the database and add fresh one. There is a workaround when storing users in a Firebase database. Instead of using that that random key provided by the push() method, to use the uid. For that, I recommend you add the data as in the following lines of code:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
UserModelClass umc = new UserModelClass("Jonas", "Simonaitis", "today", "123456789");
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference uidRef = rootRef.child("users").child(uid);
uidRef.setValue(umc);
To read data, please use the following code:
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
UserModelClass userModelClass = dataSnapshot.getValue(dataSnapshot.class);
String name = userModelClass.getName();
String sureName = userModelClass.getSureName();
String date = userModelClass.getDate();
String phoneNumber = userModelClass.getPhoneNumber();
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
uidRef.addListenerForSingleValueEvent(eventListener);
In one of my tutorials I have explained step by step how to use the model classes when using Firebase.
First you have to do make these declarations:
private DatabaseReference mUserDatabase;
private FirebaseUser mCurrentUser;
in onCreate you need to make smth like this:
mCurrentUser = FirebaseAuth.getInstance().getCurrentUser();
String current_uid = mCurrentUser.getUid();
mUserDatabase =FirebaseDatabase.getInstance().getReference().child("Users")
.child(current_uid);
mUserDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String name = dataSnapshot.child("uName").getValue().toString();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Now you have the user name in "name"
First change userInformationActivity class variable names to match names in the database
example : name -> uName
surname -> uSurname
then
private void showData(DataSnapshot dataSnapshot){
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
userInformationActivity user = dataSnapshot.getValue(userInformationActivity.class);
Log.d("user Name is : " + user.uName);
}
}

Categories