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

1. ภาพรวม

เป้าหมาย

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

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

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

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

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

2. สร้างโปรเจ็กต์คอนโซล Firebase

เพิ่ม Firebase ลงในโปรเจ็กต์

  1. ไปที่คอนโซล Firebase
  2. เลือกสร้างโปรเจ็กต์ใหม่ แล้วตั้งชื่อโปรเจ็กต์เป็น "Firestore iOS Codelab"

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

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

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

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

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

ตั้งค่า Firebase

ทำตามเอกสารประกอบเพื่อสร้างโปรเจ็กต์ Firestore ใหม่ เมื่อสร้างโปรเจ็กต์แล้ว ให้ดาวน์โหลดไฟล์ 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)

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

เราเกือบจะเสร็จแล้ว ก่อนที่จะเขียนเอกสารลงใน 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" เมื่อเรามีคำถามแล้ว เราจะต้องแนบ Listener สแนปชอตเพื่อโหลดข้อมูลจาก 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 ของสแนปชอตลงในคําค้นหาที่จะอัปเดตตัวควบคุมมุมมองทุกครั้งที่มีการเปลี่ยนแปลงข้อมูลในเซิร์ฟเวอร์ เราจะได้รับการอัปเดตโดยอัตโนมัติและไม่ต้องพุชการเปลี่ยนแปลงด้วยตนเอง โปรดทราบว่าระบบอาจเรียกใช้เครื่องมือรับข้อมูลภาพนิ่งนี้ได้ทุกเมื่อเนื่องจากการเปลี่ยนแปลงฝั่งเซิร์ฟเวอร์ ดังนั้นแอปของเราจึงต้องจัดการการเปลี่ยนแปลงได้

หลังจากแมปพจนานุกรมกับโครงสร้าง (ดู 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)

ระบบเรียกใช้เมธอด "populate" นี้จากเมธอด 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 เพื่อปกป้องข้อมูลของเรา

ก่อนอื่น เรามาเจาะลึกกฎความปลอดภัยที่เราเขียนไว้ตอนเริ่มต้นโค้ดแล็บกัน เปิดคอนโซล 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;
    }
  }
}

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

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

9. บทสรุป

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

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