Codelab สำหรับ iOS ใน Cloud Firestore

Codelab ของ Cloud Firestore สำหรับ iOS

เกี่ยวกับ Codelab นี้

subjectอัปเดตล่าสุดเมื่อ ก.ย. 15, 2025
account_circleเขียนโดย Googler

1 ภาพรวม

ในโค้ดแล็บนี้ คุณจะได้สร้างแอปแนะนำร้านอาหารที่ใช้ Firestore เป็นแบ็กเอนด์บน iOS ใน Swift คุณจะได้เรียนรู้วิธีต่อไปนี้

  1. อ่านและเขียนข้อมูลไปยัง Firestore จากแอป iOS
  2. ฟังการเปลี่ยนแปลงข้อมูล Firestore แบบเรียลไทม์
  3. ใช้การตรวจสอบสิทธิ์ Firebase และกฎการรักษาความปลอดภัยเพื่อรักษาความปลอดภัยของข้อมูล Firestore
  4. เขียนการค้นหา Firestore ที่ซับซ้อน

ข้อกำหนดเบื้องต้น

โปรดตรวจสอบว่าคุณได้ติดตั้งสิ่งต่อไปนี้ก่อนเริ่ม Codelab นี้

  • Xcode เวอร์ชัน 14.0 (หรือสูงกว่า)
  • CocoaPods 1.12.0 (หรือสูงกว่า)

2 รับโปรเจ็กต์ตัวอย่าง

ดาวน์โหลดรหัส

เริ่มต้นด้วยการโคลนโปรเจ็กต์ตัวอย่างและเรียกใช้ pod update ในไดเรกทอรีโปรเจ็กต์

git clone https://github.com/firebase/friendlyeats-ios
cd friendlyeats-ios
pod update

เปิด FriendlyEats.xcworkspace ใน Xcode แล้วเรียกใช้ (Cmd+R) แอปควรคอมไพล์ได้อย่างถูกต้องและขัดข้องทันทีเมื่อเปิดใช้ เนื่องจากไม่มีไฟล์ GoogleService-Info.plist เราจะแก้ไขในขั้นตอนถัดไป

3 ตั้งค่า Firebase

สร้างโปรเจ็กต์ Firebase

  1. ลงชื่อเข้าใช้คอนโซล Firebase โดยใช้บัญชี Google
  2. คลิกปุ่มเพื่อสร้างโปรเจ็กต์ใหม่ แล้วป้อนชื่อโปรเจ็กต์ (เช่น FriendlyEats)
  3. คลิกต่อไป
  4. หากได้รับแจ้ง ให้อ่านและยอมรับข้อกำหนดของ Firebase แล้วคลิกต่อไป
  5. (ไม่บังคับ) เปิดใช้ความช่วยเหลือจาก AI ในคอนโซล Firebase (เรียกว่า "Gemini ใน Firebase")
  6. สำหรับ Codelab นี้ คุณไม่จำเป็นต้องใช้ Google Analytics ดังนั้นให้ปิดตัวเลือก Google Analytics
  7. คลิกสร้างโปรเจ็กต์ รอให้ระบบจัดสรรโปรเจ็กต์ แล้วคลิกดำเนินการต่อ

เชื่อมต่อแอปกับ Firebase

สร้างแอป iOS ในโปรเจ็กต์ Firebase ใหม่

ดาวน์โหลดไฟล์ GoogleService-Info.plist ของโปรเจ็กต์จากคอนโซล Firebase แล้วลากไปยังรูทของโปรเจ็กต์ Xcode เรียกใช้โปรเจ็กต์อีกครั้งเพื่อให้แน่ใจว่าแอปได้รับการกำหนดค่าอย่างถูกต้องและไม่ขัดข้องเมื่อเปิดตัวอีกต่อไป หลังจากเข้าสู่ระบบแล้ว คุณควรเห็นหน้าจอว่างเปล่าเหมือนตัวอย่างด้านล่าง หากเข้าสู่ระบบไม่ได้ โปรดตรวจสอบว่าคุณได้เปิดใช้เมธอดการลงชื่อเข้าใช้ด้วยอีเมล/รหัสผ่านในคอนโซล Firebase ภายใต้การตรวจสอบสิทธิ์แล้ว

d5225270159c040b.png

4 เขียนข้อมูลไปยัง Firestore

ในส่วนนี้ เราจะเขียนข้อมูลบางอย่างลงใน Firestore เพื่อให้เราสามารถแสดงข้อมูลใน UI ของแอปได้ คุณทำได้ด้วยตนเองผ่านคอนโซล Firebase แต่เราจะดำเนินการในแอปเองเพื่อแสดงการเขียน Firestore ขั้นพื้นฐาน

ออบเจ็กต์โมเดลหลักในแอปของเราคือร้านอาหาร ข้อมูล Firestore จะแบ่งออกเป็นเอกสาร คอลเล็กชัน และคอลเล็กชันย่อย เราจะจัดเก็บร้านอาหารแต่ละร้านเป็นเอกสารในคอลเล็กชันระดับบนสุดที่ชื่อ restaurants หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับโมเดลข้อมูล Firestore โปรดอ่านเกี่ยวกับเอกสารและคอลเล็กชันในเอกสารประกอบ

ก่อนที่จะเพิ่มข้อมูลลงใน Firestore ได้ เราต้องรับการอ้างอิงไปยังคอลเล็กชันร้านอาหาร เพิ่มโค้ดต่อไปนี้ในลูป for ด้านในในเมธอด RestaurantsTableViewController.didTapPopulateButton(_:)

let collection = Firestore.firestore().collection("restaurants")

ตอนนี้เรามีข้อมูลอ้างอิงของคอลเล็กชันแล้ว เราจึงเขียนข้อมูลบางอย่างได้ เพิ่มโค้ดต่อไปนี้ต่อจากบรรทัดสุดท้ายของโค้ดที่เราเพิ่ม

let collection = Firestore.firestore().collection("restaurants")

// ====== ADD THIS ======
let restaurant
= Restaurant(
  name
: name,
  category
: category,
  city
: city,
  price
: price,
  ratingCount
: 0,
  averageRating
: 0
)

collection
.addDocument(data: restaurant.dictionary)

โค้ดด้านบนจะเพิ่มเอกสารใหม่ลงในคอลเล็กชันร้านอาหาร ข้อมูลเอกสารมาจากพจนานุกรมซึ่งเราได้รับจากโครงสร้างร้านอาหาร

เรามาถึงขั้นตอนสุดท้ายแล้ว ก่อนที่จะเขียนเอกสารลงใน Firestore ได้ เราต้องเปิดกฎความปลอดภัยของ Firestore และอธิบายว่าผู้ใช้รายใดควรมีสิทธิ์เขียนส่วนใดของฐานข้อมูล ตอนนี้เราจะอนุญาตให้เฉพาะผู้ใช้ที่ตรวจสอบสิทธิ์แล้วเท่านั้นที่อ่านและเขียนไปยังทั้งฐานข้อมูลได้ ซึ่งอาจเป็นข้อจำกัดที่มากเกินไปสำหรับแอปเวอร์ชันที่ใช้งานจริง แต่ในระหว่างกระบวนการสร้างแอป เราต้องการข้อจำกัดที่ยืดหยุ่นพอที่จะไม่ทำให้เกิดปัญหาการตรวจสอบสิทธิ์อยู่ตลอดเวลาขณะทดลอง ในช่วงท้ายของโค้ดแล็บนี้ เราจะพูดถึงวิธีเพิ่มความแข็งแกร่งให้กับกฎความปลอดภัยและจำกัดความเป็นไปได้ของการอ่านและการเขียนโดยไม่ตั้งใจ

ในแท็บกฎของคอนโซล Firebase ให้เพิ่มกฎต่อไปนี้ แล้วคลิกเผยแพร่

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurants/{any}/ratings/{rating} {
      // Users can only write ratings with their user ID
      allow read;
      allow write: if request.auth != null
                   && request.auth.uid == request.resource.data.userId;
    }

    match /restaurants/{any} {
      // Only authenticated users can read or write data
      allow read, write: if request.auth != null;
    }
  }
}

เราจะพูดถึงกฎการรักษาความปลอดภัยอย่างละเอียดในภายหลัง แต่หากคุณรีบร้อน โปรดดูเอกสารประกอบเกี่ยวกับกฎการรักษาความปลอดภัย

เรียกใช้แอปและลงชื่อเข้าใช้ จากนั้นแตะปุ่ม "ป้อนข้อมูล" ที่ด้านซ้ายบน ซึ่งจะสร้างเอกสารร้านอาหารเป็นชุด แต่คุณจะยังไม่เห็นเอกสารนี้ในแอป

จากนั้นไปที่แท็บข้อมูล Firestore ในคอนโซล Firebase ตอนนี้คุณควรเห็นรายการใหม่ในคอลเล็กชันร้านอาหารแล้ว

Screen Shot 2017-07-06 at 12.45.38 PM.png

ขอแสดงความยินดี คุณเพิ่งเขียนข้อมูลลงใน Firestore จากแอป iOS ในส่วนถัดไป คุณจะได้เรียนรู้วิธีดึงข้อมูลจาก Firestore และแสดงในแอป

5 แสดงข้อมูลจาก Firestore

ในส่วนนี้ คุณจะได้เรียนรู้วิธีดึงข้อมูลจาก Firestore และแสดงในแอป ขั้นตอนสำคัญ 2 อย่างคือการสร้างการค้นหาและการเพิ่มเครื่องมือฟังภาพรวม Listener นี้จะได้รับการแจ้งเตือนเกี่ยวกับข้อมูลที่มีอยู่ทั้งหมดที่ตรงกับการค้นหาและได้รับการอัปเดตแบบเรียลไทม์

ก่อนอื่น มาสร้างคำค้นหาที่จะแสดงรายการร้านอาหารเริ่มต้นที่ไม่มีการกรองกัน ดูการติดตั้งใช้งาน RestaurantsTableViewController.baseQuery()

return Firestore.firestore().collection("restaurants").limit(to: 50)

คำค้นหานี้จะดึงร้านอาหารได้สูงสุด 50 แห่งจากคอลเล็กชันระดับบนสุดที่ชื่อ "restaurants" ตอนนี้เรามีคำค้นหาแล้ว เราจึงต้องแนบเครื่องมือฟังการสแนปชอตเพื่อโหลดข้อมูลจาก Firestore ลงในแอป เพิ่มโค้ดต่อไปนี้ลงในเมธอด RestaurantsTableViewController.observeQuery() ทันทีหลังจากเรียกใช้ stopObserving()

listener = query.addSnapshotListener { [unowned self] (snapshot, error) in
  guard let snapshot = snapshot else {
    print("Error fetching snapshot results: \(error!)")
    return
  }
  let models = snapshot.documents.map { (document) -> Restaurant in
    if let model = Restaurant(dictionary: document.data()) {
      return model
    } else {
      // Don't use fatalError here in a real app.
      fatalError("Unable to initialize type \(Restaurant.self) with dictionary \(document.data())")
    }
  }
  self.restaurants = models
  self.documents = snapshot.documents

  if self.documents.count > 0 {
    self.tableView.backgroundView = nil
  } else {
    self.tableView.backgroundView = self.backgroundView
  }

  self.tableView.reloadData()
}

โค้ดด้านบนจะดาวน์โหลดคอลเล็กชันจาก Firestore และจัดเก็บไว้ในอาร์เรย์ในเครื่อง addSnapshotListener(_:) จะเพิ่ม Listener ของสแนปชอตลงในการค้นหาที่จะอัปเดต ViewController ทุกครั้งที่ข้อมูลมีการเปลี่ยนแปลงในเซิร์ฟเวอร์ เราจะได้รับการอัปเดตโดยอัตโนมัติและไม่ต้องพุชการเปลี่ยนแปลงด้วยตนเอง โปรดทราบว่าระบบจะเรียกใช้เครื่องมือฟังภาพรวมนี้ได้ทุกเมื่ออันเป็นผลมาจากการเปลี่ยนแปลงฝั่งเซิร์ฟเวอร์ ดังนั้นแอปของเราจึงต้องรองรับการเปลี่ยนแปลงได้

หลังจากแมปพจนานุกรมกับโครงสร้าง (ดู Restaurant.swift) การแสดงข้อมูลก็เป็นเพียงการกำหนดพร็อพเพอร์ตี้ของมุมมอง 2-3 รายการ เพิ่มบรรทัดต่อไปนี้ลงใน RestaurantTableViewCell.populate(restaurant:) ใน RestaurantsTableViewController.swift

nameLabel.text = restaurant.name
cityLabel
.text = restaurant.city
categoryLabel
.text = restaurant.category
starsView
.rating = Int(restaurant.averageRating.rounded())
priceLabel
.text = priceString(from: restaurant.price)

วิธีการป้อนข้อมูลนี้เรียกใช้จากเมธอด tableView(_:cellForRowAtIndexPath:) ของแหล่งข้อมูลมุมมองตาราง ซึ่งจะดูแลการแมปคอลเล็กชันของประเภทค่าจากก่อนหน้าไปยังเซลล์มุมมองตารางแต่ละเซลล์

เรียกใช้แอปอีกครั้งและตรวจสอบว่าร้านอาหารที่เราเห็นก่อนหน้านี้ในคอนโซลปรากฏในโปรแกรมจำลองหรืออุปกรณ์แล้ว หากทำส่วนนี้สำเร็จ ตอนนี้แอปของคุณจะอ่านและเขียนข้อมูลด้วย Cloud Firestore ได้แล้ว

391c0259bf05ac25.png

6 การจัดเรียงและการกรองข้อมูล

ปัจจุบันแอปของเราแสดงรายชื่อร้านอาหาร แต่ผู้ใช้ไม่สามารถกรองตามความต้องการได้ ในส่วนนี้ คุณจะได้ใช้การค้นหาขั้นสูงของ Firestore เพื่อเปิดใช้การกรอง

ต่อไปนี้คือตัวอย่างคําค้นหาอย่างง่ายเพื่อดึงร้านอาหารติ่มซำทั้งหมด

let filteredQuery = query.whereField("category", isEqualTo: "Dim Sum")

ตามชื่อของฟังก์ชัน whereField(_:isEqualTo:) เมธอดจะทำให้การดาวน์โหลดคำค้นหาของเรามีเฉพาะสมาชิกของคอลเล็กชันที่มีช่องตรงตามข้อจำกัดที่เราตั้งไว้ ในกรณีนี้ ระบบจะดาวน์โหลดเฉพาะร้านอาหารที่ category เป็น "Dim Sum"

ในแอปนี้ ผู้ใช้สามารถเชื่อมโยงตัวกรองหลายรายการเพื่อสร้างคำค้นหาที่เฉพาะเจาะจง เช่น "พิซซ่าในซานฟรานซิสโก" หรือ "อาหารทะเลในลอสแอนเจลิสที่จัดเรียงตามความนิยม"

เปิด RestaurantsTableViewController.swift แล้วเพิ่มโค้ดบล็อกต่อไปนี้ตรงกลางของ query(withCategory:city:price:sortBy:)

if let category = category, !category.isEmpty {
  filtered
= filtered.whereField("category", isEqualTo: category)
}

if let city = city, !city.isEmpty {
  filtered
= filtered.whereField("city", isEqualTo: city)
}

if let price = price {
  filtered
= filtered.whereField("price", isEqualTo: price)
}

if let sortBy = sortBy, !sortBy.isEmpty {
  filtered
= filtered.order(by: sortBy)
}

ข้อมูลโค้ดด้านบนจะเพิ่มคําสั่ง whereField และ order หลายรายการเพื่อสร้างคําค้นหาแบบผสมรายการเดียวตามข้อมูลที่ผู้ใช้ป้อน ตอนนี้การค้นหาจะแสดงเฉพาะร้านอาหารที่ตรงกับความต้องการของผู้ใช้เท่านั้น

เรียกใช้โปรเจ็กต์และตรวจสอบว่าคุณกรองตามราคา เมือง และหมวดหมู่ได้ (ตรวจสอบว่าได้พิมพ์ชื่อหมวดหมู่และเมืองอย่างถูกต้อง) ขณะทดสอบ คุณอาจเห็นข้อผิดพลาดในบันทึกที่มีลักษณะดังนี้

Error fetching snapshot results: Error Domain=io.grpc Code=9
"The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=..."
UserInfo={NSLocalizedDescription=The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=...}

เนื่องจาก Firestore ต้องใช้ดัชนีสำหรับการค้นหาแบบผสมส่วนใหญ่ การกำหนดให้ใช้ดัชนีในการค้นหาจะช่วยให้ Firestore ทำงานได้อย่างรวดเร็วเมื่อมีการปรับขนาด การเปิดลิงก์จากข้อความแสดงข้อผิดพลาดจะเปิด UI การสร้างดัชนีในคอนโซล Firebase โดยอัตโนมัติพร้อมกับพารามิเตอร์ที่ถูกต้อง ดูข้อมูลเพิ่มเติมเกี่ยวกับดัชนีใน Firestore ได้ที่เอกสารประกอบ

7 การเขียนข้อมูลในธุรกรรม

ในส่วนนี้ เราจะเพิ่มความสามารถให้ผู้ใช้ส่งรีวิวไปยังร้านอาหารได้ จนถึงตอนนี้ การเขียนทั้งหมดของเราเป็นแบบอะตอมมิกและค่อนข้างเรียบง่าย หากมีข้อผิดพลาดเกิดขึ้น เราอาจแจ้งให้ผู้ใช้ลองอีกครั้งหรือลองอีกครั้งโดยอัตโนมัติ

หากต้องการเพิ่มคะแนนให้กับร้านอาหาร เราต้องประสานงานการอ่านและการเขียนหลายครั้ง โดยคุณต้องส่งรีวิวก่อน จากนั้นจึงอัปเดตจำนวนการให้คะแนนและคะแนนเฉลี่ยของร้านอาหาร หากการดำเนินการอย่างใดอย่างหนึ่งล้มเหลว แต่การดำเนินการอีกอย่างหนึ่งสำเร็จ เราจะอยู่ในสถานะที่ไม่สอดคล้องกัน ซึ่งข้อมูลในส่วนหนึ่งของฐานข้อมูลไม่ตรงกับข้อมูลในอีกส่วนหนึ่ง

โชคดีที่ Firestore มีฟังก์ชันการทำธุรกรรมที่ช่วยให้เราอ่านและเขียนหลายครั้งในการดำเนินการแบบอะตอมเดียวได้ ซึ่งจะช่วยให้ข้อมูลของเรายังคงสอดคล้องกัน

เพิ่มโค้ดต่อไปนี้ใต้การประกาศ let ทั้งหมดใน RestaurantDetailViewController.reviewController(_:didSubmitFormWithReview:)

let firestore = Firestore.firestore()
firestore
.runTransaction({ (transaction, errorPointer) -> Any? in

 
// Read data from Firestore inside the transaction, so we don't accidentally
 
// update using stale client data. Error if we're unable to read here.
  let restaurantSnapshot
: DocumentSnapshot
 
do {
   
try restaurantSnapshot = transaction.getDocument(reference)
 
} catch let error as NSError {
    errorPointer
?.pointee = error
   
return nil
 
}

 
// Error if the restaurant data in Firestore has somehow changed or is malformed.
  guard let data
= restaurantSnapshot.data(),
        let restaurant
= Restaurant(dictionary: data) else {

    let error
= NSError(domain: "FireEatsErrorDomain", code: 0, userInfo: [
     
NSLocalizedDescriptionKey: "Unable to write to restaurant at Firestore path: \(reference.path)"
   
])
    errorPointer
?.pointee = error
   
return nil
 
}

 
// Update the restaurant's rating and rating count and post the new review at the
 
// same time.
  let newAverage
= (Float(restaurant.ratingCount) * restaurant.averageRating + Float(review.rating))
     
/ Float(restaurant.ratingCount + 1)

  transaction
.setData(review.dictionary, forDocument: newReviewReference)
  transaction
.updateData([
   
"numRatings": restaurant.ratingCount + 1,
   
"avgRating": newAverage
 
], forDocument: reference)
 
return nil
}) { (object, error) in
 
if let error = error {
   
print(error)
 
} else {
   
// Pop the review controller on success
   
if self.navigationController?.topViewController?.isKind(of: NewReviewViewController.self) ?? false {
     
self.navigationController?.popViewController(animated: true)
   
}
 
}
}

ภายในบล็อกการอัปเดต การดำเนินการทั้งหมดที่เราทำโดยใช้ออบเจ็กต์ธุรกรรมจะถือเป็นการอัปเดตแบบอะตอมเดียวโดย Firestore หากอัปเดตในเซิร์ฟเวอร์ไม่สำเร็จ Firestore จะลองอีกครั้งโดยอัตโนมัติ 2-3 ครั้ง ซึ่งหมายความว่าเงื่อนไขข้อผิดพลาดของเรามักจะเป็นข้อผิดพลาดเดียวที่เกิดขึ้นซ้ำๆ เช่น หากอุปกรณ์ออฟไลน์โดยสมบูรณ์ หรือผู้ใช้ไม่มีสิทธิ์เขียนไปยังเส้นทางที่พยายามเขียน

8 กฎความปลอดภัย

ผู้ใช้แอปของเราไม่ควรอ่านและเขียนข้อมูลทุกชิ้นในฐานข้อมูลของเราได้ เช่น ทุกคนควรดูคะแนนของร้านอาหารได้ แต่ควรอนุญาตให้เฉพาะผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์แล้วเท่านั้นที่โพสต์คะแนนได้ การเขียนโค้ดที่ดีในไคลเอ็นต์ยังไม่เพียงพอ เราต้องระบุโมเดลความปลอดภัยของข้อมูลในแบ็กเอนด์เพื่อให้ปลอดภัยอย่างสมบูรณ์ ในส่วนนี้ เราจะมาดูวิธีใช้กฎความปลอดภัยของ Firebase เพื่อปกป้องข้อมูล

ก่อนอื่น มาดูรายละเอียดกฎความปลอดภัยที่เราเขียนไว้ตอนเริ่ม Codelab กัน เปิดคอนโซล Firebase แล้วไปที่ฐานข้อมูล > กฎในแท็บ Firestore

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurants/{any}/ratings/{rating} {
      // Users can only write ratings with their user ID
      allow read;
      allow write: if request.auth != null
                   && request.auth.uid == request.resource.data.userId;
    }

    match /restaurants/{any} {
      // Only authenticated users can read or write data
      allow read, write: if request.auth != null;
    }
  }
}

ตัวแปร request ในกฎเป็นตัวแปรส่วนกลางที่ใช้ได้ในทุกกฎ และเงื่อนไขที่เราเพิ่มเข้าไปจะช่วยให้มั่นใจได้ว่าคำขอจะได้รับการตรวจสอบสิทธิ์ก่อนที่จะอนุญาตให้ผู้ใช้ทำสิ่งใดก็ตาม ซึ่งจะช่วยป้องกันไม่ให้ผู้ใช้ที่ไม่ได้รับการตรวจสอบสิทธิ์ใช้ Firestore API เพื่อทำการเปลี่ยนแปลงข้อมูลโดยไม่ได้รับอนุญาต นี่เป็นจุดเริ่มต้นที่ดี แต่เราสามารถใช้กฎ Firestore เพื่อทำสิ่งต่างๆ ที่มีประสิทธิภาพมากขึ้นได้

เราต้องการจำกัดการเขียนรีวิวเพื่อให้รหัสผู้ใช้ของรีวิวต้องตรงกับรหัสของผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์ ซึ่งช่วยให้มั่นใจได้ว่าผู้ใช้จะแอบอ้างเป็นบุคคลอื่นและเขียนรีวิวที่เป็นการฉ้อโกงไม่ได้

คำสั่งจับคู่แรกจะจับคู่คอลเล็กชันย่อยชื่อ ratings ของเอกสารใดก็ตามที่อยู่ในคอลเล็กชัน restaurants จากนั้นallow writeเงื่อนไขจะป้องกันไม่ให้ส่งรีวิวหากรหัสผู้ใช้ของรีวิวไม่ตรงกับรหัสของผู้ใช้ คำสั่งจับคู่ที่ 2 อนุญาตให้ผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์อ่านและเขียนร้านอาหารลงในฐานข้อมูล

ซึ่งใช้ได้ดีกับรีวิวของเรา เนื่องจากเราได้ใช้กฎความปลอดภัยเพื่อระบุการรับประกันโดยนัยที่เราเขียนไว้ในแอปก่อนหน้านี้อย่างชัดเจน นั่นคือผู้ใช้จะเขียนได้เฉพาะรีวิวของตนเอง หากเราเพิ่มฟังก์ชันแก้ไขหรือลบรีวิว ชุดกฎเดียวกันนี้จะป้องกันไม่ให้ผู้ใช้แก้ไขหรือลบรีวิวของผู้ใช้รายอื่นด้วย แต่คุณยังใช้กฎ Firestore ในลักษณะที่ละเอียดยิ่งขึ้นเพื่อจำกัดการเขียนในแต่ละช่องภายในเอกสารแทนที่จะเป็นทั้งเอกสารได้ด้วย เราสามารถใช้ข้อมูลนี้เพื่ออนุญาตให้ผู้ใช้อัปเดตเฉพาะคะแนน คะแนนเฉลี่ย และจำนวนคะแนนของร้านอาหารได้ ซึ่งจะช่วยลดโอกาสที่ผู้ใช้ที่เป็นอันตรายจะแก้ไขชื่อหรือตำแหน่งของร้านอาหาร

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurants/{restaurant} {
      match /ratings/{rating} {
        allow read: if request.auth != null;
        allow write: if request.auth != null
                     && request.auth.uid == request.resource.data.userId;
      }

      allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null
                    && request.resource.data.name == resource.data.name
                    && request.resource.data.city == resource.data.city
                    && request.resource.data.price == resource.data.price
                    && request.resource.data.category == resource.data.category;
    }
  }
}

ในที่นี้ เราได้แยกสิทธิ์เขียนออกเป็นสร้างและอัปเดตเพื่อให้ระบุการดำเนินการที่ควรได้รับอนุญาตได้ชัดเจนยิ่งขึ้น ผู้ใช้ทุกคนเขียนร้านอาหารลงในฐานข้อมูลได้ โดยยังคงฟังก์ชันการทำงานของปุ่มป้อนข้อมูลที่เราสร้างขึ้นเมื่อเริ่ม Codelab แต่เมื่อเขียนร้านอาหารแล้ว จะเปลี่ยนชื่อ สถานที่ ราคา และหมวดหมู่ของร้านอาหารไม่ได้ กล่าวอย่างเจาะจงคือ กฎข้อสุดท้ายกำหนดให้การดำเนินการอัปเดตร้านอาหารต้องคงชื่อ เมือง ราคา และหมวดหมู่ของฟิลด์ที่มีอยู่แล้วในฐานข้อมูลไว้เหมือนเดิม

ดูข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่ดำเนินการกับกฎความปลอดภัยได้ที่เอกสารประกอบ

9 บทสรุป

ในโค้ดแล็บนี้ คุณได้เรียนรู้วิธีอ่านและเขียนข้อมูลพื้นฐานและขั้นสูงด้วย Firestore รวมถึงวิธีรักษาความปลอดภัยในการเข้าถึงข้อมูลด้วยกฎการรักษาความปลอดภัย คุณดูโซลูชันทั้งหมดได้ในcodelab-complete Branch

ดูข้อมูลเพิ่มเติมเกี่ยวกับ Firestore ได้ที่แหล่งข้อมูลต่อไปนี้