Cloud Firestore Web Codelab

1. סקירה כללית

מטרות

במעבדת הקוד הזה, תבנה אפליקציית אינטרנט להמלצה על מסעדות המופעלת על ידי Cloud Firestore .

img5.png

מה תלמד

  • קרא וכתוב נתונים ל-Cloud Firestore מאפליקציית אינטרנט
  • האזן לשינויים בנתוני Cloud Firestore בזמן אמת
  • השתמש באימות Firebase ובכללי אבטחה כדי לאבטח נתוני Cloud Firestore
  • כתוב שאילתות מורכבות של Cloud Firestore

מה אתה צריך

לפני שתתחיל מעבדת קוד זה, ודא שהתקנת:

2. צור והגדר פרוייקט Firebase

צור פרויקט Firebase

  1. במסוף Firebase , לחץ על הוסף פרויקט ולאחר מכן תן שם לפרויקט Firebase FriendlyEats .

זכור את מזהה הפרויקט עבור פרויקט Firebase שלך.

  1. לחץ על צור פרויקט .

האפליקציה שאנחנו הולכים לבנות משתמשת בכמה שירותי Firebase הזמינים באינטרנט:

  • אימות Firebase כדי לזהות בקלות את המשתמשים שלך
  • Cloud Firestore כדי לשמור נתונים מובנים בענן ולקבל הודעה מיידית כאשר הנתונים מתעדכנים
  • Firebase Hosting לארח ולשרת את הנכסים הסטטיים שלך

עבור מעבדת הקוד הספציפית הזו, כבר הגדרנו את אירוח Firebase. עם זאת, עבור Firebase Auth ו-Cloud Firestore, אנו נדריך אותך דרך התצורה וההפעלה של השירותים באמצעות קונסולת Firebase.

אפשר אישור אנונימי

למרות שאימות אינו המוקד של מעבדת הקוד הזה, חשוב שתהיה צורה כלשהי של אימות באפליקציה שלנו. אנו נשתמש בכניסה אנונימית - כלומר המשתמש ייכנס בשקט מבלי לקבל הנחיה.

יהיה עליך להפעיל התחברות אנונימית.

  1. במסוף Firebase, אתר את הקטע Build בניווט השמאלי.
  2. לחץ על אימות ולאחר מכן לחץ על הכרטיסייה שיטת כניסה (או לחץ כאן כדי לעבור ישירות לשם).
  3. הפעל את ספק הכניסה האנונימית ולאחר מכן לחץ על שמור .

img7.png

זה יאפשר לאפליקציה להיכנס בשקט למשתמשים שלך כאשר הם ניגשים לאפליקציית האינטרנט. אל תהסס לקרוא את תיעוד האימות האנונימי כדי ללמוד עוד.

הפעל את Cloud Firestore

האפליקציה משתמשת ב-Cloud Firestore כדי לשמור ולקבל מידע ודירוגי מסעדות.

יהיה עליך להפעיל את Cloud Firestore. בקטע Build של מסוף Firebase, לחץ על Firestore Database . לחץ על צור מסד נתונים בחלונית Cloud 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 Code...) פתח או ייבא את ספריית 📁 friendlyeats-web . ספרייה זו מכילה את קוד ההתחלה של מעבדת הקוד אשר מורכבת מאפליקציית המלצה למסעדות שעדיין לא פונקציונלית. אנו נהפוך אותו לפונקציונלי בכל מעבדת הקוד הזה, כך שתצטרך לערוך קוד בספרייה זו בקרוב.

4. התקן את ממשק שורת הפקודה של Firebase

ממשק שורת הפקודה של Firebase (CLI) מאפשר לך לשרת את אפליקציית האינטרנט שלך באופן מקומי ולפרוס את אפליקציית האינטרנט שלך ל-Firebase Hosting.

  1. התקן את ה-CLI על ידי הפעלת הפקודה npm הבאה:
npm -g install firebase-tools
  1. ודא שה-CLI הותקן כהלכה על ידי הפעלת הפקודה הבאה:
firebase --version

ודא שהגרסה של Firebase CLI היא גרסה 7.4.0 ואילך.

  1. אשר את Firebase CLI על ידי הפעלת הפקודה הבאה:
firebase login

הגדרנו את תבנית אפליקציית האינטרנט כדי למשוך את תצורת האפליקציה שלך עבור Firebase Hosting מהספרייה והקבצים המקומיים של האפליקציה שלך. אבל כדי לעשות זאת, עלינו לשייך את האפליקציה שלך לפרויקט Firebase שלך.

  1. ודא ששורת הפקודה שלך ניגשת לספרייה המקומית של האפליקציה שלך.
  2. שייך את האפליקציה שלך לפרויקט Firebase שלך ​​על ידי הפעלת הפקודה הבאה:
firebase use --add
  1. כשתתבקש, בחר את מזהה הפרויקט שלך ולאחר מכן תן לפרויקט Firebase שלך ​​כינוי.

כינוי שימושי אם יש לך מספר סביבות (הפקה, הבמה וכו'). עם זאת, עבור מעבדת קוד זה, בוא נשתמש בכינוי default .

  1. עקוב אחר ההוראות הנותרות בשורת הפקודה שלך.

5. הפעל את השרת המקומי

אנחנו מוכנים להתחיל לעבוד על האפליקציה שלנו! בואו להפעיל את האפליקציה שלנו באופן מקומי!

  1. הפעל את פקודת Firebase CLI הבאה:
firebase emulators:start --only hosting
  1. שורת הפקודה שלך אמורה להציג את התגובה הבאה:
hosting: Local server: http://localhost:5000

אנו משתמשים באמולטור Firebase Hosting כדי לשרת את האפליקציה שלנו באופן מקומי. אפליקציית האינטרנט אמורה להיות זמינה כעת מ- http://localhost: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 רגיל. אנו עושים זאת על ידי תחילה קבלת הפניה restaurants אוסף Cloud Firestore ולאחר מכן add הנתונים.

בואו נוסיף מסעדות!

  1. חזור לאפליקציית FriendlyEats שלך בדפדפן ורענן אותה.
  2. לחץ על הוסף נתונים מדומים .

האפליקציה תיצור אוטומטית קבוצה אקראית של אובייקטים של מסעדות, ואז תקרא לפונקציית addRestaurant שלך. עם זאת, עדיין לא תראה את הנתונים באפליקציית האינטרנט שלך בפועל, כי אנחנו עדיין צריכים ליישם את אחזור הנתונים (החלק הבא של מעבדת הקוד).

עם זאת, אם אתה מנווט ללשונית Cloud 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

לעת עתה, אינך יכול להוסיף דירוגים מכיוון שאנו עדיין צריכים ליישם הוספת דירוגים מאוחר יותר ב-codelab.

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 CLI.

  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. בקטע Build של מסוף Firebase, לחץ על Firestore Database .
  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 CLI כדי לפרוס כללים לפרויקט Firebase שלך. הקובץ firestore.rules בספריית העבודה שלך כבר מכיל את הכללים מלמעלה. כדי לפרוס כללים אלה ממערכת הקבצים המקומית שלך (במקום להשתמש במסוף Firebase), תפעיל את הפקודה הבאה:

firebase deploy --only firestore:rules

13. מסקנה

במעבדת הקוד הזה, למדת כיצד לבצע קריאה וכתיבה בסיסית ומתקדמת עם Cloud Firestore, כמו גם כיצד לאבטח גישה לנתונים באמצעות כללי אבטחה. אתה יכול למצוא את הפתרון המלא במאגר quickstarts-js .

למידע נוסף על Cloud Firestore, בקר במשאבים הבאים: