0
votes

I had a function in my CustomerNotifier class that reads all customers as a list from Firebase as below:

  getCustomers(CustomerNotifier customerNotifier) async {
    String userId = (await FirebaseAuth.instance.currentUser()).uid;

    print('Current logged in user uid is: $userId');

    var snapshot = await customerCollection
        .orderBy('created_at', descending: true)
        .getDocuments();

    List<Customer> _customerList = [];

    snapshot.documents.forEach((document) {
      Customer customer = Customer.fromMap(document.data);
      _customerList.add(customer);
    });

    customerNotifier.customerList = _customerList;
  }

I have another function to updates or creates a new customer and saves to Firebase as below:

Future updateCustomer(Customer customer, bool isUpdating) async {
    CollectionReference customerRef =
        await Firestore.instance.collection('customer');
    if (isUpdating) {
      customer.updatedAt = Timestamp.now();
      await customerRef.document().updateData(customer.toMap());
      print('updated customer with id: ${customer.id}');
    } else {
      customer.createdAt = Timestamp.now();

      DocumentReference documentReference =
          await customerRef.add(customer.toMap());

      customer.id = documentReference.documentID;

      print('created customer successfully with id: ${customer.id}');

      await documentReference.setData(customer.toMap(), merge: true);
      addCustomer(customer);
    }
    notifyListeners();
  }

With both methods above, I used to successfully read and write customer data to my Firebase. However, I am trying to only read data created and updated by the currently signed in User. So suggestions from other stackoverflow threads, I've been advised to set my customer.id to userId, where userId == currentUser().uid. I can successfully write to my DB using an updated version of my updateCustomer as below:

Future updateCustomer(Customer customer, bool isUpdating) async {
    CollectionReference customerRef =
        await Firestore.instance.collection('customer');
    FirebaseUser user = await FirebaseAuth.instance.currentUser();
    String userId = user.uid;
    print('Current logged in user uid is: $userId');

    if (isUpdating) {
      customer.updatedAt = Timestamp.now();
      await customerRef.document(userId).updateData(customer.toMap());
      print('updated customer with id: ${customer.id}');
    } else {
      customer.createdAt = Timestamp.now();

      DocumentReference documentReference = await customerRef.document(userId);
      // add(customer.toMap());

      customer.id = documentReference.documentID;

      print('created customer successfully with id: ${customer.id}');

      await documentReference.setData(customer.toMap(), merge: true);
      addCustomer(customer);
    }
    notifyListeners();
  }

How do I proceed to read customer data from firebase only created by currentUser() since documentID/customer.id is now equals to userId fo the currentUser() logged in?


Here's what I've tried so far:

  getCustomers(CustomerNotifier customerNotifier) async {
String userId = (await FirebaseAuth.instance.currentUser()).uid;

print('Current logged in user uid is: $userId');

QuerySnapshot snapshot = await Firestore.instance
    .collection('customers')
    .where('id', isEqualTo: userId)
    .orderBy('created_at', descending: true)
    .getDocuments();

List<Customer> _customerList = [];

snapshot.documents.forEach((document) {
  Customer customer = Customer.fromMap(document.data);
  _customerList.add(customer);
});

customerNotifier.customerList = _customerList;
}

//customer_screen.dart //this uses a ListView.builder to display all customers created by currentUser()

class CustomersScreen extends StatefulWidget {
  static String id = 'customers';
    
  @override
  _CustomersScreenState createState() => _CustomersScreenState();
}
    
class _CustomersScreenState extends State<CustomersScreen> {

  bool showSpinner = true;
  bool _isInit = true;

  @override
  void initState() {
    if (_isInit) {
      showSpinner = true;
    } else {
      showSpinner = false;
    }
    CustomerNotifier customerNotifier =
        Provider.of<CustomerNotifier>(context, listen: false);
    customerNotifier.getCustomers(customerNotifier);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    CustomerNotifier customerNotifier = Provider.of<CustomerNotifier>(context);

    Future<void> _resfreshList() async {
      customerNotifier.getCustomers(customerNotifier);
    }

    return Scaffold(
      drawer: DrawerClass(),
      appBar: AppBar(
        title: Text(
          'All customers',
          style: kAppBarTextStyle,
        ),
        backgroundColor: kAppBarColour,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          customerNotifier.currentCustomer = null;
          Navigator.of(context)
              .push(MaterialPageRoute(builder: (BuildContext context) {
            return CustomerFormScreen(isUpdating: false);
          }));
        },
        child: Icon(Icons.add),
        backgroundColor: kThemeIconColour,
      ),
      // body: showSpinner
      //     ? Center(child: CircularProgressIndicator())
      body: RefreshIndicator(
        child: Consumer<CustomerNotifier>(
          builder: (context, customer, child) {
            return customer == null
                ? Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      PaddingClass(bodyImage: 'images/empty.png'),
                      SizedBox(
                        height: 20.0,
                      ),
                      Text(
                        'You don\'t have any customer',
                        style: kLabelTextStyle,
                      ),
                    ],
                  )
                : Padding(
                    padding: const EdgeInsets.only(top: 50.0),
                    child: ListView.separated(
                      itemBuilder: (context, int index) {
                        return Card(
                          margin: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 0.0),
                          elevation: 15.0,
                          color: Colors.white70,
                          child: Row(
                            crossAxisAlignment: CrossAxisAlignment.center,
                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                            children: <Widget>[
                              Container(
                                height: 100.0,
                                child: Icon(
                                  FontAwesomeIcons.userCircle,
                                  color: kThemeIconColour,
                                  size: 50.0,
                                ),
                              ),
                              SizedBox(width: 20.0),
                              Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: <Widget>[
                                  Text(' ${customer.customerList[index].firstName}' +
                                      '  ${customer.customerList[index].lastName}'),
                                  SizedBox(
                                    height: 8.0,
                                  ),
                                  Text(
                                      ' ${customer.customerList[index].phoneNumber}'),
                                  SizedBox(
                                    height: 8.0,
                                  ),
                                  Text(
                                      ' ${customer.customerList[index].email}'),
                                ],
                              ),
                              GestureDetector(
                                onTap: () {
                                  customerNotifier.currentCustomer =
                                      customerNotifier.customerList[index];
                                  Navigator.of(context).push(MaterialPageRoute(
                                      builder: (BuildContext context) {
                                    return CustomerDetailsScreen();
                                  }));
                                },
                                child: Icon(
                                  FontAwesomeIcons.caretDown,
                                  color: kThemeIconColour,
                                ),
                              ),
                            ],
                          ),
                        );
                      },
                      separatorBuilder: (BuildContext context, int index) {
                        return SizedBox(
                          height: 20.0,
                        );
                      },
                      itemCount: customerNotifier.customerList.length,
                    ),
                  );
          },
        ),
        onRefresh: _resfreshList,
      ),
    );
  }
}

Thanks.

1
can you post your current code you've tried to retrieve an individual customer's data? - Scott
I just posted what I've tried. - hermie_brown
What I'm seeing here is you have a collection called Users, and the documents within that collection are named by the uid. Are you looking to get just a single uid's information? If so, a documentSnapshot is what you're looking to use. But, your function looks like it is looking for a collection of customers -> that implies you might need a second collection so it looks like: customer (a collection) -> uid (a document) -> thisUsersItems (a collection) - Scott
I'm looking to read a collection of customers. So basically, all the customers created by the current logged in user. I have gone ahead to set my documentId for each customer document is same as currentUser() uid. I'm not sure I get the latter part of your last comment. Could you explain with a sample example / more explanation? Thanks. - hermie_brown
Okay, that makes sense! There's more than one way to do this, and you've selected to do this based on a value in the customer document. let me update my answer - Scott

1 Answers

1
votes

EDIT2: Check out the compound queries here: https://firebase.google.com/docs/firestore/query-data/queries

Specifically this section:

db.collection("cities").where("capital", "==", true)
    .get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            // doc.data() is never undefined for query doc snapshots
            console.log(doc.id, " => ", doc.data());
        });
    })
    .catch(function(error) {
        console.log("Error getting documents: ", error);
    });

You could use this structure. 'cities' is 'customer' for you, and "capital" is the field on your document that you've saved as userIdWhenYouCreatedThisCustomerDocument, and instead of true you'd put your current user id. Of course, this uses .then() and you could do that or assign the result to a variable using await.

I'll note that you should review the documentation as you work through this, particularly as it regards doing it this way vs. doing a subcollection by user ID. Either are correct, but if you go beyond a proof of concept you'll find the phrase "where clauses are not filters" in firestore to be a reason to consider the latter approach. Of course, that may not matter at all in the end.


EDIT: based on new information

Why are you using QuerySnapshot? You are (currently) retrieving a document snapshot for this, so you can use that directly.

Alternatively, can you post a screenshot of your firestore, with senstive data removed? I'm wondering if you are intending to store your data in the user document, like the immediate retrieval query code expects as-written and your upsert code delivers, as opposed to storing it in a collection within your user document. If the latter, the code from the other answer would probably work better for you and your current code since it is set up to read from a collection, not a document. Of course, either approach could work just as well. The problem is that right now your upsert and the first half of your query is doing the former and halfway through your retrieval query you switch to the latter.


I think you can combine the answer to your other question (Read data from Firebase created by loggedin user - Flutter) with the documentation to find your solution here.

This uses a more up to date version of firestore than you're using, but provides a good starting point. https://firebase.google.com/docs/firestore/query-data/get-data

For example.

    DocumentReference ref =
    Firestore.instance.collection('YourCollectionNameInFireStore').document(user.uid);
    var temp = await docRef.getDocument();
    // do something with the data if you want.. not sure of syntax for old version on 
    // this, for new version it is .data()
    temp.data.<field>