1. Übersicht
Ziele
In diesem Codelab erstellen Sie eine von Cloud Firestore unterstützte App für Restaurantempfehlungen auf Android. Du wirst lernen wie:
- Lesen und schreiben Sie Daten von einer Android-App in Firestore
- Hören Sie sich Änderungen in Firestore-Daten in Echtzeit an
- Verwenden Sie die Firebase-Authentifizierung und Sicherheitsregeln, um Firestore-Daten zu sichern
- Schreiben Sie komplexe Firestore-Abfragen
Voraussetzungen
Bevor Sie mit diesem Codelab beginnen, stellen Sie sicher, dass Sie Folgendes haben:
- Android Studio 4.0 oder höher
- Ein Android-Emulator mit API 19 oder höher
- Node.js-Version 10 oder höher
- Java-Version 8 oder höher
2. Erstellen Sie ein Firebase-Projekt
- Melden Sie sich mit Ihrem Google-Konto bei der Firebase-Konsole an.
- Klicken Sie in der Firebase-Konsole auf Projekt hinzufügen .
- Geben Sie, wie im Screenshot unten gezeigt, einen Namen für Ihr Firebase-Projekt ein (z. B. „Friendly Eats“) und klicken Sie auf Weiter .
- Möglicherweise werden Sie aufgefordert, Google Analytics zu aktivieren. Für die Zwecke dieses Codelabs spielt Ihre Auswahl keine Rolle.
- Nach ungefähr einer Minute ist Ihr Firebase-Projekt fertig. Klicken Sie auf Weiter .
3. Richten Sie das Beispielprojekt ein
Laden Sie den Code herunter
Führen Sie den folgenden Befehl aus, um den Beispielcode für dieses Codelab zu klonen. Dadurch wird auf Ihrem Computer ein Ordner namens friendlyeats-android
erstellt:
$ git clone https://github.com/firebase/friendlyeats-android
Wenn Sie Git nicht auf Ihrem Computer haben, können Sie den Code auch direkt von GitHub herunterladen.
Firebase-Konfiguration hinzufügen
- Wählen Sie in der Firebase-Konsole im linken Navigationsbereich Projektübersicht aus. Klicken Sie auf die Android- Schaltfläche, um die Plattform auszuwählen. Wenn Sie zur Eingabe eines Paketnamens aufgefordert werden, verwenden Sie
com.google.firebase.example.fireeats
- Klicken Sie auf App registrieren und befolgen Sie die Anweisungen zum Herunterladen der Datei
google-services.json
und verschieben Sie sie in den Ordnerapp/
des gerade heruntergeladenen Codes. Klicken Sie dann auf Weiter .
Importieren Sie das Projekt
Öffnen Sie Android Studio. Klicken Sie auf Datei > Neu > Projekt importieren und wählen Sie den Ordner friendlyeats-android aus.
4. Richten Sie die Firebase-Emulatoren ein
In diesem Codelab verwenden Sie die Firebase Emulator Suite , um Cloud Firestore und andere Firebase-Dienste lokal zu emulieren. Dies bietet eine sichere, schnelle und kostenlose lokale Entwicklungsumgebung zum Erstellen Ihrer App.
Installieren Sie die Firebase-CLI
Zuerst müssen Sie die Firebase CLI installieren. Wenn Sie macOS oder Linux verwenden, können Sie den folgenden cURL-Befehl ausführen:
curl -sL https://firebase.tools | bash
Wenn Sie Windows verwenden, lesen Sie die Installationsanweisungen , um eine eigenständige Binärdatei zu erhalten oder über npm
zu installieren.
Sobald Sie die CLI installiert haben, sollte das Ausführen von firebase --version
eine Version von 9.0.0
oder höher melden:
$ firebase --version 9.0.0
Anmeldung
Führen Sie firebase login
aus, um die CLI mit Ihrem Google-Konto zu verbinden. Dadurch wird ein neues Browserfenster geöffnet, um den Anmeldevorgang abzuschließen. Stellen Sie sicher, dass Sie dasselbe Konto auswählen, das Sie zuvor beim Erstellen Ihres Firebase-Projekts verwendet haben.
Verlinken Sie Ihr Projekt
Führen Sie im Ordner friendlyeats-android
firebase use --add
um Ihr lokales Projekt mit Ihrem Firebase-Projekt zu verbinden. Folgen Sie den Eingabeaufforderungen, um das zuvor erstellte Projekt auszuwählen, und geben Sie default
ein, wenn Sie aufgefordert werden, einen Alias auszuwählen.
5. Führen Sie die App aus
Jetzt ist es an der Zeit, die Firebase Emulator Suite und die FriendlyEats Android App zum ersten Mal auszuführen.
Führen Sie die Emulatoren aus
Führen Sie in Ihrem Terminal im Verzeichnis friendlyeats-android
firebase emulators:start
um die Firebase-Emulatoren zu starten. Sie sollten Protokolle wie diese sehen:
$ firebase emulators:start i emulators: Starting emulators: auth, firestore i firestore: Firestore Emulator logging to firestore-debug.log i ui: Emulator UI logging to ui-debug.log ┌─────────────────────────────────────────────────────────────┐ │ ✔ All emulators ready! It is now safe to connect your app. │ │ i View Emulator UI at http://localhost:4000 │ └─────────────────────────────────────────────────────────────┘ ┌────────────────┬────────────────┬─────────────────────────────────┐ │ Emulator │ Host:Port │ View in Emulator UI │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Authentication │ localhost:9099 │ http://localhost:4000/auth │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Firestore │ localhost:8080 │ http://localhost:4000/firestore │ └────────────────┴────────────────┴─────────────────────────────────┘ Emulator Hub running at localhost:4400 Other reserved ports: 4500 Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
Sie haben jetzt eine vollständige lokale Entwicklungsumgebung, die auf Ihrem Computer ausgeführt wird! Stellen Sie sicher, dass dieser Befehl für den Rest des Codelabs ausgeführt wird, Ihre Android-App muss sich mit den Emulatoren verbinden.
Verbinden Sie die App mit den Emulatoren
Öffnen Sie die Dateien util/FirestoreInitializer.kt
und util/AuthInitializer.kt
in Android Studio. Diese Dateien enthalten die Logik, um die Firebase-SDKs beim Start der Anwendung mit den lokalen Emulatoren zu verbinden, die auf Ihrem Computer ausgeführt werden.
Untersuchen Sie in der create()
Methode der FirestoreInitializer
Klasse diesen Codeabschnitt:
// Use emulators only in debug builds
if (BuildConfig.DEBUG) {
firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
}
Wir verwenden BuildConfig
um sicherzustellen, dass wir nur dann eine Verbindung zu den Emulatoren herstellen, wenn unsere App im debug
ausgeführt wird. Wenn wir die App im release
Modus kompilieren, ist diese Bedingung falsch.
Wir können sehen, dass es die Methode useEmulator(host, port)
verwendet, um das Firebase SDK mit dem lokalen Firestore-Emulator zu verbinden. In der gesamten App werden wir FirebaseUtil.getFirestore()
verwenden, um auf diese Instanz von FirebaseFirestore
zuzugreifen, damit wir sicher sein können, dass wir immer eine Verbindung zum Firestore-Emulator herstellen, wenn wir im debug
Modus laufen.
Führen Sie die App aus
Wenn Sie die Datei google-services.json
ordnungsgemäß hinzugefügt haben, sollte das Projekt jetzt kompiliert werden. Klicken Sie in Android Studio auf Build > Rebuild Project und stellen Sie sicher, dass keine verbleibenden Fehler vorhanden sind.
In Android Studio Führen Sie die App auf Ihrem Android-Emulator aus. Zunächst wird Ihnen ein „Anmelden“-Bildschirm angezeigt. Sie können eine beliebige E-Mail-Adresse und ein beliebiges Passwort verwenden, um sich bei der App anzumelden. Dieser Anmeldevorgang stellt eine Verbindung zum Firebase Authentication-Emulator her, sodass keine echten Anmeldeinformationen übertragen werden.
Öffnen Sie nun die Benutzeroberfläche der Emulatoren, indem Sie in Ihrem Webbrowser zu http://localhost:4000 navigieren. Klicken Sie dann auf die Registerkarte Authentifizierung und Sie sollten das Konto sehen, das Sie gerade erstellt haben:
Sobald Sie den Anmeldevorgang abgeschlossen haben, sollten Sie den Startbildschirm der App sehen:
Bald werden wir einige Daten hinzufügen, um den Startbildschirm zu füllen.
6. Daten in Firestore schreiben
In diesem Abschnitt werden wir einige Daten in Firestore schreiben, damit wir den derzeit leeren Startbildschirm füllen können.
Das Hauptmodellobjekt in unserer App ist ein Restaurant (siehe model/Restaurant.kt
). Firestore-Daten werden in Dokumente, Sammlungen und Untersammlungen aufgeteilt. Wir speichern jedes Restaurant als Dokument in einer Sammlung der obersten Ebene namens "restaurants"
. Weitere Informationen zum Firestore-Datenmodell finden Sie unter Dokumente und Sammlungen in der Dokumentation .
Zu Demonstrationszwecken werden wir der App Funktionen hinzufügen, um zehn zufällige Restaurants zu erstellen, wenn wir im Überlaufmenü auf die Schaltfläche „Zufällige Elemente hinzufügen“ klicken. Öffnen Sie die Datei MainFragment.kt
und ersetzen Sie den Inhalt in der Methode onAddItemsClicked()
durch:
private fun onAddItemsClicked() {
val restaurantsRef = firestore.collection("restaurants")
for (i in 0..9) {
// Create random restaurant / ratings
val randomRestaurant = RestaurantUtil.getRandom(requireContext())
// Add restaurant
restaurantsRef.add(randomRestaurant)
}
}
Es gibt ein paar wichtige Dinge, die zum obigen Code zu beachten sind:
- Wir begannen damit, einen Verweis auf die Sammlung
"restaurants"
zu erhalten. Sammlungen werden implizit erstellt, wenn Dokumente hinzugefügt werden, sodass die Sammlung vor dem Schreiben von Daten nicht erstellt werden musste. - Dokumente können mithilfe von Kotlin-Datenklassen erstellt werden, die wir zum Erstellen jedes Restaurantdokuments verwenden.
- Die Methode
add()
fügt ein Dokument mit einer automatisch generierten ID zu einer Sammlung hinzu, sodass wir nicht für jedes Restaurant eine eindeutige ID angeben mussten.
Führen Sie die App jetzt erneut aus und klicken Sie im Überlaufmenü (in der oberen rechten Ecke) auf die Schaltfläche "Zufällige Elemente hinzufügen", um den gerade geschriebenen Code aufzurufen:
Öffnen Sie nun die Benutzeroberfläche der Emulatoren, indem Sie in Ihrem Webbrowser zu http://localhost:4000 navigieren. Klicken Sie dann auf die Registerkarte Firestore und Sie sollten die gerade hinzugefügten Daten sehen:
Diese Daten sind zu 100 % lokal auf Ihrem Computer. Tatsächlich enthält Ihr echtes Projekt noch nicht einmal eine Firestore-Datenbank! Dies bedeutet, dass Sie ohne Bedenken mit dem Ändern und Löschen dieser Daten experimentieren können.
Herzlichen Glückwunsch, Sie haben gerade Daten in Firestore geschrieben! Im nächsten Schritt erfahren Sie, wie Sie diese Daten in der App anzeigen können.
7. Zeigen Sie Daten von Firestore an
In diesem Schritt erfahren Sie, wie Sie Daten aus Firestore abrufen und in unserer App anzeigen. Der erste Schritt zum Lesen von Daten aus Firestore besteht darin, eine Query
zu erstellen. Öffnen Sie die Datei MainFragment.kt
und fügen Sie den folgenden Code am Anfang der Methode onViewCreated()
hinzu:
// Firestore
firestore = Firebase.firestore
// Get the 50 highest rated restaurants
query = firestore.collection("restaurants")
.orderBy("avgRating", Query.Direction.DESCENDING)
.limit(LIMIT.toLong())
Jetzt wollen wir die Abfrage abhören, damit wir alle passenden Dokumente bekommen und in Echtzeit über zukünftige Updates benachrichtigt werden. Da unser letztendliches Ziel darin besteht, diese Daten an eine RecyclerView
zu binden, müssen wir eine RecyclerView.Adapter
Klasse erstellen, um auf die Daten zu hören.
Öffnen Sie die FirestoreAdapter
Klasse, die bereits teilweise implementiert wurde. Lassen Sie uns zunächst den Adapter EventListener
implementieren und die onEvent
Funktion definieren, damit sie Aktualisierungen einer Firestore-Abfrage empfangen kann:
abstract class FirestoreAdapter<VH : RecyclerView.ViewHolder>(private var query: Query?) :
RecyclerView.Adapter<VH>(),
EventListener<QuerySnapshot> { // Add this implements
// ...
// Add this method
override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {
// Handle errors
if (e != null) {
Log.w(TAG, "onEvent:error", e)
return
}
// Dispatch the event
if (documentSnapshots != null) {
for (change in documentSnapshots.documentChanges) {
// snapshot of the changed document
when (change.type) {
DocumentChange.Type.ADDED -> {
// TODO: handle document added
}
DocumentChange.Type.MODIFIED -> {
// TODO: handle document changed
}
DocumentChange.Type.REMOVED -> {
// TODO: handle document removed
}
}
}
}
onDataChanged()
}
// ...
}
Beim anfänglichen Laden erhält der Listener ein ADDED
Ereignis für jedes neue Dokument. Da sich die Ergebnismenge der Abfrage im Laufe der Zeit ändert, erhält der Listener mehr Ereignisse, die die Änderungen enthalten. Lassen Sie uns nun die Implementierung des Listeners abschließen. Fügen Sie zunächst drei neue Methoden hinzu: onDocumentAdded
, onDocumentModified
und onDocumentRemoved
:
private fun onDocumentAdded(change: DocumentChange) {
snapshots.add(change.newIndex, change.document)
notifyItemInserted(change.newIndex)
}
private fun onDocumentModified(change: DocumentChange) {
if (change.oldIndex == change.newIndex) {
// Item changed but remained in same position
snapshots[change.oldIndex] = change.document
notifyItemChanged(change.oldIndex)
} else {
// Item changed and changed position
snapshots.removeAt(change.oldIndex)
snapshots.add(change.newIndex, change.document)
notifyItemMoved(change.oldIndex, change.newIndex)
}
}
private fun onDocumentRemoved(change: DocumentChange) {
snapshots.removeAt(change.oldIndex)
notifyItemRemoved(change.oldIndex)
}
Rufen Sie dann diese neuen Methoden von onEvent
auf:
override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {
// Handle errors
if (e != null) {
Log.w(TAG, "onEvent:error", e)
return
}
// Dispatch the event
if (documentSnapshots != null) {
for (change in documentSnapshots.documentChanges) {
// snapshot of the changed document
when (change.type) {
DocumentChange.Type.ADDED -> {
onDocumentAdded(change) // Add this line
}
DocumentChange.Type.MODIFIED -> {
onDocumentModified(change) // Add this line
}
DocumentChange.Type.REMOVED -> {
onDocumentRemoved(change) // Add this line
}
}
}
}
onDataChanged()
}
Implementieren Sie schließlich die Methode startListening()
, um den Listener anzuhängen:
fun startListening() {
if (registration == null) {
registration = query.addSnapshotListener(this)
}
}
Jetzt ist die App vollständig konfiguriert, um Daten aus Firestore zu lesen. Führen Sie die App erneut aus und Sie sollten die Restaurants sehen, die Sie im vorherigen Schritt hinzugefügt haben:
Gehen Sie nun zurück zur Emulator-Benutzeroberfläche in Ihrem Browser und bearbeiten Sie einen der Restaurantnamen. Sie sollten sehen, dass es sich fast sofort in der App ändert!
8. Daten sortieren und filtern
Die App zeigt derzeit die am besten bewerteten Restaurants in der gesamten Sammlung an, aber in einer echten Restaurant-App würde der Benutzer die Daten sortieren und filtern wollen. So soll die App zum Beispiel „Beste Fischrestaurants in Philadelphia“ oder „Günstigste Pizza“ anzeigen können.
Durch Klicken auf den weißen Balken oben in der App wird ein Filterdialogfeld angezeigt. In diesem Abschnitt verwenden wir Firestore-Abfragen, damit dieses Dialogfeld funktioniert:
Bearbeiten wir die Methode onFilter()
von MainFragment.kt
. Diese Methode akzeptiert ein Filters
Objekt, bei dem es sich um ein Hilfsobjekt handelt, das wir erstellt haben, um die Ausgabe des Filterdialogfelds zu erfassen. Wir werden diese Methode ändern, um eine Abfrage aus den Filtern zu erstellen:
override fun onFilter(filters: Filters) {
// Construct query basic query
var query: Query = firestore.collection("restaurants")
// Category (equality filter)
if (filters.hasCategory()) {
query = query.whereEqualTo(Restaurant.FIELD_CATEGORY, filters.category)
}
// City (equality filter)
if (filters.hasCity()) {
query = query.whereEqualTo(Restaurant.FIELD_CITY, filters.city)
}
// Price (equality filter)
if (filters.hasPrice()) {
query = query.whereEqualTo(Restaurant.FIELD_PRICE, filters.price)
}
// Sort by (orderBy with direction)
if (filters.hasSortBy()) {
query = query.orderBy(filters.sortBy.toString(), filters.sortDirection)
}
// Limit items
query = query.limit(LIMIT.toLong())
// Update the query
adapter.setQuery(query)
// Set header
binding.textCurrentSearch.text = HtmlCompat.fromHtml(
filters.getSearchDescription(requireContext()),
HtmlCompat.FROM_HTML_MODE_LEGACY
)
binding.textCurrentSortBy.text = filters.getOrderDescription(requireContext())
// Save filters
viewModel.filters = filters
}
Im obigen Snippet erstellen wir ein Query
Objekt, indem wir where
und orderBy
Klauseln anhängen, um den angegebenen Filtern zu entsprechen.
Führen Sie die App erneut aus und wählen Sie den folgenden Filter aus, um die beliebtesten Niedrigpreisrestaurants anzuzeigen:
Sie sollten jetzt eine gefilterte Liste von Restaurants sehen, die nur Niedrigpreisoptionen enthält:
Wenn Sie es bis hierher geschafft haben, haben Sie jetzt eine voll funktionsfähige App zum Anzeigen von Restaurantempfehlungen auf Firestore erstellt! Sie können jetzt Restaurants in Echtzeit sortieren und filtern. In den nächsten Abschnitten werden wir Bewertungen zu den Restaurants hinzufügen und Sicherheitsregeln zur App hinzufügen.
9. Organisieren Sie Daten in Untersammlungen
In diesem Abschnitt fügen wir der App Bewertungen hinzu, damit Benutzer ihre bevorzugten (oder am wenigsten bevorzugten) Restaurants bewerten können.
Sammlungen und Untersammlungen
Bisher haben wir alle Restaurantdaten in einer übergeordneten Sammlung namens "Restaurants" gespeichert. Wenn ein Benutzer ein Restaurant bewertet, möchten wir den Restaurants ein neues Rating
hinzufügen. Für diese Aufgabe verwenden wir eine Untersammlung. Sie können sich eine Untersammlung als eine Sammlung vorstellen, die an ein Dokument angehängt ist. Jedes Restaurantdokument hat also eine Bewertungsuntersammlung voller Bewertungsdokumente. Untersammlungen helfen bei der Organisation von Daten, ohne unsere Dokumente aufzublähen oder komplexe Abfragen zu erfordern.
Um auf eine Untersammlung zuzugreifen, rufen Sie .collection()
im übergeordneten Dokument auf:
val subRef = firestore.collection("restaurants")
.document("abc123")
.collection("ratings")
Sie können wie bei einer Sammlung der obersten Ebene auf eine Untersammlung zugreifen und diese abfragen, es gibt keine Größenbeschränkungen oder Leistungsänderungen. Weitere Informationen zum Firestore-Datenmodell finden Sie hier .
Schreiben von Daten in eine Transaktion
Das Hinzufügen einer Rating
zur richtigen Untersammlung erfordert nur den Aufruf von .add()
, aber wir müssen auch die durchschnittliche Bewertung und die Anzahl der Bewertungen des Restaurant
Objekts aktualisieren, um die neuen Daten widerzuspiegeln. Wenn wir separate Vorgänge verwenden, um diese beiden Änderungen vorzunehmen, gibt es eine Reihe von Rennbedingungen, die zu veralteten oder falschen Daten führen können.
Um sicherzustellen, dass Bewertungen ordnungsgemäß hinzugefügt werden, verwenden wir eine Transaktion, um Bewertungen zu einem Restaurant hinzuzufügen. Diese Transaktion führt einige Aktionen aus:
- Lesen Sie die aktuelle Bewertung des Restaurants und berechnen Sie die neue
- Fügen Sie die Bewertung der Untersammlung hinzu
- Aktualisieren Sie die durchschnittliche Bewertung und die Anzahl der Bewertungen des Restaurants
Öffnen Sie RestaurantDetailFragment.kt
und implementieren Sie die Funktion addRating
:
private fun addRating(restaurantRef: DocumentReference, rating: Rating): Task<Void> {
// Create reference for new rating, for use inside the transaction
val ratingRef = restaurantRef.collection("ratings").document()
// In a transaction, add the new rating and update the aggregate totals
return firestore.runTransaction { transaction ->
val restaurant = transaction.get(restaurantRef).toObject<Restaurant>()
?: throw Exception("Restaurant not found at ${restaurantRef.path}")
// Compute new number of ratings
val newNumRatings = restaurant.numRatings + 1
// Compute new average rating
val oldRatingTotal = restaurant.avgRating * restaurant.numRatings
val newAvgRating = (oldRatingTotal + rating.rating) / newNumRatings
// Set new restaurant info
restaurant.numRatings = newNumRatings
restaurant.avgRating = newAvgRating
// Commit to Firestore
transaction.set(restaurantRef, restaurant)
transaction.set(ratingRef, rating)
null
}
}
Die Funktion addRating()
gibt eine Task
zurück, die die gesamte Transaktion darstellt. In der Funktion onRating()
werden der Aufgabe Listener hinzugefügt, um auf das Ergebnis der Transaktion zu antworten.
Führen Sie nun die App erneut aus und klicken Sie auf eines der Restaurants, wodurch der Restaurant-Detailbildschirm angezeigt werden sollte. Klicken Sie auf die Schaltfläche + , um mit dem Hinzufügen einer Bewertung zu beginnen. Fügen Sie eine Bewertung hinzu, indem Sie eine Reihe von Sternen auswählen und Text eingeben.
Wenn Sie auf Senden klicken, wird die Transaktion gestartet. Wenn die Transaktion abgeschlossen ist, sehen Sie unten Ihre Bewertung und eine Aktualisierung der Bewertungsanzahl des Restaurants:
Herzlichen Glückwunsch! Sie haben jetzt eine soziale, lokale, mobile Restaurantbewertungs-App, die auf Cloud Firestore basiert. Ich habe gehört, dass diese heutzutage sehr beliebt sind.
10. Sichern Sie Ihre Daten
Bisher haben wir die Sicherheit dieser Anwendung nicht berücksichtigt. Woher wissen wir, dass Benutzer nur die richtigen eigenen Daten lesen und schreiben können? Firestore-Datenbanken werden durch eine Konfigurationsdatei namens Security Rules gesichert.
Öffnen Sie die Datei firestore.rules
, Sie sollten Folgendes sehen:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
//
// WARNING: These rules are insecure! We will replace them with
// more secure rules later in the codelab
//
allow read, write: if request.auth != null;
}
}
}
Lassen Sie uns diese Regeln ändern, um unerwünschte Datenzugriffe oder -änderungen zu verhindern, öffnen Sie die Datei firestore.rules
und ersetzen Sie den Inhalt durch Folgendes:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Determine if the value of the field "key" is the same
// before and after the request.
function isUnchanged(key) {
return (key in resource.data)
&& (key in request.resource.data)
&& (resource.data[key] == request.resource.data[key]);
}
// Restaurants
match /restaurants/{restaurantId} {
// Any signed-in user can read
allow read: if request.auth != null;
// Any signed-in user can create
// WARNING: this rule is for demo purposes only!
allow create: if request.auth != null;
// Updates are allowed if no fields are added and name is unchanged
allow update: if request.auth != null
&& (request.resource.data.keys() == resource.data.keys())
&& isUnchanged("name");
// Deletes are not allowed.
// Note: this is the default, there is no need to explicitly state this.
allow delete: if false;
// Ratings
match /ratings/{ratingId} {
// Any signed-in user can read
allow read: if request.auth != null;
// Any signed-in user can create if their uid matches the document
allow create: if request.auth != null
&& request.resource.data.userId == request.auth.uid;
// Deletes and updates are not allowed (default)
allow update, delete: if false;
}
}
}
}
Diese Regeln schränken den Zugriff ein, um sicherzustellen, dass Clients nur sichere Änderungen vornehmen. Beispielsweise können Aktualisierungen eines Restaurantdokuments nur die Bewertungen ändern, nicht den Namen oder andere unveränderliche Daten. Bewertungen können nur erstellt werden, wenn die Benutzer-ID mit dem angemeldeten Benutzer übereinstimmt, wodurch Spoofing verhindert wird.
Weitere Informationen zu Sicherheitsregeln finden Sie in der Dokumentation .
11. Fazit
Sie haben jetzt eine voll funktionsfähige App auf Firestore erstellt. Sie haben die wichtigsten Firestore-Funktionen kennengelernt, darunter:
- Dokumente und Sammlungen
- Lesen und Schreiben von Daten
- Sortieren und Filtern mit Abfragen
- Untersammlungen
- Transaktionen
Erfahren Sie mehr
Hier sind einige gute Einstiegspunkte, um sich weiter mit Firestore vertraut zu machen:
Die Restaurant-App in diesem Codelab basierte auf der Beispielanwendung „Friendly Eats“. Sie können den Quellcode für diese App hier durchsuchen.
Optional: In der Produktion bereitstellen
Bisher hat diese App nur die Firebase Emulator Suite verwendet. Wenn Sie erfahren möchten, wie Sie diese App in einem echten Firebase-Projekt bereitstellen, fahren Sie mit dem nächsten Schritt fort.
12. (Optional) Stellen Sie Ihre App bereit
Bisher war diese App vollständig lokal, alle Daten sind in der Firebase Emulator Suite enthalten. In diesem Abschnitt erfahren Sie, wie Sie Ihr Firebase-Projekt konfigurieren, damit diese App in der Produktion funktioniert.
Firebase-Authentifizierung
Gehen Sie in der Firebase-Konsole zum Abschnitt Authentifizierung und klicken Sie auf Erste Schritte . Navigieren Sie zur Registerkarte Anmeldemethode und wählen Sie unter Native Anbieter die Option E-Mail/Passwort aus.
Aktivieren Sie die Anmeldemethode E-Mail/Passwort und klicken Sie auf Speichern .
Feuerwehr
Datenbank erstellen
Navigieren Sie zum Abschnitt Firestore Database der Konsole und klicken Sie auf Create Database :
- Wenn Sie zu den Sicherheitsregeln aufgefordert werden, im Produktionsmodus zu starten , werden wir diese Regeln bald aktualisieren.
- Wählen Sie den Datenbankspeicherort aus, den Sie für Ihre App verwenden möchten. Beachten Sie, dass die Auswahl eines Datenbankspeicherorts eine dauerhafte Entscheidung ist und dass Sie ein neues Projekt erstellen müssen, um ihn zu ändern. Weitere Informationen zur Auswahl eines Projektstandorts finden Sie in der Dokumentation .
Regeln bereitstellen
Um die zuvor geschriebenen Sicherheitsregeln bereitzustellen, führen Sie den folgenden Befehl im Codelab-Verzeichnis aus:
$ firebase deploy --only firestore:rules
Dadurch wird der Inhalt von firestore.rules
in Ihrem Projekt bereitgestellt, was Sie bestätigen können, indem Sie in der Konsole zur Registerkarte „Regeln“ navigieren.
Indizes bereitstellen
Die FriendlyEats-App verfügt über eine komplexe Sortierung und Filterung, die eine Reihe von benutzerdefinierten zusammengesetzten Indizes erfordert. Diese können manuell in der Firebase-Konsole erstellt werden, aber es ist einfacher, ihre Definitionen in die Datei firestore.indexes.json
zu schreiben und sie mit der Firebase-CLI bereitzustellen.
Wenn Sie die Datei firestore.indexes.json
öffnen, sehen Sie, dass die erforderlichen Indizes bereits bereitgestellt wurden:
{
"indexes": [
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "city", "mode": "ASCENDING" },
{ "fieldPath": "avgRating", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "category", "mode": "ASCENDING" },
{ "fieldPath": "avgRating", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "price", "mode": "ASCENDING" },
{ "fieldPath": "avgRating", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "city", "mode": "ASCENDING" },
{ "fieldPath": "numRatings", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "category", "mode": "ASCENDING" },
{ "fieldPath": "numRatings", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "price", "mode": "ASCENDING" },
{ "fieldPath": "numRatings", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "city", "mode": "ASCENDING" },
{ "fieldPath": "price", "mode": "ASCENDING" }
]
},
{
"collectionId": "restaurants",
"fields": [
{ "fieldPath": "category", "mode": "ASCENDING" },
{ "fieldPath": "price", "mode": "ASCENDING" }
]
}
],
"fieldOverrides": []
}
Um diese Indizes bereitzustellen, führen Sie den folgenden Befehl aus:
$ firebase deploy --only firestore:indexes
Beachten Sie, dass die Indexerstellung nicht sofort erfolgt. Sie können den Fortschritt in der Firebase-Konsole überwachen.
Konfigurieren Sie die App
In den Dateien util/FirestoreInitializer.kt
und util/AuthInitializer.kt
haben wir das Firebase SDK so konfiguriert, dass es im Debug-Modus eine Verbindung zu den Emulatoren herstellt:
override fun create(context: Context): FirebaseFirestore {
val firestore = Firebase.firestore
// Use emulators only in debug builds
if (BuildConfig.DEBUG) {
firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
}
return firestore
}
Wenn Sie Ihre App mit Ihrem echten Firebase-Projekt testen möchten, können Sie entweder:
- Erstellen Sie die App im Release-Modus und führen Sie sie auf einem Gerät aus.
- Ersetzen Sie
BuildConfig.DEBUG
vorübergehend durchfalse
und führen Sie die App erneut aus.
Beachten Sie, dass Sie sich möglicherweise von der App abmelden und erneut anmelden müssen, um eine ordnungsgemäße Verbindung zur Produktion herzustellen.