1. סקירה כללית
מטרות עסקיות
ב-codelab הזה תיצרו אפליקציה להמלצות על מסעדות ב-Android, שמגובה על ידי Cloud Firestore. תלמדו איך:
- קריאת נתונים וכתיבתם ב-Firestore מאפליקציית Android
- האזנה לשינויים בנתוני Firestore בזמן אמת
- שימוש באימות ב-Firebase ובכללי אבטחה לאבטחת נתונים ב-Firestore
- כתיבת שאילתות מורכבות ב-Firestore
דרישות מוקדמות
לפני שמתחילים את ה-codelab הזה, חשוב לוודא שיש לכם:
- Android Studio Flamingo או גרסה חדשה יותר
- אמולטור Android עם API 19 ומעלה
- Node.js בגרסה 16 ואילך
- Java בגרסה 17 ואילך
2. יצירת פרויקט Firebase
- נכנסים למסוף Firebase באמצעות חשבון Google.
- לוחצים על הלחצן כדי ליצור פרויקט חדש, ואז מזינים שם לפרויקט (לדוגמה,
FriendlyEats
).
- לוחצים על המשך.
- אם מוצגת בקשה לעשות זאת, קוראים ומאשרים את התנאים של Firebase, ואז לוחצים על המשך.
- (אופציונלי) מפעילים את העזרה מבוססת-AI במסוף Firebase (שנקראת Gemini ב-Firebase).
- ב-codelab הזה לא צריך להשתמש ב-Google Analytics, ולכן משביתים את האפשרות Google Analytics.
- לוחצים על יצירת פרויקט, מחכים שהפרויקט יוקצה ולוחצים על המשך.
3. הגדרת פרויקט לדוגמה
הורדת הקוד
מריצים את הפקודה הבאה כדי לשכפל את קוד לדוגמה של ה-codelab הזה. תיקייה בשם friendlyeats-android
תיווצר במחשב:
$ git clone https://github.com/firebase/friendlyeats-android
אם אין לכם git במחשב, אתם יכולים גם להוריד את הקוד ישירות מ-GitHub.
הוספת הגדרות Firebase
- במסוף Firebase, בוחרים באפשרות Project Overview (סקירת הפרויקט) בתפריט הניווט שמימין. לוחצים על הלחצן Android כדי לבחור את הפלטפורמה. כשמתבקשים להזין שם חבילה, משתמשים ב-
com.google.firebase.example.fireeats
- לוחצים על Register App (רישום האפליקציה) ופועלים לפי ההוראות להורדת הקובץ
google-services.json
ולהעברתו לתיקייהapp/
של הקוד שהורדתם. לאחר מכן לוחצים על Next.
ייבוא הפרויקט
פותחים את Android Studio. לוחצים על File (קובץ) > New (חדש) > Import Project (ייבוא פרויקט) ובוחרים בתיקייה friendlyeats-android.
4. הגדרת האמולטורים של Firebase
ב-codelab הזה תשתמשו בכלים לאמולטור מקומי ב-Firebase כדי להפעיל אמולציה מקומית של Cloud Firestore ושירותים אחרים של Firebase. כך תוכלו ליצור סביבת פיתוח מקומית בטוחה, מהירה וללא עלות כדי לפתח את האפליקציה.
התקנת Firebase CLI
קודם צריך להתקין את Firebase CLI. אם אתם משתמשים ב-macOS או ב-Linux, אתם יכולים להריץ את פקודת cURL הבאה:
curl -sL https://firebase.tools | bash
אם אתם משתמשים ב-Windows, תוכלו לקרוא את הוראות ההתקנה כדי לקבל קובץ בינארי עצמאי או להתקין דרך npm
.
אחרי שמתקינים את ה-CLI, הפעלת הפקודה firebase --version
אמורה להציג גרסה של 9.0.0
ומעלה:
$ firebase --version 9.0.0
התחברות
מריצים את הפקודה firebase login
כדי לקשר את ה-CLI לחשבון Google. ייפתח חלון חדש בדפדפן להשלמת תהליך הכניסה. חשוב לבחור את אותו חשבון שבו השתמשתם כשיצרתם את פרויקט Firebase קודם.
קישור הפרויקט
מתוך התיקייה friendlyeats-android
מריצים את הפקודה firebase use --add
כדי לקשר את הפרויקט המקומי לפרויקט Firebase. פועלים לפי ההנחיות כדי לבחור את הפרויקט שיצרתם קודם. אם מתבקשים לבחור כינוי, מזינים default
.
5. הפעלת האפליקציה
עכשיו הגיע הזמן להפעיל את Firebase Emulator Suite ואת אפליקציית Android FriendlyEats בפעם הראשונה.
הפעלת האמולטורים
בטרמינל, מתוך ספריית friendlyeats-android
, מריצים את הפקודה firebase emulators:start
כדי להפעיל את האמולטורים של Firebase. אתם אמורים לראות יומנים כאלה:
$ 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.
עכשיו יש לכם סביבת פיתוח מקומית מלאה שפועלת במחשב שלכם. חשוב להשאיר את הפקודה הזו פועלת למשך שאר ה-codelab, כי אפליקציית Android תצטרך להתחבר לאמולטורים.
קישור האפליקציה לאמולטורים
פותחים את הקבצים util/FirestoreInitializer.kt
ו-util/AuthInitializer.kt
ב-Android Studio. הקבצים האלה מכילים את הלוגיקה לחיבור ערכות ה-SDK של Firebase לאמולטורים המקומיים שפועלים במחשב שלכם, כשהאפליקציה מופעלת.
בשיטה create()
של המחלקה FirestoreInitializer
, בודקים את קטע הקוד הזה:
// Use emulators only in debug builds
if (BuildConfig.DEBUG) {
firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
}
אנחנו משתמשים ב-BuildConfig
כדי לוודא שאנחנו מתחברים לאמולטורים רק כשהאפליקציה שלנו פועלת במצב debug
. כשנרכיב את האפליקציה במצב release
, התנאי הזה יהיה false.
אנחנו רואים שהאפליקציה משתמשת בשיטה useEmulator(host, port)
כדי לקשר את Firebase SDK לאמולטור המקומי של Firestore. באפליקציה נשתמש ב-FirebaseUtil.getFirestore()
כדי לגשת למופע הזה של FirebaseFirestore
, כדי לוודא שאנחנו תמיד מתחברים לאמולטור של Firestore כשהאפליקציה פועלת במצב debug
.
הפעלת האפליקציה
אם הוספתם את הקובץ google-services.json
בצורה נכונה, הפרויקט אמור לעבור קומפילציה. ב-Android Studio, לוחצים על Build (בנייה) > Rebuild Project (בנייה מחדש של הפרויקט) ומוודאים שלא נשארו שגיאות.
ב-Android Studio, מריצים את האפליקציה ב-Android Emulator באמצעות Run. בהתחלה יוצג מסך הכניסה. אתם יכולים להשתמש בכל כתובת אימייל וסיסמה כדי להיכנס לאפליקציה. תהליך הכניסה הזה מתחבר לאמולטור האימות של Firebase, כך שלא מועברים פרטי כניסה אמיתיים.
עכשיו פותחים את ממשק המשתמש של האמולטורים על ידי מעבר לכתובת http://localhost:4000 בדפדפן האינטרנט. לאחר מכן לוחצים על הכרטיסייה אימות ומופיע החשבון שיצרתם:
אחרי שתשלימו את תהליך הכניסה, יוצג מסך הבית של האפליקציה:
בקרוב נוסיף נתונים כדי למלא את מסך הבית.
6. כתיבת נתונים ל-Firestore
בקטע הזה נכתוב נתונים ל-Firestore כדי לאכלס את מסך הבית שכרגע ריק.
אובייקט המודל הראשי באפליקציה שלנו הוא מסעדה (ראו model/Restaurant.kt
). הנתונים ב-Firestore מחולקים למסמכים, לקולקציות ולתת-קולקציות. כל מסעדה תישמר כמסמך באוסף ברמה העליונה שנקרא "restaurants"
. מידע נוסף על מודל הנתונים של Firestore זמין במאמרים על מסמכים ואוספים בתיעוד.
לצורך הדגמה, נוסיף לאפליקציה פונקציונליות ליצירת עשר מסעדות אקראיות כשלוחצים על הלחצן 'הוספת פריטים אקראיים' בתפריט האפשרויות הנוספות. פותחים את הקובץ MainFragment.kt
ומחליפים את התוכן בשיטה onAddItemsClicked()
בתוכן הבא:
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)
}
}
יש כמה נקודות חשובות שכדאי לדעת על הקוד שלמעלה:
- התחלנו בקבלת הפניה לאוסף
"restaurants"
. האוספים נוצרים באופן מרומז כשמוסיפים מסמכים, כך שלא היה צורך ליצור את האוסף לפני כתיבת הנתונים. - אפשר ליצור מסמכים באמצעות מחלקות נתונים של Kotlin, שבהן אנחנו משתמשים כדי ליצור כל מסמך של מסעדה.
- השיטה
add()
מוסיפה מסמך לקולקציה עם מזהה שנוצר אוטומטית, לכן לא היה צורך לציין מזהה ייחודי לכל מסעדה.
עכשיו מריצים שוב את האפליקציה ולוחצים על הלחצן Add Random Items (הוספת פריטים אקראיים) בתפריט האפשרויות הנוספות (בפינה השמאלית העליונה) כדי להפעיל את הקוד שכתבתם:
עכשיו פותחים את ממשק המשתמש של האמולטורים על ידי מעבר לכתובת http://localhost:4000 בדפדפן האינטרנט. אחר כך לוחצים על הכרטיסייה Firestore ורואים את הנתונים שהוספתם:
הנתונים האלה נשמרים באופן מקומי במחשב שלכם. למעשה, הפרויקט האמיתי שלכם עדיין לא מכיל מסד נתונים של Firestore. כלומר, אפשר לשנות את הנתונים האלה ולמחוק אותם בלי שיהיו לכך השלכות.
כל הכבוד, הרגע כתבת נתונים ל-Firestore! בשלב הבא נסביר איך להציג את הנתונים האלה באפליקציה.
7. הצגת נתונים מ-Firestore
בשלב הזה נלמד איך לאחזר נתונים מ-Firestore ולהציג אותם באפליקציה. השלב הראשון בקריאת נתונים מ-Firestore הוא ליצור Query
. פותחים את הקובץ MainFragment.kt
ומוסיפים את הקוד הבא לתחילת השיטה onViewCreated()
:
// Firestore
firestore = Firebase.firestore
// Get the 50 highest rated restaurants
query = firestore.collection("restaurants")
.orderBy("avgRating", Query.Direction.DESCENDING)
.limit(LIMIT.toLong())
עכשיו אנחנו רוצים להאזין לשאילתה, כדי לקבל את כל המסמכים התואמים ולקבל התראות על עדכונים עתידיים בזמן אמת. מכיוון שהמטרה הסופית שלנו היא לקשור את הנתונים האלה ל-RecyclerView
, אנחנו צריכים ליצור מחלקה RecyclerView.Adapter
שתאזין לנתונים.
פותחים את הכיתה FirestoreAdapter
, שכבר הוטמעה באופן חלקי. קודם כול, נגרום למתאם להטמיע את EventListener
ונגדיר את הפונקציה onEvent
כדי שהיא תוכל לקבל עדכונים לשאילתת Firestore:
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()
}
// ...
}
בטעינה הראשונית, מאזין יקבל אירוע ADDED
אחד לכל מסמך חדש. ככל שקבוצת התוצאות של השאילתה משתנה לאורך זמן, רכיב ה-listener יקבל יותר אירועים שמכילים את השינויים. עכשיו נסיים את ההטמעה של רכיב ההאזנה. קודם מוסיפים שלוש שיטות חדשות: onDocumentAdded
, onDocumentModified
ו-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)
}
לאחר מכן קוראים לשיטות החדשות האלה מ-onEvent
:
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()
}
לבסוף, מטמיעים את ה-method startListening()
כדי לצרף את ה-listener:
fun startListening() {
if (registration == null) {
registration = query.addSnapshotListener(this)
}
}
האפליקציה מוגדרת עכשיו באופן מלא לקריאת נתונים מ-Firestore. מריצים את האפליקציה שוב ואמורים לראות את המסעדות שהוספתם בשלב הקודם:
עכשיו חוזרים לממשק של האמולטור בדפדפן ועורכים את אחד משמות המסעדות. השינוי אמור להופיע באפליקציה כמעט באופן מיידי.
8. מיון וסינון של נתונים
בשלב הזה, האפליקציה מציגה את המסעדות עם הדירוג הכי גבוה מכלל המסעדות, אבל באפליקציה אמיתית של מסעדות המשתמש ירצה למיין ולסנן את הנתונים. לדוגמה, האפליקציה צריכה להיות מסוגלת להציג את התוצאות של השאילתות 'מסעדות פירות הים הכי טובות בפילדלפיה' או 'פיצה הכי זולה'.
כשלוחצים על הסרגל הלבן בחלק העליון של האפליקציה, מופיע תיבת דו-שיח של מסננים. בקטע הזה נשתמש בשאילתות Firestore כדי להפעיל את תיבת הדו-שיח הזו:
עכשיו נשנה את onFilter()
השיטה של MainFragment.kt
. השיטה הזו מקבלת אובייקט Filters
, שהוא אובייקט עזר שיצרנו כדי לתעד את הפלט של תיבת הדו-שיח של המסננים. נשנה את השיטה הזו ליצירת שאילתה מהמסננים:
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
}
בקטע הקוד שלמעלה אנחנו יוצרים אובייקט Query
על ידי צירוף סעיפים של where
ו-orderBy
כדי להתאים למסננים שצוינו.
מריצים את האפליקציה שוב ובוחרים את המסנן הבא כדי לראות את המסעדות הפופולריות ביותר במחירים נמוכים:
עכשיו אמורה להופיע רשימה מסוננת של מסעדות שמכילה רק אפשרויות במחירים נמוכים:
אם הגעתם עד כאן, סימן שבניתם אפליקציה עם פונקציונליות מלאה לצפייה בהמלצות למסעדות ב-Firestore. עכשיו אפשר למיין ולסנן מסעדות בזמן אמת. בקטעים הבאים נוסיף ביקורות למסעדות וכללי אבטחה לאפליקציה.
9. ארגון נתונים באוספי משנה
בקטע הזה נוסיף דירוגים לאפליקציה כדי שהמשתמשים יוכלו לכתוב ביקורות על המסעדות שהם הכי אוהבים (או הכי פחות אוהבים).
אוספים ותת-אוספים
עד עכשיו שמרנו את כל נתוני המסעדות באוסף ברמה העליונה שנקרא 'מסעדות'. כשמשתמש מדרג מסעדה, אנחנו רוצים להוסיף אובייקט Rating
חדש למסעדות. במשימה הזו נשתמש באוסף משנה. אפשר לחשוב על אוסף משנה כאוסף שמצורף למסמך. לכן, לכל מסמך של מסעדה תהיה קולקציית משנה של דירוגים, מלאה במסמכי דירוגים. אוספי משנה עוזרים לארגן את הנתונים בלי להגדיל את המסמכים או לדרוש שאילתות מורכבות.
כדי לגשת לאוסף משנה, קוראים ל-.collection()
במסמך האב:
val subRef = firestore.collection("restaurants")
.document("abc123")
.collection("ratings")
אפשר לגשת לתת-אוסף ולשאול עליו שאילתות בדיוק כמו באוסף ברמה העליונה, ואין מגבלות גודל או שינויים בביצועים. כאן אפשר לקרוא מידע נוסף על מודל הנתונים של Firestore.
כתיבת נתונים בעסקה
כדי להוסיף Rating
לקולקציית המשנה המתאימה, צריך רק לקרוא ל-.add()
, אבל צריך גם לעדכן את הדירוג הממוצע ואת מספר הדירוגים של אובייקט Restaurant
כדי לשקף את הנתונים החדשים. אם נשתמש בפעולות נפרדות כדי לבצע את שני השינויים האלה, יכולות להתרחש כמה תופעות של תנאי מירוץ שיובילו לנתונים לא עדכניים או שגויים.
כדי לוודא שהדירוגים נוספים בצורה תקינה, נשתמש בעסקה כדי להוסיף דירוגים למסעדה. העסקה הזו תבצע כמה פעולות:
- קוראים את הדירוג הנוכחי של המסעדה ומחשבים את הדירוג החדש
- הוספת הדירוג לאוסף המשנה
- עדכון הדירוג הממוצע של המסעדה ומספר הדירוגים
פותחים את RestaurantDetailFragment.kt
ומטמיעים את הפונקציה 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
}
}
הפונקציה addRating()
מחזירה Task
שמייצג את העסקה כולה. בפונקציה onRating()
, מוסיפים listener למשימה כדי להגיב לתוצאה של העסקה.
עכשיו מריצים את האפליקציה שוב ולוחצים על אחת המסעדות. אמור להופיע מסך הפרטים של המסעדה. לוחצים על הלחצן + כדי להתחיל להוסיף ביקורת. כדי להוסיף ביקורת, בוחרים מספר כוכבים ומזינים טקסט.
לחיצה על שליחה תתחיל את העסקה. בסיום העסקה, הביקורת שלכם תוצג למטה וספירת הביקורות של המסעדה תתעדכן:
מזל טוב! עכשיו יש לכם אפליקציה לנייד לביקורת על מסעדות, שמבוססת על Cloud Firestore וכוללת תכונות חברתיות ומקומיות. שמעתי שהם מאוד פופולריים בימים האלה.
10. אבטחת הנתונים
עד עכשיו לא התייחסנו לאבטחה של האפליקציה הזו. איך אנחנו יודעים שהמשתמשים יכולים רק לקרוא ולכתוב את הנתונים הנכונים שלהם? מסדי נתונים של Firestore מאובטחים באמצעות קובץ הגדרה שנקרא כללי אבטחה.
פותחים את הקובץ firestore.rules
ומחליפים את התוכן שלו בתוכן הבא:
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;
}
}
}
}
הכללים האלה מגבילים את הגישה כדי להבטיח שהלקוחות יבצעו רק שינויים בטוחים. לדוגמה, עדכונים במסמך של מסעדה יכולים לשנות רק את הדירוגים, ולא את השם או נתונים אחרים שלא ניתן לשנות. אפשר ליצור דירוגים רק אם מזהה המשתמש תואם למשתמש המחובר, וכך נמנעת התחזות.
מידע נוסף על כללי אבטחה זמין במסמכי התיעוד.
11. סיכום
יצרתם עכשיו אפליקציה עם כל התכונות על בסיס Firestore. למדתם על התכונות החשובות ביותר של Firestore, כולל:
- מסמכים ואוספים
- קריאה וכתיבה של נתונים
- מיון וסינון באמצעות שאילתות
- אוספי משנה
- עסקאות
מידע נוסף
כדי להמשיך ללמוד על Firestore, הנה כמה מקומות טובים להתחיל מהם:
אפליקציית המסעדה ב-codelab הזה מבוססת על אפליקציית הדוגמה Friendly Eats. כאן אפשר לעיין בקוד המקור של האפליקציה.
אופציונלי: פריסה בסביבת ייצור
עד עכשיו נעשה באפליקציה הזו שימוש רק בחבילת הכלים לאמולטור מקומי ב-Firebase. אם רוצים ללמוד איך פורסים את האפליקציה הזו לפרויקט Firebase אמיתי, ממשיכים לשלב הבא.
12. (אופציונלי) פריסת האפליקציה
עד עכשיו האפליקציה הזו הייתה מקומית לחלוטין, וכל הנתונים נכללים בחבילת Firebase Emulator Suite. בקטע הזה נסביר איך להגדיר את פרויקט Firebase כדי שהאפליקציה הזו תפעל בסביבת פרודקשן.
אימות ב-Firebase
במסוף Firebase, עוברים לקטע אימות ולוחצים על תחילת העבודה. עוברים לכרטיסייה שיטת כניסה ובוחרים באפשרות אימייל/סיסמה מתוך ספקי מערכת.
מפעילים את אמצעי הכניסה אימייל/סיסמה ולוחצים על שמירה.
Firestore
יצירת מסד נתונים
עוברים לקטע Firestore Database (מסד נתונים של Firestore) במסוף ולוחצים על Create Database (יצירת מסד נתונים):
- כשתוצג לכם הנחיה לגבי כללי אבטחה, תצטרכו לבחור באפשרות מצב הפקה כדי שנוכל לעדכן את הכללים האלה בקרוב.
- בוחרים את מיקום מסד הנתונים שרוצים להשתמש בו באפליקציה. חשוב לזכור שהבחירה של מיקום מסד הנתונים היא קבועה, ואם תרצו לשנות אותה תצטרכו ליצור פרויקט חדש. מידע נוסף על בחירת מיקום לפרויקט זמין במסמכי התיעוד.
פריסת כללים
כדי לפרוס את כללי האבטחה שכתבתם קודם, מריצים את הפקודה הבאה בספרייה של ה-codelab:
$ firebase deploy --only firestore:rules
הפעולה הזו תפרוס את התוכן של firestore.rules
בפרויקט שלכם. כדי לוודא זאת, אפשר לעבור לכרטיסייה כללים במסוף.
פריסת אינדקסים
לאפליקציית FriendlyEats יש מיון וסינון מורכבים שדורשים מספר אינדקסים מורכבים בהתאמה אישית. אפשר ליצור אותם ידנית במסוף Firebase, אבל יותר פשוט לכתוב את ההגדרות שלהם בקובץ firestore.indexes.json
ולפרוס אותם באמצעות Firebase CLI.
אם פותחים את הקובץ firestore.indexes.json
, אפשר לראות שהאינדקסים הנדרשים כבר סופקו:
{
"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": []
}
כדי לפרוס את האינדקסים האלה, מריצים את הפקודה הבאה:
$ firebase deploy --only firestore:indexes
שימו לב: יצירת האינדקס לא מתבצעת באופן מיידי, ואפשר לעקוב אחרי ההתקדמות במסוף Firebase.
הגדרת האפליקציה
בקובצי util/FirestoreInitializer.kt
ו-util/AuthInitializer.kt
הגדרנו את Firebase SDK להתחבר לאמולטורים במצב ניפוי באגים:
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
}
אם רוצים לבדוק את האפליקציה עם פרויקט Firebase אמיתי, אפשר:
- יוצרים את האפליקציה במצב הפצה ומריצים אותה במכשיר.
- מחליפים באופן זמני את
BuildConfig.DEBUG
ב-false
ומריצים את האפליקציה שוב.
שימו לב: יכול להיות שתצטרכו לצאת מהאפליקציה ולהיכנס אליה שוב כדי להתחבר בצורה תקינה לסביבת הייצור.