Cloud Firestore Web Codelab

1. Обзор

Цели

В этом codelab, вы будете создавать веб - приложения ресторан рекомендации питание от Cloud Firestore .

img5.png

Что ты узнаешь

  • Чтение и запись данных в Cloud Firestore из веб-приложения
  • Слушайте изменения в данных Cloud Firestore в режиме реального времени
  • Используйте Firebase Authentication и правила безопасности для защиты данных Cloud Firestore
  • Написание сложных запросов Cloud Firestore

Что тебе понадобится

Перед запуском этой кодовой лаборатории убедитесь, что вы установили:

2. Создайте и настройте проект Firebase.

Создать проект Firebase

  1. В Firebase консоли , нажмите кнопку Добавить проект, а затем назвать FriendlyEats проекта Firebase.

Запомните идентификатор проекта для вашего проекта Firebase.

  1. Нажмите кнопку Создать проект.

Приложение, которое мы собираемся создать, использует несколько сервисов Firebase, доступных в Интернете:

  • Firebase Authentication легко идентифицировать пользователей
  • Облако Firestore для сохранения структурированных данных на облаке и получить уведомление момента , когда данные обновляются
  • Firebase хостинг для хоста и обслуживать статические активы

Для этой конкретной кодовой лаборатории мы уже настроили хостинг Firebase. Однако для Firebase Auth и Cloud Firestore мы проведем вас через настройку и включение служб с помощью консоли Firebase.

Включить анонимную аутентификацию

Хотя аутентификация не является основной темой этой лаборатории, важно иметь какую-либо форму аутентификации в нашем приложении. Мы будем использовать анонимный вход - это означает , что пользователь будет молча подписал в без запроса.

Вам необходимо разрешить анонимный вход.

  1. В Firebase консоли найдите раздел сборки в левой навигационной панели.
  2. Нажмите Authentication, затем нажмите Вход в систему вкладке метод (или нажмите здесь , чтобы перейти непосредственно туда).
  3. Включить анонимный Вход в систему провайдера, а затем нажмите кнопку Сохранить.

img7.png

Это позволит приложению автоматически входить в систему ваших пользователей, когда они обращаются к веб-приложению. Вы можете прочитать документацию анонимной проверки подлинности , чтобы узнать больше.

Включить Cloud Firestore

Приложение использует Cloud Firestore для сохранения и получения информации о ресторанах и оценок.

Вам нужно будет включить Cloud Firestore. В разделе сборки Firebase консоли, нажмите Firestore базы данных. Нажмите кнопку Создать базу данных в области облачных Firestore.

Доступ к данным в Cloud Firestore контролируется Правилами безопасности. Мы поговорим больше о правилах позже в этой лаборатории кода, но сначала нам нужно установить некоторые основные правила для наших данных, чтобы начать работу. В закладке Правила консоли Firebase добавить следующие правила , а затем нажмите кнопку Опубликовать.

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;
    }
  }
}

Вышеупомянутые правила ограничивают доступ к данным для пользователей, которые вошли в систему, что предотвращает чтение и запись неаутентифицированных пользователей. Это лучше, чем разрешить публичный доступ, но все еще далеко от безопасности, мы улучшим эти правила позже в кодовой лаборатории.

3. Получите образец кода.

Клон хранилище GitHub из командной строки:

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

Пример кода должен был клонирован в 📁 friendlyeats-web - каталога. С этого момента обязательно запускайте все свои команды из этого каталога:

cd friendlyeats-web

Импортируйте стартовое приложение

Использование IDE (WebStorm, Atom, Sublime, Visual Studio код ...) открытый или импортировать 📁 friendlyeats-web - каталог. Этот каталог содержит начальный код для codelab, который состоит из еще не работающего приложения для рекомендации ресторанов. Мы сделаем его работоспособным на протяжении всей этой кодовой лаборатории, поэтому вам скоро нужно будет отредактировать код в этом каталоге.

4. Установите интерфейс командной строки Firebase.

Интерфейс командной строки Firebase (CLI) позволяет вам обслуживать ваше веб-приложение локально и развертывать ваше веб-приложение на хостинге Firebase.

  1. Установите CLI, выполнив следующую команду npm:
npm -g install firebase-tools
  1. Убедитесь, что интерфейс командной строки установлен правильно, выполнив следующую команду:
firebase --version

Убедитесь, что версия Firebase CLI - v7.4.0 или новее.

  1. Авторизуйте интерфейс командной строки Firebase, выполнив следующую команду:
firebase login

Мы настроили шаблон веб-приложения для извлечения конфигурации вашего приложения для хостинга Firebase из локального каталога и файлов вашего приложения. Но для этого нам нужно связать ваше приложение с вашим проектом Firebase.

  1. Убедитесь, что ваша командная строка обращается к локальному каталогу вашего приложения.
  2. Свяжите свое приложение с проектом Firebase, выполнив следующую команду:
firebase use --add
  1. При появлении запроса выберите идентификатор проекта, затем дайте Firebase проекту псевдоним.

Псевдоним полезен, если у вас несколько сред (производственная, промежуточная и т. Д.). Однако, для этого codelab, давайте просто использовать псевдоним по default .

  1. Следуйте оставшимся инструкциям в командной строке.

5. Запустите локальный сервер.

Мы готовы приступить к работе над нашим приложением! Давайте запустим наше приложение локально!

  1. Выполните следующую команду Firebase CLI:
firebase emulators:start --only hosting
  1. В вашей командной строке должен отобразиться следующий ответ:
hosting: Local server: http://localhost:5000

Мы используем хостинг Firebase эмулятор , чтобы служить наше приложение локально. Теперь веб - приложение должно быть доступно с HTTP: // локальный: 5000 .

  1. Откройте приложение на HTTP: // Localhost: 5000 .

Вы должны увидеть свою копию FriendlyEats, подключенную к вашему проекту Firebase.

Приложение автоматически подключилось к вашему проекту Firebase и автоматически выполнило вход в систему как анонимный пользователь.

img2.png

6. Запишите данные в Cloud Firestore.

В этом разделе мы запишем некоторые данные в Cloud Firestore, чтобы мы могли заполнить пользовательский интерфейс приложения. Это можно сделать вручную с помощью Firebase консоли , но мы будем делать это в самом приложении , чтобы продемонстрировать основной Cloud Firestore записи.

Модель данных

Данные Firestore разбиты на коллекции, документы, поля и вложенные коллекции. Мы будем хранить каждый ресторан в качестве документа в коллекции верхнего уровня называется restaurants .

img3.png

Позже мы сохраняем каждый обзор подколлекции под названием ratings в рамках каждого ресторана.

img4.png

Добавить рестораны в Firestore

Основным объектом модели в нашем приложении является ресторан. Давайте напишем код , который добавляет ресторан документ в restaurants коллекции.

  1. Из вашего загруженных файлов, открытых scripts/FriendlyEats.Data.js .
  2. Найти функцию FriendlyEats.prototype.addRestaurant .
  3. Замените всю функцию следующим кодом.

FriendlyEats.Data.js

FriendlyEats.prototype.addRestaurant = function(data) {
  var collection = firebase.firestore().collection('restaurants');
  return collection.add(data);
};

Код выше добавляет новый документ в restaurants коллекции. Данные документа поступают из простого объекта JavaScript. Мы делаем это, первым получить ссылку на коллекцию Cloud Firestore restaurants затем add «ИНГ данные.

Добавим рестораны!

  1. Вернитесь в приложение FriendlyEats в браузере и обновите его.
  2. Нажмите кнопку Добавить Mock данных.

Приложение будет автоматически генерировать случайный набор ресторанов объектов, а затем вызвать вашу addRestaurant функцию. Тем не менее, вы еще не будет видеть данные в Вашем веб - приложение , потому что мы по- прежнему необходимо осуществлять извлечение данных (следующий раздел о codelab).

Если перейти на вкладку Облако Firestore в Firebase консоли, хотя, вы должны увидеть новые документы в restaurants коллекции!

img6.png

Поздравляем, вы только что записали данные в Cloud Firestore из веб-приложения!

В следующем разделе вы узнаете, как получать данные из Cloud Firestore и отображать их в своем приложении.

7. Отображение данных из Cloud Firestore.

В этом разделе вы узнаете, как получить данные из Cloud Firestore и отобразить их в своем приложении. Два ключевых шага - это создание запроса и добавление прослушивателя моментальных снимков. Этот слушатель будет уведомлен обо всех существующих данных, соответствующих запросу, и будет получать обновления в режиме реального времени.

Во-первых, давайте создадим запрос, который будет обслуживать нефильтрованный список ресторанов по умолчанию.

  1. Вернитесь в файл scripts/FriendlyEats.Data.js .
  2. Найти функцию FriendlyEats.prototype.getAllRestaurants .
  3. Замените всю функцию следующим кодом.

FriendlyEats.Data.js

FriendlyEats.prototype.getAllRestaurants = function(renderer) {
  var query = firebase.firestore()
      .collection('restaurants')
      .orderBy('avgRating', 'desc')
      .limit(50);

  this.getDocumentsInQuery(query, renderer);
};

В приведенной выше коде, мы построим запрос , который будет извлекать до 50 ресторанов из коллекции верхнего уровня названных restaurants , которые отсортированы по среднему рейтингу ( в настоящее время всего ноля). После того, как мы объявили этот запрос, мы передаем его в getDocumentsInQuery() метод , который отвечает за загрузку и визуализации данных.

Мы сделаем это, добавив прослушиватель снимков.

  1. Вернитесь в файл scripts/FriendlyEats.Data.js .
  2. Найти функцию FriendlyEats.prototype.getDocumentsInQuery .
  3. Замените всю функцию следующим кодом.

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);
      }
    });
  });
};

В приведенном выше коде, query.onSnapshot запустит свой обратный вызов каждый раз , когда есть изменение в результате запроса.

  • Первый раз, обратный вызов инициируется с целым набором результатов запроса - то есть целые restaurants коллекции от Cloud Firestore. Затем он проходит все отдельные документы renderer.display функции.
  • Когда документ будет удален, change.type равно removed . Итак, в этом случае мы вызовем функцию, которая удаляет ресторан из пользовательского интерфейса.

Теперь, когда мы реализовали оба метода, обновите приложение и убедитесь, что рестораны, которые мы видели ранее в консоли Firebase, теперь отображаются в приложении. Если вы успешно завершили этот раздел, ваше приложение теперь читает и записывает данные с помощью Cloud Firestore!

По мере изменения вашего списка ресторанов этот слушатель будет обновляться автоматически. Попробуйте зайти в консоль Firebase и вручную удалить ресторан или изменить его название - вы сразу увидите, как изменения отобразятся на вашем сайте!

img5.png

8. Get () данные

До сих пор мы показали , как использовать onSnapshot для получения обновлений в режиме реального времени; однако это не всегда то, что мы хотим. Иногда имеет смысл получить данные только один раз.

Мы хотим реализовать метод, который запускается, когда пользователь нажимает на конкретный ресторан в вашем приложении.

  1. Вернитесь в ваш файл scripts/FriendlyEats.Data.js .
  2. Найти функцию FriendlyEats.prototype.getRestaurant .
  3. Замените всю функцию следующим кодом.

FriendlyEats.Data.js

FriendlyEats.prototype.getRestaurant = function(id) {
  return firebase.firestore().collection('restaurants').doc(id).get();
};

После того, как вы внедрите этот метод, вы сможете просматривать страницы для каждого ресторана. Просто нажмите на ресторан в списке, и вы увидите страницу с подробностями о нем:

img1.png

На данный момент вы не можете добавлять рейтинги, поскольку нам все еще необходимо реализовать добавление рейтингов позже в кодовой таблице.

9. Сортировка и фильтрация данных.

В настоящее время в нашем приложении отображается список ресторанов, но пользователь не может фильтровать его по своим потребностям. В этом разделе вы будете использовать расширенные запросы Cloud Firestore для включения фильтрации.

Вот пример простого запроса на выборку всех Dim Sum ресторанов:

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

Как следует из названия, where() метод будет сделать наш запрос скачать только член коллекции , чьи поля удовлетворяют ограничения , которые мы поставили. В этом случае, это будет только скачать рестораны , где category является Dim Sum .

В нашем приложении пользователь может связать несколько фильтров для создания определенных запросов, например «Пицца в Сан-Франциско» или «Морепродукты в Лос-Анджелесе, заказанные по популярности».

Мы создадим метод, который будет составлять запрос, который будет фильтровать наши рестораны на основе нескольких критериев, выбранных нашими пользователями.

  1. Вернитесь в ваш файл scripts/FriendlyEats.Data.js .
  2. Найти функцию FriendlyEats.prototype.getFilteredRestaurants .
  3. Замените всю функцию следующим кодом.

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);
};

Выше код добавляет множественным , where фильтры и один orderBy пункта для создания составного запроса на основе пользовательского ввода. Теперь наш запрос будет возвращать только те рестораны, которые соответствуют требованиям пользователя.

Обновите приложение FriendlyEats в браузере, затем убедитесь, что вы можете фильтровать по цене, городу и категории. Во время тестирования вы увидите ошибки в консоли JavaScript вашего браузера, которые выглядят следующим образом:

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

Эти ошибки вызваны тем, что Cloud Firestore требует индексов для большинства составных запросов. Требование индексов для запросов позволяет Cloud Firestore быстро масштабироваться.

Открытие ссылки из сообщения об ошибке автоматически откроет пользовательский интерфейс создания индекса в консоли Firebase с правильными заполненными параметрами. В следующем разделе мы напишем и развернем индексы, необходимые для этого приложения.

10. Разверните индексы.

Если мы не хотим исследовать каждый путь в нашем приложении и следовать каждой из ссылок создания индекса, мы можем легко развернуть сразу несколько индексов с помощью интерфейса командной строки Firebase.

  1. В загруженном локальном каталоге вашего приложения, вы найдете firestore.indexes.json файл.

В этом файле описаны все индексы, необходимые для всех возможных комбинаций фильтров.

firestore.indexes.json

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

   ...

 ]
}
  1. Разверните эти индексы с помощью следующей команды:
firebase deploy --only firestore:indexes

Через несколько минут ваши индексы будут активны, и сообщения об ошибках исчезнут.

11. Запись данных в транзакцию

В этом разделе мы добавим возможность пользователям отправлять отзывы о ресторанах. До сих пор все наши записи были атомарными и относительно простыми. Если в каком-либо из них возникнет ошибка, мы, скорее всего, просто предложим пользователю повторить их, или наше приложение автоматически повторит попытку записи.

В нашем приложении будет много пользователей, которые захотят поставить оценку ресторану, поэтому нам нужно будет координировать несколько операций чтения и записи. Первый обзор сам должен быть представлен, то рейтинг ресторана count и average rating потребности в обновлении. Если один из них не работает, а другой нет, мы остаемся в несогласованном состоянии, когда данные в одной части нашей базы данных не совпадают с данными в другой.

К счастью, Cloud Firestore предоставляет функциональные возможности транзакций, которые позволяют нам выполнять несколько операций чтения и записи за одну атомарную операцию, гарантируя, что наши данные остаются согласованными.

  1. Вернитесь в ваш файл scripts/FriendlyEats.Data.js .
  2. Найти функцию FriendlyEats.prototype.addRating .
  3. Замените всю функцию следующим кодом.

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);
    });
  });
};

В приведенном выше блоке, мы вызываем операцию , чтобы обновить числовые значения avgRating и numRatings в документе ресторана. В то же время, мы добавляем новый rating в ratings подколлекции.

12. Защитите свои данные

В начале этой кодовой лаборатории мы установили правила безопасности нашего приложения, чтобы полностью открыть базу данных для любого чтения или записи. В реальном приложении мы хотели бы установить гораздо более подробные правила, чтобы предотвратить нежелательный доступ к данным или их изменение.

  1. В разделе сборки Firebase консоли, нажмите Firestore базы данных.
  2. Перейдите на вкладку Правила в разделе Cloud Firestore (или нажмите здесь , чтобы перейти непосредственно туда).
  3. Заменить по умолчанию со следующими правилами, а затем нажмите кнопку Опубликовать.

firestore.rules

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;
      }
    }
  }
}

Эти правила ограничивают доступ, чтобы клиенты могли вносить только безопасные изменения. Например:

  • Обновления документа о ресторане могут изменять только рейтинги, но не имя или любые другие неизменяемые данные.
  • Рейтинги могут быть созданы только в том случае, если идентификатор пользователя соответствует зарегистрированному пользователю, что предотвращает спуфинг.

В качестве альтернативы использованию консоли Firebase вы можете использовать интерфейс командной строки Firebase для развертывания правил в своем проекте Firebase. Firestore.rules файл в рабочем каталоге уже содержит правила сверху. Чтобы развернуть эти правила из вашей локальной файловой системы (вместо использования консоли Firebase), вы должны выполнить следующую команду:

firebase deploy --only firestore:rules

13. Заключение

В этой кодовой лаборатории вы узнали, как выполнять базовые и расширенные операции чтения и записи с помощью Cloud Firestore, а также как защитить доступ к данным с помощью правил безопасности. Вы можете найти полное решение в quickstarts-JS хранилища .

Чтобы узнать больше о Cloud Firestore, посетите следующие ресурсы: