Reputation: 666
Question is updated
How would I query 2 collections? I have a wishlist collection where each wishlist document looks like this:
documentID: "some-wishlist-id",
modified: "1564061309920",
productId: "2tqXaLUDy1IjxLafIu9O",
userId: "0uM7Dt286JYK6q8iLFyF4tG9cK53"
And a product collection where each product document looks like this. The productId in the wishlist collection would be a documentID in the product collection:
documentID: "2tqXaLUDy1IjxLafIu9O",
dateCreated: "1563820643577",
description: "This is a description for Product 9",
images: ["some_image.jpg"],
isNegotiable: true,
isSold: false,
rentPrice: 200,
sellPrice: 410,
title: "Product 9",
totalWishLists: 0,
userId: "0uM7Dt286JYK6q8iLFyF4tG9cK53"
NOTE: To be clear, the wishlist query should return a list of documents that I need to iterate over to retrieve the product.
Not sure if I need to use streams or futures in this case, but this is what I have so far:
Future<Product> getProduct(String documentId) {
return Firestore.instance
.collection(APIPath.products())
.document(documentId)
.get()
.then((DocumentSnapshot ds) => Product.fromMap(ds.data));
}
Query getWishListByUser(userId) {
return Firestore.instance
.collection(APIPath.wishlists())
.where('userId', isEqualTo: userId);
}
Future<List<Product>> getProductsWishList(userId) async {
List<Product> _products = [];
await getWishListByUser(userId)
.snapshots()
.forEach((QuerySnapshot snapshot) {
snapshot.documents.forEach((DocumentSnapshot snapshot) async {
Map<String, dynamic> map = Map.from(snapshot.data);
map.addAll({
'documentID': snapshot.documentID,
});
WishList wishList = WishList.fromMap(map);
await getProduct(wishList.productId).then((Product product) {
print(product.title); // This is printing
_products.add(product);
});
});
});
// This is not printing
print(_products);
return _products;
}
Thanks
Upvotes: 5
Views: 7880
Reputation: 666
I found a solution last weekend but forgot to post it. Figured I do that now in case someone else has this problem.
I used a combination of StreamBuilder
and FutureBuilder
. Not sure if there is a better answer, perhaps combining multiple streams? But this worked for me.
return StreamBuilder<List<Wishlist>>(
stream: wishListStream(userId),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Wishlist> wishlists = snapshot.data;
if (wishlists.length > 0) {
return new GridView.builder(
scrollDirection: Axis.vertical,
itemCount: wishlists.length,
itemBuilder: (context, index) {
Future<Product> product =
getProduct(wishlists[index].productId);
return FutureBuilder(
future: product,
builder: (context, snapshot) {
if (snapshot.hasData) {
Product product = snapshot.data;
// Do something with product
} else {
return Container();
}
},
);
},
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
);
} else {
return Text('No products in your wishlist');
}
}
if (snapshot.hasError) {
print('WishlistPage - ${snapshot.error}');
return Center(child: Text('Some error occurred'));
}
return Center(child: CircularProgressIndicator());
},
);
Upvotes: 2
Reputation: 1366
With any database you'll often need to join data from multiple tables to build your view.
In relational databases, you can get the data from these multiple tables with a single statement, by using a JOIN clause.
But in Firebase (and many other NoSQL databases) there is no built-in way to join data from multiple locations. So you will have to do that in your code.
Create Wishlist Model:
class Wishlist {
Wishlist({
this.id,
this.modified,
this.productId,
this.userId
});
final String id;
final String modified;
final String productId;
final String userId;
Wishlist.fromMap(json)
: id = json['id'].toString(),
modified = json['modified'].toString(),
productId = json['productId'].toString(),
userId = json['userId'].toString();
}
And in your API file, do this:
final Firestore _fireStore = Firestore.instance;
getWishList(wishlistId) async {
return await _fireStore.collection('wishlists').document(wishlistId).get();
}
getProduct(productId) async {
return await _fireStore.collection('product').document(productId).get();
}
Future<List<Product>>getProductsWishList(wishlistId) async {
var _wishlists = null;
List<Product> _products = []; // I am assuming that you have product model like above
await getWishList(wishlistId).then((val) {
_wishlists = Wishlist.fromMap(val.data);
_wishlists.forEach((wish) {
await getProduct(wish.productId).then((product) {
_products.add(product));
});
});
});
return _products;
}
Upvotes: 4