I have 2 object stored in Firestore Product & Shop
And there maybe a lot of Product and Shop in the future, so I have Shop DocumentReference in Product and vise-versa
Here are how they look like
class Shop extends Equatable {
final String? id;
final String name;
final List<Product?> shopProduct;
final DateTime createDate;
...
static Future<Shop> fromDocument(DocumentSnapshot doc) async {
final data = doc.data() as Map<String, dynamic>;
final shopProductRef = data['shopProduct'];
final List<Product?> shopProductList;
if (shopProductRef.isNotEmpty) {
shopProductList = List.from(shopProductRef.map((ref) async {
Product.fromDocument(await ref!.get());
}));
} else {
shopProductList = [];
}
return Shop(
id: doc.id,
name: data['name'],
shopProduct: shopProductList,
createDate: (data['createDate'] as Timestamp).toDate(),
);
}
And
class Product extends Equatable {
final String? id;
final Shop shop;
final double price;
final String title;
final DateTime date;
...
static Future<Product?> fromDocument(DocumentSnapshot doc) async {
final data = doc.data() as Map<String, dynamic>;
final shopRef = data['shop'] as DocumentReference;
final shopDoc = await shopRef.get();
return Product(
id: doc.id,
shop: await Shop.fromDocument(shopDoc),
price: data['price'],
title: data['title'],
date: (data['date'] as Timestamp).toDate(),
);
}
This is what I think should work in the first place but it brings up a problem that it is causing a loop since both are referencing each other.
I have come up a fix which is creating a second fromDocument method which skip the shopProduct or Shop when I am referencing it.
Is it the only / best way to do it?
Thank you
As I know you have two options
First one is to add a document reference instead of referring to the classes
something like this
class Product extends Equatable {
final String? id;
final DocumentRefrence<Shop> shop;
final double price;
final String title;
final DateTime date;
...
and also do the same for Shop model
class Shop extends Equatable {
final String? id;
final String name;
final List<DocumentReference<Product>?> shopProduct;
final DateTime createDate;
...
Second one is as you mentioned, create a method for Product model and name it for example: Map<String, dynamic> toShopCollection() and use it while setting a shop in firestore and also do the same for Shop model.
cm if you need more details
Related
I'm recently started using Firebase firestore. I'm stuck at the point were the collection have sub collections and get those items to my model.
Let say I have this collection
meats
\ id : 1
\ name : Chicken
subMeats
\ id : 11
\ name : Country Chicken
\ id : 12
\ name : Broiler Chicken
meats
\ id : 2
\ name : Pork
subMeats
\ id : 21
\ name : White Pork
\ id : 22
\ name : Black Pork
return meatTypeCollection.get().then((value) {
value.docs.forEach((mainDoc) {
// MainMeatEntity.fromSnapshot(mainDoc)
_logger.i(mainDoc.data()['name']);
meatTypeCollection
.doc(mainDoc.id)
.collection('subMeats')
.get()
.then((snapShots) {
snapShots.docs.forEach((doc) {
_logger.i(doc.data()['name']);
MainMeat.fromEntity(MainMeatEntity.fromSnapshot(doc));
});
});
});
//return MeatTypes.fromEntity(MeatTypeEntity.fromJson(value.docs));
}).catchError((error) => _logger.e("Failed to load meat types : $error"));
The above does not capture the collection. I use entity to model.
import 'package:flutter/cupertino.dart';
import 'entities/main_meat_entity.dart';
import 'entities/meat_type_entity.dart';
#immutable
class MainMeat {
final String id;
final String name;
final MeatTypeEntity subMeats;
final String shopId;
const MainMeat({
this.id,
this.name,
this.subMeats,
this.shopId,
});
static MainMeat fromEntity(MainMeatEntity mainMeatEntity) {
return MainMeat(
id: mainMeatEntity.id,
name: mainMeatEntity.name,
subMeats: mainMeatEntity.subMeats,
shopId: mainMeatEntity.shopId,
);
}
MainMeatEntity toEntity() {
return MainMeatEntity(id, name, subMeats, shopId);
}
static const empty = MainMeat(id: '', shopId: "", name: "");
}
-----------------------------****************--------------------
part 'meat_type_entity.g.dart';
#JsonSerializable()
class MeatTypeEntity extends Equatable {
final String id;
final String name;
final String shopId;
const MeatTypeEntity(this.id, this.name, this.shopId);
factory MeatTypeEntity.fromJson(Map<String, dynamic> json) =>
_$MeatTypeEntityFromJson(json);
Map<String, dynamic> toJson() => _$MeatTypeEntityToJson(this);
#override
List<Object> get props => [
id,
name,
shopId,
];
static MeatTypeEntity fromSnapshot(DocumentSnapshot snap) {
return MeatTypeEntity(
snap.id,
snap.data()['name'],
snap.data()["shopId"],
);
}
Map<String, Object> toDocument() {
return {
"id": id,
'mainMeat': name,
"shopId": shopId,
};
}
}
I can list it's collection and sub collections fine. But not sure to load into my model. Any help appreciated thanks.
You can't store subCollection to collection directly, Read FireStore data model section.
So your structure will be look like
Meal(document) -> all meal collection -> Sub Meal (document) -> all sub meals collection.
If you want to read the data like firebase database, you can't read the complete tree/hierarchy at a time.
So assuming this is a Dart question of how to create models, I would create something like this:
class Meat {
final String id;
final String name;
final List<SubMeat> subMeats;
Meat({
#required this.id,
#required this.name,
this.subMeats,
});
}
class SubMeat {
final String id;
final String name;
SubMeat({
#required this.id,
#required this.name,
});
}
I've created a map yet am having difficulty accessing the variable 'contact' within a stateful widget. I'm not sure where else to declare final Contact contact; .
The Contact model file.
class Contact {
int rating;
String name;
String location;
int phoneNumber;
String instagram;
int birthday;
String notes;
Contact(this.name, this.phoneNumber, this.location, this.rating,
this.instagram, this.birthday, this.notes);
Map<String, dynamic> toJson() => {
'Name': name,
'PhoneNumber': phoneNumber,
'Location': location,
'Rating': rating,
'Instagram': instagram,
'Birthday': birthday,
'Notes': notes,
};
Where final Contact contact; is currently declared
class NewContact extends StatefulWidget {
NewContact({Key key, #required this.contact}) : super(key: key);
final Contact contact;
#override
NewContactState createState() => NewContactState();
}
class NewContactState<T extends NewContact> extends State<T> {
final db = FirebaseFirestore.instance; //...etc
Where the Map is being called
await db
.collection("userData")
.doc(uid)
.collection("Contacts")
.add(contact.toJson());
Error = Undefined name 'contact'.
Since contact is defined in the class that extends statful widget, NewContact, and you want to access it's corresponding state class NewContactState, you should call it like this widget.contact.toJson().
I am trying to fill my CurrentUser object with the same information as the uid of the logged in user when my users login to the application
My databaseService :
final CollectionReference userCollection =
Firestore.instance.collection('users');
Future<User> getCurrentUserData(String uid) async{
var doc = userCollection.document(uid);
And My Home Page :
class HomeScreen extends StatefulWidget {
final FirebaseUser currentUser;
HomeScreen({#required this.currentUser});
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
And My CurrentUser Model :
class CurrentUser {
static String name;
static String lastName;
static String uid;
static String phone;
static String addresses;
static String photoString;
static int cityId;
static int districtId;
static List<Loss> userLosses;
}
But i cant figure out connect them
If you are using the firebase authentication then you can use FiresbaseAuth.instance.currentUser, it will return a FirebaseUser object that will contain the info of the current user.
I figure it like this:
Future<User> getCurrentUserData(String uid)async {
var docRef = await userCollection.document(uid).get();
User currentUser = User.fromJson(docRef.data);
currentUser.uid=docRef.documentID;
return currentUser;
}
docRef.data is <String,dynamic> map and just i change my user class like this:
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['Name'].toString(),
lastName: json['LastName'].toString(),
phone: json['Phone'].toString(),
photoString: json['PhotoString'].toString(),
districtId: int.parse(json['DistrictId'].toString()),
cityId: int.parse(json['CityId'].toString()),
addresses: json['Addresess'].toString());
}
i'm trying to query a firestore collection with a dynamic path (user specific), it works hardcoded, but not dynamic with a variable, someone know the issue and can help?
Thanks in advance
final CollectionReference addressCollection =
Firestore.instance.collection('users/r9qClctByGXinYAmB2MqQNctgd53/addresses');
works.
This not:
final CollectionReference addressCollection =
Firestore.instance.collection('users/$userId/addresses');
userId is = r9qClctByGXinYAmB2MqQNctgd53
Full FirestoreDatabase code:
class FirestoreDatabase {
final _service = FirestoreService.instance;
static String userId;
void setUserId(uid) {
userId = uid;
}
final CollectionReference addressCollection =
Firestore.instance.collection('users/$userId/addresses');
// Adresses List Stream
Stream<List<Address>> get addressesStream {
return addressCollection.snapshots().map(_addressListFromSnapshot);
}
List<Address> _addressListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return Address.fromMap(doc.data);
}).toList();
}
}
I have the following database data which I intend to display on a ListView with FirebaseListAdapter
My problem is creating a Query since the child elements after date are anonymous. Here is the query code
Query query = FirebaseDatabase.getInstance().getReference().child("Updates").child(refMail).child(day)
.orderByKey();
refMail and day are user email address and date respectively.
Here is also my Data Model Class
public class NotesDataModel {
private String Note;
private String uid;
private String time;
public NotesDataModel(){
}
public NotesDataModel(String Note, String uid, String time){
this.Note=Note;
this.uid=uid;
this.time=time;
}
public String getNote() {
return Note;
}
public void setNote(String note) {
Note = note;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}}
and finally the adapter initialization
FirebaseListOptions<NotesDataModel> options = new FirebaseListOptions.Builder<NotesDataModel>()
.setQuery(query, NotesDataModel.class)
.setLayout(R.layout.notes_cell_layout)
.build();
mAdapter = new FirebaseListAdapter<NotesDataModel>(options) {
#Override
protected void populateView(View view, NotesDataModel note, int position) { //some code }}; notesList.setAdapter(mAdapter);
Previous version worked like this
ref = FirebaseDatabase.getInstance().getReferenceFromUrl(FactoryDaftari.firebaseURL + "Updates/" + refMail + "/" + day);
And the Adapter initialization
mAdapter = new FirebaseListAdapter<NotesDataModel>(this, NotesDataModel.class, R.layout.notes_cell_layout, ref) {
#Override
protected void populateView(View view, NotesDataModel note, int position) { }};
You won't be able to make this query with the way your data is structured. It's common in NoSQL databases to make copies of data, structured for the purpose of specialized querying. So, if you want to query a list of notes, you'll need a structure where all the notes are children of the same parent, then make your query against that structure.
(Also, organizing your notes by a node with a date, like you have now, may not even be the best general structure in the first place.)