Home > Software engineering >  How to make a user read a collection of all documents where documents.uid == user.uid in firebase fl
How to make a user read a collection of all documents where documents.uid == user.uid in firebase fl

Time:06-28

Basically I have 2 collections 'Bookings' and 'Users'. The 'Bookings' collection contains all bookings created by every user, and the 'Users' collection displays information about the user.

User: {
    name:
    uid:
}

Bookings: {
   location:
   time:
   uid:
   etc:
}

I have a GetBookings() function that retrieves the 'Bookings' collection and display it for an admin account. However, I am currently stuck on how to approach displaying a user his bookings.

getBookings() {
  var bookings = FirebaseFirestore.instance.collection('bookings');
  return bookings.get();
}

I thought about creating another 'Bookings' collection under each user but am unsure on how to link this new 'Bookings' collection with the previous collection in order to preserve the same bookings id. I had a go with security rules as mentioned by @Renaud Tarnec, however I might be getting the syntax wrong, or during looping through the bookings collection and receiving a permission denied on our request it preemptively stops my fetchBookings() function, or a user might be able to access the entire 'Bookings' collection regardless of whether each booking has his uid or not.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    
    // Allows users to view their bookings
    match /bookings/{booking} {
        allow read: if request.auth != null && request.auth.uid == booking.uid;
        allow write: if true;
    }
  }
}
Future<List<BookingModel>> fetchBookings() async {
    var bookings = await _bookingRepository.fetchAllBookings();

    return bookings.map((snapshot) {
      var bookingMap = snapshot.data();
      return BookingModel(bookingMap['email'], bookingMap['location'], bookingMap['phoneNumber'],
          bookingMap['dateTime'], bookingMap['uid'], bookingMap['dateCreated']);
    }).toList();
  }

I'd like to know what would be professional/industrially accepted way in tackling this problem.

CodePudding user response:

It would be better if: While you adding the booking data to the "Booking" collection, you also need to add it also to the user.booking collection.

CodePudding user response:

Since the bookings collection can only be accessed by an admin account, a classical solution in your case (denormalization in a NoSQL Database) is to use a Cloud Function to create the Booking document in the users/{userID}/bookings subcollection when a new Booking is created in the bookings collection.

Something along the following lines:

exports.duplicateBooking = functions
    .firestore
    .document('bookings/{docId}')
    .onCreate((snap, context) => {

        const userId = ....; // Not clear from your question how you define that. You should probably add it to the booking doc.

        const bookingData = snap.data();

        return admin
            .firestore()
            .collection(`users/${userId}/bookings)
            .add({
                 'location': bookingData.location,
                 'time': bookingData.time, 
                 'email': bookingData.email,
                 'phoneNumber': bookingData.phoneNumber
          });

    });

Another possibilities would be to keep a unique bookings collection with a set of Security Rules that allows a user to read his own bookings. In this case, remember that rules are not filters when you write the corresponding query.

CodePudding user response:

Like I said, in my opinion, the best solution for you is to set correct rules in database and create correct queries to get that data.

Rules:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
    match /bookings/{docId} {
      allow read: if resource.data.uid == request.auth.uid || isAdmin()
      // bellow you can use second part after && but im not sure are it will be null or unassigned this is overenginered so you can just not use condition after &&.
      allow update: if resource.data.uid == request.auth.uid && request.resource.data.uid == null || isAdmin()
      allow create: if request.auth != null && request.resource.data.uid == request.auth.uid || isAdmin()
      allow delete: if isAdmin()
    }
  }
}

function isAdmin() {
    return request.auth.token.admin == true;
}

Queries you need to make for users:

getBookings() {
  // Im not sure are it will work like that in flutter im not a flutter programmer.
  // You need to specify using where() method that you want documents with your uid or rules will not allow you to get eny data.
  var bookings = FirebaseFirestore.instance.collection('bookings').where('uid', '==', 'user.uid');
  return bookings.get();
}
  • Related