1. Przegląd
Cele
W tym laboratorium kodowania utworzysz aplikację internetową z rekomendacjami restauracji obsługiwaną przez Cloud Firestore .
Czego się nauczysz
- Odczytuj i zapisuj dane w Cloud Firestore z aplikacji internetowej
- Słuchaj zmian w danych Cloud Firestore w czasie rzeczywistym
- Użyj uwierzytelniania Firebase i reguł bezpieczeństwa, aby zabezpieczyć dane Cloud Firestore
- Pisz złożone zapytania Cloud Firestore
Co będziesz potrzebował
Przed rozpoczęciem tego modułu CodeLab upewnij się, że masz zainstalowane:
2. Utwórz i skonfiguruj projekt Firebase
Utwórz projekt Firebase
- W konsoli Firebase kliknij Dodaj projekt , a następnie nazwij projekt Firebase FriendlyEats .
Zapamiętaj identyfikator projektu dla swojego projektu Firebase.
- Kliknij Utwórz projekt .
Aplikacja, którą zamierzamy zbudować, korzysta z kilku usług Firebase dostępnych w sieci:
- Uwierzytelnianie Firebase w celu łatwej identyfikacji użytkowników
- Cloud Firestore do zapisywania uporządkowanych danych w chmurze i otrzymywania natychmiastowych powiadomień, gdy dane zostaną zaktualizowane
- Hosting Firebase do hostowania i udostępniania zasobów statycznych
Na potrzeby tego konkretnego laboratorium kodu skonfigurowaliśmy już Hosting Firebase. Jednak w przypadku Firebase Auth i Cloud Firestore przeprowadzimy Cię przez proces konfiguracji i włączania usług przy użyciu konsoli Firebase.
Włącz anonimową autoryzację
Chociaż uwierzytelnianie nie jest głównym tematem tego laboratorium kodowania, ważne jest, aby mieć jakąś formę uwierzytelniania w naszej aplikacji. Będziemy używać logowania anonimowego , co oznacza, że użytkownik będzie logował się po cichu bez monitu.
Musisz włączyć anonimowe logowanie.
- W konsoli Firebase znajdź sekcję Kompilacja w lewym panelu nawigacyjnym.
- Kliknij Uwierzytelnianie , a następnie kliknij kartę Metoda logowania (lub kliknij tutaj , aby przejść bezpośrednio do tej karty).
- Włącz dostawcę logowania anonimowego , a następnie kliknij przycisk Zapisz .
Umożliwi to aplikacji dyskretne logowanie użytkowników, gdy uzyskują oni dostęp do aplikacji internetowej. Zachęcamy do zapoznania się z dokumentacją uwierzytelniania anonimowego, aby dowiedzieć się więcej.
Włącz Cloud Firestore
Aplikacja korzysta z Cloud Firestore do zapisywania i odbierania informacji o restauracjach oraz ocen.
Musisz włączyć Cloud Firestore. W sekcji Kompilacja konsoli Firebase kliknij opcję Baza danych Firestore . Kliknij Utwórz bazę danych w okienku Cloud Firestore.
Dostęp do danych w Cloud Firestore jest kontrolowany przez Reguły bezpieczeństwa. Porozmawiamy więcej o regułach później w tym laboratorium kodowania, ale najpierw musimy ustawić kilka podstawowych reguł dotyczących naszych danych, aby rozpocząć. Na karcie Reguły konsoli Firebase dodaj następujące reguły, a następnie kliknij Publikuj .
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; } } }
Powyższe reguły ograniczają dostęp do danych do zalogowanych użytkowników, co uniemożliwia nieuwierzytelnionym użytkownikom odczytywanie i zapisywanie. Jest to lepsze niż umożliwienie publicznego dostępu, ale nadal jest dalekie od bezpieczeństwa. Poprawimy te zasady później w codelab.
3. Pobierz przykładowy kod
Sklonuj repozytorium GitHub z wiersza poleceń:
git clone https://github.com/firebase/friendlyeats-web
Przykładowy kod powinien zostać sklonowany do katalogu 📁 friendlyeats-web
. Od teraz upewnij się, że uruchamiasz wszystkie polecenia z tego katalogu:
cd friendlyeats-web
Zaimportuj aplikację startową
Używając swojego IDE (WebStorm, Atom, Sublime, Visual Studio Code...) otwórz lub zaimportuj katalog 📁 friendlyeats-web
. Ten katalog zawiera kod startowy dla codelab, który składa się z jeszcze niefunkcjonalnej aplikacji do rekomendacji restauracji. Sprawimy, że będzie działać w całym tym laboratorium, więc wkrótce będziesz musiał edytować kod w tym katalogu.
4. Zainstaluj interfejs wiersza poleceń Firebase
Interfejs wiersza poleceń Firebase (CLI) umożliwia lokalne udostępnianie aplikacji internetowej i wdrażanie aplikacji internetowej w Hostingu Firebase.
- Zainstaluj interfejs CLI, uruchamiając następujące polecenie npm:
npm -g install firebase-tools
- Sprawdź, czy interfejs CLI został poprawnie zainstalowany, uruchamiając następujące polecenie:
firebase --version
Upewnij się, że wersja Firebase CLI to 7.4.0 lub nowsza.
- Autoryzuj Firebase CLI, uruchamiając następujące polecenie:
firebase login
Skonfigurowaliśmy szablon aplikacji internetowej, aby pobierać konfigurację Twojej aplikacji dla Hostingu Firebase z lokalnego katalogu i plików Twojej aplikacji. Aby to zrobić, musimy powiązać Twoją aplikację z projektem Firebase.
- Upewnij się, że wiersz poleceń uzyskuje dostęp do katalogu lokalnego Twojej aplikacji.
- Powiąż swoją aplikację z projektem Firebase, uruchamiając następujące polecenie:
firebase use --add
- Po wyświetleniu monitu wybierz swój identyfikator projektu , a następnie nadaj projektowi Firebase alias.
Alias jest przydatny, jeśli masz wiele środowisk (produkcyjne, pomostowe itp.). Jednak w tym laboratorium kodu po prostu użyjmy aliasu default
.
- Postępuj zgodnie z pozostałymi instrukcjami w wierszu poleceń.
5. Uruchom serwer lokalny
Jesteśmy gotowi do faktycznego rozpoczęcia pracy nad naszą aplikacją! Uruchommy naszą aplikację lokalnie!
- Uruchom następujące polecenie Firebase CLI:
firebase emulators:start --only hosting
- Twoja linia poleceń powinna wyświetlić następującą odpowiedź:
hosting: Local server: http://localhost:5000
Używamy emulatora Hostingu Firebase do obsługi naszej aplikacji lokalnie. Aplikacja internetowa powinna być teraz dostępna pod adresem http://localhost:5000 .
- Otwórz swoją aplikację pod adresem http://localhost:5000 .
Powinieneś zobaczyć swoją kopię FriendlyEats, która została połączona z twoim projektem Firebase.
Aplikacja automatycznie połączyła się z Twoim projektem Firebase i po cichu zalogowała Cię jako anonimowego użytkownika.
6. Zapisz dane w Cloud Firestore
W tej sekcji zapiszemy niektóre dane w Cloud Firestore, abyśmy mogli zapełnić interfejs aplikacji. Można to zrobić ręcznie za pomocą konsoli Firebase , ale zrobimy to w samej aplikacji, aby zademonstrować podstawowy zapis w Cloud Firestore.
Model danych
Dane Firestore są podzielone na kolekcje, dokumenty, pola i kolekcje podrzędne. Każdą restaurację będziemy przechowywać jako dokument w kolekcji najwyższego poziomu o nazwie restaurants
.
Później będziemy przechowywać każdą recenzję w podzbiorze o nazwie ratings
w każdej restauracji.
Dodaj restauracje do Firestore
Głównym modelowym obiektem w naszej aplikacji jest restauracja. Napiszmy kod, który dodaje dokument restauracji do kolekcji restaurants
.
- Z pobranych plików otwórz
scripts/FriendlyEats.Data.js
. - Znajdź funkcję
FriendlyEats.prototype.addRestaurant
. - Zastąp całą funkcję następującym kodem.
FriendlyEats.Data.js
FriendlyEats.prototype.addRestaurant = function(data) { var collection = firebase.firestore().collection('restaurants'); return collection.add(data); };
Powyższy kod dodaje nowy dokument do kolekcji restaurants
. Dane dokumentu pochodzą ze zwykłego obiektu JavaScript. Robimy to, najpierw uzyskując odniesienie do restaurants
z kolekcji Cloud Firestore, a następnie add
dane.
Dodajmy restauracje!
- Wróć do aplikacji FriendlyEats w przeglądarce i odśwież ją.
- Kliknij opcję Dodaj fałszywe dane .
Aplikacja automatycznie wygeneruje losowy zestaw obiektów restauracji, a następnie wywoła funkcję addRestaurant
. Jednak nie zobaczysz jeszcze danych w rzeczywistej aplikacji internetowej, ponieważ nadal musimy zaimplementować pobieranie danych (następna sekcja laboratorium kodu).
Jeśli jednak przejdziesz do karty Cloud Firestore w konsoli Firebase, powinieneś teraz zobaczyć nowe dokumenty w kolekcji restaurants
!
Gratulacje, właśnie zapisałeś dane w Cloud Firestore z aplikacji internetowej!
W następnej sekcji dowiesz się, jak pobierać dane z Cloud Firestore i wyświetlać je w swojej aplikacji.
7. Wyświetlaj dane z Cloud Firestore
W tej sekcji dowiesz się, jak pobierać dane z Cloud Firestore i wyświetlać je w swojej aplikacji. Dwa kluczowe kroki to utworzenie zapytania i dodanie odbiornika migawek. Ten odbiornik zostanie powiadomiony o wszystkich istniejących danych pasujących do zapytania i będzie otrzymywać aktualizacje w czasie rzeczywistym.
Najpierw skonstruujmy zapytanie, które będzie wyświetlać domyślną, niefiltrowaną listę restauracji.
- Wróć do pliku
scripts/FriendlyEats.Data.js
. - Znajdź funkcję
FriendlyEats.prototype.getAllRestaurants
. - Zastąp całą funkcję następującym kodem.
FriendlyEats.Data.js
FriendlyEats.prototype.getAllRestaurants = function(renderer) { var query = firebase.firestore() .collection('restaurants') .orderBy('avgRating', 'desc') .limit(50); this.getDocumentsInQuery(query, renderer); };
W powyższym kodzie tworzymy zapytanie, które pobierze do 50 restauracji z kolekcji najwyższego poziomu o nazwie restaurants
, które są uporządkowane według średniej oceny (obecnie wszystkie zerowe). Po zadeklarowaniu tego zapytania przekazujemy je do metody getDocumentsInQuery()
, która odpowiada za ładowanie i renderowanie danych.
Zrobimy to, dodając odbiornik migawki.
- Wróć do pliku
scripts/FriendlyEats.Data.js
. - Znajdź funkcję
FriendlyEats.prototype.getDocumentsInQuery
. - Zastąp całą funkcję następującym kodem.
FriendlyEats.Data.js
FriendlyEats.prototype.getDocumentsInQuery = function(query, renderer) { query.onSnapshot(function(snapshot) { if (!snapshot.size) return renderer.empty(); // Display "There are no restaurants". snapshot.docChanges().forEach(function(change) { if (change.type === 'removed') { renderer.remove(change.doc); } else { renderer.display(change.doc); } }); }); };
W powyższym kodzie query.onSnapshot
wywoła wywołanie zwrotne za każdym razem, gdy nastąpi zmiana wyniku zapytania.
- Za pierwszym razem wywołanie zwrotne jest uruchamiane z całym zestawem wyników zapytania – czyli z całą kolekcją
restaurants
z Cloud Firestore. Następnie przekazuje wszystkie poszczególne dokumenty do funkcjirenderer.display
. - Kiedy dokument jest usuwany,
change.type
jest równeremoved
. W takim przypadku wywołamy funkcję, która usunie restaurację z interfejsu użytkownika.
Teraz, gdy zaimplementowaliśmy obie metody, odśwież aplikację i sprawdź, czy restauracje, które widzieliśmy wcześniej w konsoli Firebase, są teraz widoczne w aplikacji. Jeśli pomyślnie ukończyłeś tę sekcję, oznacza to, że Twoja aplikacja odczytuje i zapisuje dane w Cloud Firestore!
Gdy Twoja lista restauracji się zmieni, ten odbiornik będzie aktualizował się automatycznie. Spróbuj przejść do konsoli Firebase i ręcznie usunąć restaurację lub zmienić jej nazwę — zobaczysz, że zmiany natychmiast pojawią się na Twojej stronie!
8. Pobierz() dane
Do tej pory pokazaliśmy, jak używać onSnapshot
do pobierania aktualizacji w czasie rzeczywistym; jednak nie zawsze tego chcemy. Czasami bardziej sensowne jest pobranie danych tylko raz.
Chcemy zaimplementować metodę, która jest uruchamiana, gdy użytkownik kliknie określoną restaurację w Twojej aplikacji.
- Wróć do pliku
scripts/FriendlyEats.Data.js
. - Znajdź funkcję
FriendlyEats.prototype.getRestaurant
. - Zastąp całą funkcję następującym kodem.
FriendlyEats.Data.js
FriendlyEats.prototype.getRestaurant = function(id) { return firebase.firestore().collection('restaurants').doc(id).get(); };
Po wdrożeniu tej metody będziesz mógł przeglądać strony każdej restauracji. Po prostu kliknij restaurację na liście i powinieneś zobaczyć stronę szczegółów restauracji:
Na razie nie możesz dodawać ocen, ponieważ nadal musimy zaimplementować dodawanie ocen później w codelab.
9. Sortuj i filtruj dane
Obecnie nasza aplikacja wyświetla listę restauracji, ale użytkownik nie ma możliwości filtrowania na podstawie swoich potrzeb. W tej sekcji użyjesz zaawansowanych zapytań Cloud Firestore, aby włączyć filtrowanie.
Oto przykład prostego zapytania, które pobierze wszystkie restauracje Dim Sum
:
var filteredQuery = query.where('category', '==', 'Dim Sum')
Jak sama nazwa wskazuje, metoda where()
spowoduje, że nasze zapytanie pobierze tylko członków kolekcji, których pola spełniają określone przez nas ograniczenia. W takim przypadku pobierze tylko restauracje, których category
jest Dim Sum
.
W naszej aplikacji użytkownik może połączyć wiele filtrów, aby utworzyć określone zapytania, takie jak „Pizza w San Francisco” lub „Owoce morza w Los Angeles zamówione według popularności”.
Stworzymy metodę, która zbuduje zapytanie, które będzie filtrować nasze restauracje na podstawie wielu kryteriów wybranych przez naszych użytkowników.
- Wróć do pliku
scripts/FriendlyEats.Data.js
. - Znajdź funkcję
FriendlyEats.prototype.getFilteredRestaurants
. - Zastąp całą funkcję następującym kodem.
FriendlyEats.Data.js
FriendlyEats.prototype.getFilteredRestaurants = function(filters, renderer) { var query = firebase.firestore().collection('restaurants'); if (filters.category !== 'Any') { query = query.where('category', '==', filters.category); } if (filters.city !== 'Any') { query = query.where('city', '==', filters.city); } if (filters.price !== 'Any') { query = query.where('price', '==', filters.price.length); } if (filters.sort === 'Rating') { query = query.orderBy('avgRating', 'desc'); } else if (filters.sort === 'Reviews') { query = query.orderBy('numRatings', 'desc'); } this.getDocumentsInQuery(query, renderer); };
Powyższy kod dodaje wiele filtrów where
i jedną klauzulę orderBy
w celu zbudowania zapytania złożonego na podstawie danych wprowadzonych przez użytkownika. Nasze zapytanie będzie teraz zwracało tylko restauracje spełniające wymagania użytkownika.
Odśwież aplikację FriendlyEats w przeglądarce, a następnie sprawdź, czy możesz filtrować według ceny, miasta i kategorii. Podczas testowania zobaczysz następujące błędy w konsoli JavaScript swojej przeglądarki:
The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=...
Te błędy są spowodowane tym, że Cloud Firestore wymaga indeksów dla większości zapytań złożonych. Wymaganie indeksów w zapytaniach sprawia, że Cloud Firestore działa szybko i na dużą skalę.
Otwarcie linku z komunikatu o błędzie spowoduje automatyczne otwarcie interfejsu tworzenia indeksu w konsoli Firebase z wprowadzonymi poprawnymi parametrami. W następnej sekcji napiszemy i wdrożymy indeksy potrzebne dla tej aplikacji.
10. Wdróż indeksy
Jeśli nie chcemy eksplorować każdej ścieżki w naszej aplikacji i podążać za każdym linkiem do tworzenia indeksu, możemy z łatwością wdrożyć wiele indeksów jednocześnie za pomocą Firebase CLI.
- W lokalnym katalogu pobranej aplikacji znajdziesz plik
firestore.indexes.json
.
Ten plik opisuje wszystkie indeksy potrzebne dla wszystkich możliwych kombinacji filtrów.
firestore.indexes.json
{ "indexes": [ { "collectionGroup": "restaurants", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "city", "order": "ASCENDING" }, { "fieldPath": "avgRating", "order": "DESCENDING" } ] }, ... ] }
- Wdróż te indeksy za pomocą następującego polecenia:
firebase deploy --only firestore:indexes
Po kilku minutach Twoje indeksy będą aktywne, a komunikaty o błędach znikną.
11. Zapisz dane w transakcji
W tej sekcji dodamy możliwość przesyłania przez użytkowników recenzji do restauracji. Jak dotąd wszystkie nasze zapisy były atomowe i stosunkowo proste. Jeśli którykolwiek z nich zawierał błąd, prawdopodobnie po prostu poprosilibyśmy użytkownika o ponowienie próby lub nasza aplikacja automatycznie ponowiłaby próbę zapisu.
Nasza aplikacja będzie miała wielu użytkowników, którzy będą chcieli dodać ocenę restauracji, więc będziemy musieli skoordynować wiele odczytów i zapisów. Najpierw należy przesłać samą recenzję, a następnie zaktualizować count
ocen i average rating
restauracji. Jeśli jeden z nich zawiedzie, a drugi nie, pozostajemy w niespójnym stanie, w którym dane w jednej części naszej bazy danych nie pasują do danych w innej.
Na szczęście Cloud Firestore zapewnia funkcjonalność transakcyjną, która pozwala nam wykonywać wiele odczytów i zapisów w jednej atomowej operacji, zapewniając spójność naszych danych.
- Wróć do pliku
scripts/FriendlyEats.Data.js
. - Znajdź funkcję
FriendlyEats.prototype.addRating
. - Zastąp całą funkcję następującym kodem.
FriendlyEats.Data.js
FriendlyEats.prototype.addRating = function(restaurantID, rating) { var collection = firebase.firestore().collection('restaurants'); var document = collection.doc(restaurantID); var newRatingDocument = document.collection('ratings').doc(); return firebase.firestore().runTransaction(function(transaction) { return transaction.get(document).then(function(doc) { var data = doc.data(); var newAverage = (data.numRatings * data.avgRating + rating.rating) / (data.numRatings + 1); transaction.update(document, { numRatings: data.numRatings + 1, avgRating: newAverage }); return transaction.set(newRatingDocument, rating); }); }); };
W powyższym bloku uruchamiamy transakcję, aby zaktualizować wartości liczbowe avgRating
i numRatings
w dokumencie restauracji. Jednocześnie dodajemy nową rating
do podkolekcji ratings
.
12. Zabezpiecz swoje dane
Na początku tego laboratorium kodowania ustaliliśmy reguły bezpieczeństwa naszej aplikacji, aby całkowicie otworzyć bazę danych na dowolny odczyt lub zapis. W prawdziwej aplikacji chcielibyśmy ustawić znacznie bardziej szczegółowe reguły, aby zapobiec niepożądanemu dostępowi do danych lub ich modyfikacji.
- W sekcji Kompilacja konsoli Firebase kliknij opcję Baza danych Firestore .
- Kliknij kartę Reguły w sekcji Cloud Firestore (lub kliknij tutaj, aby przejść bezpośrednio do niej).
- Zastąp wartości domyślne następującymi regułami, a następnie kliknij Publikuj .
sklep.reguły
rules_version = '2'; service cloud.firestore { // Determine if the value of the field "key" is the same // before and after the request. function unchanged(key) { return (key in resource.data) && (key in request.resource.data) && (resource.data[key] == request.resource.data[key]); } match /databases/{database}/documents { // Restaurants: // - Authenticated user can read // - Authenticated user can create/update (for demo purposes only) // - Updates are allowed if no fields are added and name is unchanged // - Deletes are not allowed (default) match /restaurants/{restaurantId} { allow read: if request.auth != null; allow create: if request.auth != null; allow update: if request.auth != null && (request.resource.data.keys() == resource.data.keys()) && unchanged("name"); // Ratings: // - Authenticated user can read // - Authenticated user can create if userId matches // - Deletes and updates are not allowed (default) match /ratings/{ratingId} { allow read: if request.auth != null; allow create: if request.auth != null && request.resource.data.userId == request.auth.uid; } } } }
Te reguły ograniczają dostęp, aby upewnić się, że klienci dokonują tylko bezpiecznych zmian. Na przykład:
- Aktualizacje dokumentu restauracji mogą zmienić tylko oceny, a nie nazwę ani inne niezmienne dane.
- Oceny można tworzyć tylko wtedy, gdy identyfikator użytkownika jest zgodny z zalogowanym użytkownikiem, co zapobiega podszywaniu się.
Alternatywnie do konsoli Firebase możesz użyć Firebase CLI do wdrożenia reguł w swoim projekcie Firebase. Plik firestore.rules w twoim katalogu roboczym zawiera już powyższe reguły. Aby wdrożyć te reguły z lokalnego systemu plików (zamiast używać konsoli Firebase), uruchom następujące polecenie:
firebase deploy --only firestore:rules
13. Wniosek
W tym laboratorium kodowania nauczyłeś się, jak wykonywać podstawowe i zaawansowane odczyty i zapisy w Cloud Firestore, a także jak zabezpieczać dostęp do danych za pomocą reguł bezpieczeństwa. Pełne rozwiązanie można znaleźć w repozytorium quickstarts-js .
Aby dowiedzieć się więcej o Cloud Firestore, odwiedź następujące zasoby: