How to get a subcollection from a document firestore flutter - flutter

Im trying to fetch the documents from a subcollection which is in another document, and when I try to fetch the docs and fill a local list with the "docs data" it doesn't fill it, can anyone tell me what I'm doing wrong here?
My method of when I try to fetch the subcollection:
static Stream<List<CheckInOutModel>> employeeCheckInOutStream() {
return firebaseFirestore
.collection('employees')
.doc(auth.currentUser!.uid)
.collection('employeeList')
.snapshots()
.asyncMap((QuerySnapshot querySnapshot) {
final List<CheckInOutModel> employeesCheckInOutList = [];
for (final element in querySnapshot.docs) {
firebaseFirestore
.collection('employees')
.doc(auth.currentUser!.uid)
.collection('employeeList')
.doc(element.id)
.collection('checkInOutList')
.snapshots()
.asyncMap((QuerySnapshot query) {
for (final element in query.docs) {
final employeeCheckInOutModel =
CheckInOutModel.fromDocumentSnapshot(
documentSnapshot: element,
);
employeesCheckInOutList.add(employeeCheckInOutModel);
}
});
}
return employeesCheckInOutList;
});
}
My method when I fetch the fields of the documents that the subcollection is in:
static Stream<List<EmployeeModel>> employeeStream() {
return firebaseFirestore
.collection('employees')
.doc(auth.currentUser!.uid)
.collection('employeeList')
.snapshots()
.map((QuerySnapshot query) {
final List<EmployeeModel> employees = [];
for (final employee in query.docs) {
final employeeModel =
EmployeeModel.fromDocumentSnapshot(documentSnapshot: employee);
employees.add(employeeModel);
}
return employees;
});
}

So I figured out what I did wrong here, I tried to call a stream of it when I only needed it when a callBack is called, so I changed the logic accordingly and went with Future instead Stream
My updated code:
static Future<List<CheckInOutModel>> employeeCheckInOutStream({
required String id,
}) async {
final List<CheckInOutModel> employeesCheckInOutList = [];
final query = await firebaseFirestore
.collection('employees')
.doc(auth.currentUser!.uid)
.collection('employeeList')
.doc(id)
.collection('checkInOutList')
.get();
for (final employee in query.docs) {
final employeeCheckInOutModel = CheckInOutModel.fromDocumentSnapshot(
documentSnapshot: employee,
);
employeesCheckInOutList.add(employeeCheckInOutModel);
}
return employeesCheckInOutList;
}

Related

How to fetch a `DocumentReference` from a Firebase `get()`

I have a collection ads that contains a DocumentReference as ownerId.
With the code below, I am able to fetch the 10 most recent ads as aList<Ad>:
/// Returns a list of ads of the given [category]
static Future<List<ClassifiedAd>> getFromCategory(
ClassifiedAdCategory category,
{int max = 10}) async {
return FirebaseFirestore.instance
.collection('ads')
.where('category', isEqualTo: category.name)
.orderBy('creationDate', descending: true)
.limit(max)
.get()
.then((snapshot) {
return snapshot.docs.map((doc) {
final data = doc.data();
return Ad.fromMap(data);
}).toList();
});
But now I'd like to fetch the owner (collection users) from the DocumentReference I was talking about above. But I am a but puzzled about how to do that.
My modified code below does not compile:
The return type 'List' isn't a 'FutureOr<List>', as required by the closure's context.
/// Returns a list of ads of the given [category]
static Future<List<ClassifiedAd>> getFromCategory(
ClassifiedAdCategory category,
{int max = 10}) async {
return FirebaseFirestore.instance
.collection('ads')
.where('category', isEqualTo: category.name)
.orderBy('creationDate', descending: true)
.limit(max)
.get()
.then((snapshot) {
// <<<< Error starts right here down to the removeWhere()
return snapshot.docs.map((doc) {
final data = doc.data();
final DocumentReference docRef = data["ownerId"];
return docRef.get().<ClassifiedAd?>then((snapshot) {
if (snapshot.exists) {
return ClassifiedAd.fromMap(data);
}
return null;
});
}).toList()
// Don't take into account potential nulls
..removeWhere((a) => a == null);
});
How should I do that?
I would say that the wrong thing that you're doing is you're trying to get a snapshot asynchronously inside the map() method which is synchronous, for such cases like yours, I recommend using await/async and to not return anything until you guarantee that you got it, try this:
static Future<List<ClassifiedAd>> getFromCategory(
ClassifiedAdCategory category,
{int max = 10}) async {
final snapshot = await FirebaseFirestore.instance
.collection('ads')
.where('category', isEqualTo: category.name)
.orderBy('creationDate', descending: true)
.limit(max)
.get();
List<ClassifiedAd> result = [];
for (int index = 0; index < snapshot.docs.length; index++) {
final doc = snapshot.docs[index];
final data = doc.data();
final DocumentReference docRef = data["ownerId"];
final docOwnerSnapshot = await docRef.get();
if (docOwnerSnapshot.exists) {
result.add(ClassifiedAd.fromMap(data));
}
}
return result;
}

How to pass a List or specific looped list to firebase doc in flutter

I am trying to achieve a task in which I have a List<dynamic>and its giving me multiple values on its indexes e.g. ['Me','Admin', so on....] something like this.
I cannot pass the List directly to Document ID it gives index error and I don't if it will still give error or not If the List give data in string List<String>
I want to loop around the indexes of this list and pass it to Firebase collection's document id to get multiple data's of the users. For example on list's index 0 there's Me coming for myself and on index 1 there's Admin coming. Both have their respective data stored in Firestore collection with their own document id's Me and Admin. I want it to be checked on the runtime the app will check if its Me or Admin or Some other index value
Here's my code of the list and the firestore I'm trying to achieve.
List<dynamic> clientcodes = [];
void getclientcodes() async {
final clientcode = await FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser!.email)
.get()
.then((clientcode) {
return clientcode.data()!["clientcode"];
});
setState(() {
if (clientcode != null) {
clientcodes = clientcode;
} else if (clientcode == null) {
setState(() {
const SpinKitSpinningLines(size: 100, color: Color(0xFF25315B));
});
}
});
}
Firestore:
Future getdatastatus() async {
DocumentSnapshot result = await FirebaseFirestore.instance
.collection("Statements")
// .doc("If I hardcode it the value of index 0 or 1 it works fine")
.doc(portfolionames.toString()) // This is area of issue
.get();
if (result.exists) {
print("Yes");
} else {
print("No");
}
}
You can insert getdatastatus() inside a loop, and let it get the index automatically by comparing it with any value you want it, see this:
Future getdatastatus() async {
for (var item in clientcodes) {
String docId = item.id;
if (docId == 'X' || docId == 'Y') {
DocumentSnapshot result = await FirebaseFirestore.instance
.collection("Statements")
.doc(docId)
.get();
if (result.exists) {
print("Yes");
} else {
print("No");
}
}
}
}
Hope that work with you!!
Update
In the first section of your code, I think there is a problem..
You can create the list out of the firestore streaming, then add the coming data to the list of model, after that you can loop it to take the value you want.
Class Database{
List<TestModel> clientcodes = [];
getclientcodes() {
return FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser!.email)
.snapshots()
.listen((event) {
clientcodes.add(TestModel.fromMap(event));
setState(() {
if (clientcode != null) {
clientcodes = clientcode;
} else if (clientcode == null) {
setState(() {
const SpinKitSpinningLines(size: 100, color: Color(0xFF25315B));
});
}
});
});
}
}
class TestModel {
late String name;
late String description;
TestModel({
required this.name,
required this.description,
});
TestModel.fromMap(DocumentSnapshot data) {
name = data['name'];
description = data['description'];
}
}

Cloud Firestore query with Variable in Where clause is not working. Flutter

class SetFirebaseDataSourceImpl implements SetFirebaseDataSource {
final FirebaseFirestore firestore;
SetFirebaseDataSourceImpl(this.firestore);
#override
Future<List<SetModel>> getAllSet(String subjectId) async {
var data = await firestore
.collection('set')
.where('subjectId', isEqualTo: subjectId)
.get();
return data.docs.map((e) {
var a = e.data();
print('set $a');
print(a);
return SetModel.fromJson(a);
}).toList();
}
}
it is not fetch document from firestore
and when replace "mathId" in place of subjectId it workfine
when we remove variable (subjectId) and write actual value then it works fine
Cloud Firestore query with Variable in Where clause isn't working. Flutter
you missed instance
You wrote this
class SetFirebaseDataSourceImpl implements SetFirebaseDataSource {
final FirebaseFirestore firestore;
SetFirebaseDataSourceImpl(this.firestore);
#override
Future<List<SetModel>> getAllSet(String subjectId) async {
var data = await firestore
.collection('set')
.where('subjectId', isEqualTo: subjectId)
.get();
return data.docs.map((e) {
var a = e.data();
print('set $a');
print(a);
return SetModel.fromJson(a);
}).toList();
}
}
it is supposed to be this
class SetFirebaseDataSourceImpl implements SetFirebaseDataSource {
final FirebaseFirestore firestore;
SetFirebaseDataSourceImpl(this.firestore);
#override
Future<List<SetModel>> getAllSet(String subjectId) async {
var data = await firestore.instance
.collection('set')
.where('subjectId', isEqualTo: subjectId)
.get();
return data.docs.map((e) {
var a = e.data();
print('set $a');
print(a);
return SetModel.fromJson(a);
}).toList();
}
}
Your code is okay.

How to get firestore data as stream?

I am making a collection group query, where upon matching a particular field, I am going a level backwards and then read the data.
I am able to do that in Future approach.
Here is my code returning future:
#override
Future<Either<JobPostFailure, List<JobPost>>> readAppliedJobPosts({
required String seamanId,
}) async {
final querySnapshot = await FirebaseFirestore.instance
.collectionGroup(ConstStrings.applications)
.where(
ConstStrings.seamanId,
isEqualTo: seamanId,
)
.get();
final List<JobPost> mList = [];
for (var docSnap in querySnapshot.docs) {
final jobPostDocSnap = await docSnap.reference.parent.parent?.get();
mList.add(JobPostDto.fromFirestore(jobPostDocSnap!).toDomain());
}
return right(mList);
}
Now I am struggling to do this in Stream approach, where my return type would be something like this : Stream<Either<JobPostFailure, List<JobPost>>>. What is the equivalent of my above code in Stream?
My try so far :
#override
Stream<Either<JobPostFailure, List<JobPost>>> watchAppliedJobPosts({
required String seamanId,
}) async* {
yield* _firestore
.collectionGroup(ConstStrings.applications)
.where(
ConstStrings.seamanId,
isEqualTo: seamanId,
)
.snapshots()
.map((event) {
return event.docs.map((e) {
return e.reference.parent.parent!.snapshots().map((event) {
return right(JobPostDto.fromFirestore(event).toDomain());
}).toList();
});
});
}
And its a big mess!
You can use method snapshots instead of get. Is will create a new stream that will fetch data for every change your document or collection has

Read all documents from collection from firestore in Flutter

I try to read all documents from collection as a model List from firestore like the java code.
Java code is
FirebaseFirestore.getInstance().collection("Product").addSnapshotListener((value, error) -> {
if (!value.isEmpty()){
List<Product> productList = value.toObjects(Product.class);
}
});
I try in flutter
QuerySnapshot data;
List<DocumentSnapshot> snapshot = new List<DocumentSnapshot>();
List<Product> productList = [];
data = await FirebaseFirestore.instance
.collection('Product')
.get();
snapshot.addAll(data.docs);
productList = snap.map((e) => Product.fromFirestore(e)).toList();
but always the list is empty, but the snapshot length is always same as my database.
database
Model class
void getProducts () async {
List<Product> productList = [];
var productsFromFirebase = await FirebaseFirestore.instance.collection("Product").get();
if (productsFromFirebase.docs.isNotEmpty) {
for (var doc in productsFromFirebase.docs) {
productList.add(Product.fromFirestore(doc)); //without '.data()' because you have it in your model.
}
}
print(productList.length); //prints the length of your products list.
}