Laboratorium internetowe Cloud Firestore

Cele

W tym codelab, można zbudować aplikację internetową restauracja zalecenie zasilany chmurze FireStore .

img5.png

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ł zabezpieczeń, aby zabezpieczyć dane Cloud Firestore
  • Pisz złożone zapytania Cloud Firestore

Co będziesz potrzebował

Przed rozpoczęciem tego ćwiczenia z programowania upewnij się, że zainstalowałeś:

Utwórz projekt Firebase

  1. W konsoli Firebase , kliknij przycisk Dodaj projekt, a następnie wymienić FriendlyEats projektu Firebase.

Zapamiętaj identyfikator projektu dla swojego projektu Firebase.

  1. Kliknij Utwórz projekt.

Aplikacja, którą zamierzamy zbudować, korzysta z kilku usług Firebase dostępnych w sieci:

  • Uwierzytelnianie Firebase łatwo zidentyfikować użytkowników
  • Chmura Firestore aby zapisać dane strukturalne na obłoku i uzyskać natychmiastowe powiadomienie, gdy dane są aktualizowane
  • Firebase Hosting przyjmującego i służyć swoje aktywa statycznych

Na potrzeby tego konkretnego ćwiczenia z programowania skonfigurowaliśmy już Hosting Firebase. Jednak w przypadku Firebase Auth i Cloud Firestore przeprowadzimy Cię przez konfigurację i włączanie usług przy użyciu konsoli Firebase.

Włącz anonimowe uwierzytelnianie

Chociaż uwierzytelnianie nie jest głównym przedmiotem tego ćwiczenia z programowania, ważne jest, aby w naszej aplikacji istniała jakaś forma uwierzytelniania. Użyjemy anonimowego logowania - oznacza, że użytkownik zostanie podpisana w milczeniu bez monitu.

Musisz włączyć anonimowego logowania.

  1. W konsoli Firebase znajdź sekcję budować w lewym panelu nawigacyjnym.
  2. Kliknij uwierzytelniania, a następnie kliknij logowania w zakładce Metoda (lub kliknij tutaj , aby przejść bezpośrednio tam).
  3. Umożliwienia logowania w Provider anonimowy, a następnie kliknij przycisk Zapisz.

img7.png

Umożliwi to aplikacji dyskretne logowanie użytkowników, gdy uzyskują dostęp do aplikacji internetowej. Zapraszam do zapoznania się z dokumentacją Anonymous Authentication , aby dowiedzieć się więcej.

Włącz Cloud Firestore

Aplikacja korzysta z Cloud Firestore do zapisywania i odbierania informacji i ocen restauracji.

Musisz włączyć Cloud Firestore. W sekcji zbudować konsoli Firebase, kliknij FireStore Database. Kliknij przycisk Utwórz bazę danych w panelu Chmura FireStore.

Dostęp do danych w Cloud Firestore jest kontrolowany przez reguły bezpieczeństwa. Porozmawiamy więcej o regułach w dalszej części tego ćwiczenia z programowania, ale najpierw musimy ustawić kilka podstawowych reguł dotyczących naszych danych, aby rozpocząć. Na karcie Reguły konsoli Firebase dodać następujące zasady, a następnie kliknij przycisk Publish.

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 czytanie lub pisanie. Jest to lepsze niż zezwalanie na publiczny dostęp, ale nadal jest dalekie od bezpieczeństwa. Poprawimy te zasady w dalszej części ćwiczenia z programowania.

Sklonować repozytorium GitHub z wiersza poleceń:

git clone https://github.com/firebase/friendlyeats-web

Przykładowy kod powinien zostały sklonowane do 📁 friendlyeats-web katalogu. Od teraz upewnij się, że uruchamiasz wszystkie polecenia z tego katalogu:

cd friendlyeats-web

Zaimportuj aplikację startową

Korzystanie z IDE (WebStorm, atom, super, kodu Visual Studio ...) otwartą lub importować 📁 friendlyeats-web katalog. Ten katalog zawiera kod startowy ćwiczenia z programowania, które składa się z niedziałającej jeszcze aplikacji rekomendacji restauracji. Sprawimy, że będzie funkcjonował w tym laboratorium, więc wkrótce będziesz musiał edytować kod w tym katalogu.

Interfejs wiersza poleceń Firebase (CLI) umożliwia lokalną obsługę aplikacji internetowej i wdrażanie jej w Hostingu Firebase.

  1. Zainstaluj interfejs wiersza polecenia, uruchamiając następujące polecenie npm:
npm -g install firebase-tools
  1. Sprawdź, czy interfejs wiersza polecenia został poprawnie zainstalowany, uruchamiając następujące polecenie:
firebase --version

Upewnij się, że wersja interfejsu Firebase CLI to 7.4.0 lub nowsza.

  1. Autoryzuj Firebase CLI, uruchamiając następujące polecenie:
firebase login

Skonfigurowaliśmy szablon aplikacji internetowej, aby pobrać konfigurację Twojej aplikacji dla Hostingu Firebase z lokalnego katalogu i plików aplikacji. Aby to zrobić, musimy jednak powiązać Twoją aplikację z projektem Firebase.

  1. Upewnij się, że wiersz poleceń uzyskuje dostęp do lokalnego katalogu aplikacji.
  2. Powiąż swoją aplikację z projektem Firebase, uruchamiając następujące polecenie:
firebase use --add
  1. Gdy pojawi się monit, wybierz swój projekt identyfikatora, a następnie dać Firebase projektowi aliasu.

Alias ​​jest przydatny, jeśli masz wiele środowisk (produkcyjnych, tymczasowych itp.). Jednak do tej codelab, niech po prostu użyć alias default .

  1. Postępuj zgodnie z pozostałymi instrukcjami w wierszu poleceń.

Jesteśmy gotowi do rozpoczęcia prac nad naszą aplikacją! Uruchommy naszą aplikację lokalnie!

  1. Uruchom następujące polecenie Firebase CLI:
firebase emulators:start --only hosting
  1. Twój wiersz poleceń powinien wyświetlić następującą odpowiedź:
hosting: Local server: http://localhost:5000

Używamy Hosting Firebase emulator służyć naszą aplikację lokalnie. Aplikacja internetowa powinna być dostępna od http: // localhost: 5000 .

  1. Otwórz aplikację na 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 anonimowy użytkownik.

img2.png

W tej sekcji zapiszemy pewne dane w Cloud Firestore, abyśmy mogli wypełnić interfejs aplikacji. Można to zrobić ręcznie za pomocą konsoli Firebase , ale zrobimy to w aplikacji samego wykazania podstawowy zapis Chmura FireStore.

Model danych

Dane Firestore są podzielone na kolekcje, dokumenty, pola i podkolekcje. Będziemy przechowywać każdej restauracji jako dokumentu w kolekcji najwyższego poziomu o nazwie restaurants .

img3.png

Później będziemy przechowywać każdą opinię w subcollection zwanej ratings w ramach każdej restauracji.

img4.png

Dodaj restauracje do Firestore

Głównym obiektem modelowym w naszej aplikacji jest restauracja. Napiszmy trochę kodu, który dodaje dokument restauracji do restaurants kolekcji.

  1. Z pobranych plików, otwartych scripts/FriendlyEats.Data.js .
  2. Znajdź funkcję FriendlyEats.prototype.addRestaurant .
  3. 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 restaurants kolekcji. Dane dokumentu pochodzą ze zwykłego obiektu JavaScript. Robimy to przez zdobycie odniesienie do kolekcji Chmura FireStore restaurants następnie add „ing danych.

Dodajmy restauracje!

  1. Wróć do swojej aplikacji FriendlyEats w przeglądarce i odśwież ją.
  2. Kliknij przycisk Dodaj Mock danych.

Aplikacja automatycznie generuje losowy zestaw restauracji obiektów, a następnie połączyć się addRestaurant funkcję. Jednak nie będzie jeszcze zobaczyć dane w aktualnej aplikacji internetowych, ponieważ wciąż musimy wdrożyć pobierania danych (następny odcinek codelab).

Jeśli przejdź do zakładki Chmura FireStore w konsoli Firebase, choć należy teraz zobaczyć nowe dokumenty w restaurants kolekcji!

img6.png

Gratulacje, właśnie zapisałeś dane do Cloud Firestore z aplikacji internetowej!

W następnej sekcji dowiesz się, jak pobierać dane z Cloud Firestore i wyświetlać je w swojej aplikacji.

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 detektora migawki. Ten detektor zostanie powiadomiony o wszystkich istniejących danych, które pasują do zapytania i będzie otrzymywać aktualizacje w czasie rzeczywistym.

Najpierw skonstruujmy zapytanie, które będzie obsługiwać domyślną, niefiltrowaną listę restauracji.

  1. Wróć do pliku scripts/FriendlyEats.Data.js .
  2. Znajdź funkcję FriendlyEats.prototype.getAllRestaurants .
  3. 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, możemy skonstruować kwerendę, która będzie pobierać maksymalnie 50 restauracji z kolekcji najwyższego poziomu o nazwie restaurants , które są uporządkowane według średniej oceny (obecnie wszystko zero). Po tym, jak ogłosił to zapytanie, mijamy go do getDocumentsInQuery() metody, która jest odpowiedzialna za ładowanie i renderowania danych.

Zrobimy to, dodając detektor migawek.

  1. Wróć do pliku scripts/FriendlyEats.Data.js .
  2. Znajdź funkcję FriendlyEats.prototype.getDocumentsInQuery .
  3. 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 spowoduje jej wywołanie zwrotne za każdym razem jest zmiana wyniku zapytania.

  • Za pierwszym razem, wywołanie zwrotne jest wyzwalane z całego zbioru wynikowego zapytania - czyli cała restaurants kolekcję od chmur FireStore. Następnie przechodzi wszystkie poszczególne dokumenty do renderer.display funkcji.
  • Gdy dokument zostanie usunięty, change.type równa removed . W tym przypadku wywołamy funkcję, która usunie restaurację z interfejsu użytkownika.

Teraz, gdy wdrożyliś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ę, Twoja aplikacja odczytuje i zapisuje dane w Cloud Firestore!

Wraz ze zmianą listy restauracji ten odbiornik będzie aktualizował się automatycznie. Spróbuj przejść do konsoli Firebase i ręcznie usunąć restaurację lub zmienić jej nazwę – zmiany natychmiast pojawią się w Twojej witrynie.

img5.png

Do tej pory mamy pokazane jak używać onSnapshot pobierać aktualizacje 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.

  1. Wróć do plików scripts/FriendlyEats.Data.js .
  2. Znajdź funkcję FriendlyEats.prototype.getRestaurant .
  3. 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. Wystarczy kliknąć restaurację na liście, a powinna wyświetlić się strona szczegółów restauracji:

img1.png

Na razie nie możesz dodawać ocen, ponieważ nadal musimy zaimplementować dodawanie ocen w dalszej części ćwiczenia z programowania.

Obecnie nasza aplikacja wyświetla listę restauracji, ale użytkownik nie ma możliwości filtrowania według swoich potrzeb. W tej sekcji użyjesz zaawansowanych zapytań Cloud Firestore, aby włączyć filtrowanie.

Oto przykład prostego zapytania, aby pobrać wszystkie Dim Sum restauracje:

var filteredQuery = query.where('category', '==', 'Dim Sum')

Jak sama nazwa wskazuje, where() metoda uczyni nasze zapytanie do pobrania tylko członkowie kolekcji, której pola spełnienia ograniczeń stawiamy. W tym przypadku będzie to pobrać tylko restauracje, gdzie category jest Dim Sum .

W naszej aplikacji użytkownik może połączyć wiele filtrów, aby utworzyć konkretne zapytania, takie jak „Pizza w San Francisco” lub „Owoce morza w Los Angeles uporządkowane 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.

  1. Wróć do plików scripts/FriendlyEats.Data.js .
  2. Znajdź funkcję FriendlyEats.prototype.getFilteredRestaurants .
  3. 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 wielokrotny where filtry i jeden orderBy klauzula zbudować kwerendę związku na podstawie danych wprowadzonych przez użytkownika. Nasze zapytanie zwróci teraz tylko te restauracje, które spełniają 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 błędy w konsoli JavaScript swojej przeglądarki, które wyglądają tak:

The query requires an index. You can create it here: https://console.firebase.google.com/project/.../database/firestore/indexes?create_index=...

Te błędy wynikają z tego, że Cloud Firestore wymaga indeksów w przypadku większości zapytań złożonych. Wymaganie indeksów w zapytaniach sprawia, że ​​Cloud Firestore działa szybko na dużą skalę.

Otwarcie linku z komunikatu o błędzie spowoduje automatyczne otwarcie interfejsu tworzenia indeksu w konsoli Firebase z wypełnionymi poprawnymi parametrami. W następnej sekcji napiszemy i wdrożymy indeksy potrzebne dla tej aplikacji.

Jeśli nie chcemy eksplorować każdej ścieżki w naszej aplikacji i podążać za każdym z linków do tworzenia indeksów, możemy łatwo wdrożyć wiele indeksów jednocześnie, korzystając z interfejsu Firebase CLI.

  1. W pobranym katalogu lokalnego Twojej aplikacji, znajdziesz firestore.indexes.json pliku.

Ten plik opisuje wszystkie indeksy potrzebne do wszystkich możliwych kombinacji filtrów.

firestore.indexes.json

{
 "indexes": [
   {
     "collectionGroup": "restaurants",
     "queryScope": "COLLECTION",
     "fields": [
       { "fieldPath": "city", "order": "ASCENDING" },
       { "fieldPath": "avgRating", "order": "DESCENDING" }
     ]
   },

   ...

 ]
}
  1. 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ą.

W tej sekcji dodamy możliwość przesyłania opinii do restauracji przez użytkowników. Jak dotąd wszystkie nasze teksty były atomowe i stosunkowo proste. Jeśli któryś z nich jest błędny, prawdopodobnie po prostu poprosimy użytkownika o ponowną próbę lub nasza aplikacja spróbuje automatycznie ponowić zapis.

Nasza aplikacja będzie miała wielu użytkowników, którzy będą chcieli dodać ocenę restauracji, więc będziemy musieli koordynować wiele odczytów i zapisów. Pierwszy przegląd sama musi zostać złożone, a następnie ocena restauracji jest count i average rating muszą być aktualizowane. Jeśli jeden z nich zawiedzie, ale nie drugi, pozostaniemy 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ść transakcji, która pozwala nam wykonywać wiele odczytów i zapisów w jednej atomowej operacji, zapewniając spójność naszych danych.

  1. Wróć do plików scripts/FriendlyEats.Data.js .
  2. Znajdź funkcję FriendlyEats.prototype.addRating .
  3. 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 bloku powyżej, możemy wywołać transakcję do aktualizacji wartości liczbowych avgRating i numRatings w dokumencie restauracji. Jednocześnie, dodajemy nowy rating do ratings subcollection.

Na początku tego ćwiczenia z kodowania ustawiliś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.

  1. W sekcji zbudować konsoli Firebase, kliknij FireStore Database.
  2. Kliknij zakładkę zasady w sekcji Chmura FireStore (lub kliknij tutaj , aby przejść bezpośrednio tam).
  3. Wymień domyślne z następującymi zasadami, a następnie kliknij przycisk Publish.

firestore.zasady

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 zapewnić, że klienci wprowadzają tylko bezpieczne zmiany. Na przykład:

  • Aktualizacje dokumentu restauracji mogą zmienić tylko oceny, a nie nazwę lub 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 korzystania z konsoli Firebase możesz użyć interfejsu wiersza polecenia Firebase, aby wdrożyć reguły w swoim projekcie Firebase. Firestore.rules plik w katalogu roboczym zawiera już reguły z góry. 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

Podczas tego ćwiczenia z programowania nauczyłeś się, jak wykonywać podstawowe i zaawansowane odczyty i zapisy w Cloud Firestore, a także jak zabezpieczyć dostęp do danych za pomocą reguł bezpieczeństwa. Można znaleźć pełnego rozwiązania w repozytorium quickstarts-js .

Aby dowiedzieć się więcej o Cloud Firestore, odwiedź następujące zasoby: