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

1. ภาพรวม

เป้าหมาย

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

  1. อ่านและเขียนข้อมูลจากแอป iOS ไปยัง Firestore
  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 ได้ เพิ่มโค้ดต่อไปนี้ไว้ด้านในสำหรับลูปในเมธอด 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 และอธิบายว่าผู้ใช้ควรเขียนฐานข้อมูลส่วนใดในฐานข้อมูล ในปัจจุบัน เราจะอนุญาตให้เฉพาะผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์เท่านั้นที่อ่านและเขียนไปยังฐานข้อมูลทั้งหมดได้ แอปเวอร์ชันที่ใช้งานจริงอาจทำได้ยากเกินไป แต่ในระหว่างขั้นตอนการสร้างแอป เราต้องการให้ผ่อนคลายมากพอเพื่อไม่ให้เกิดปัญหาการตรวจสอบสิทธิ์อยู่ตลอดเวลาขณะทำการทดลอง ในตอนท้ายของ Codelab นี้ เราจะพูดถึงวิธีปิดช่องโหว่ของกฎความปลอดภัยและจำกัดความเป็นไปได้ในการอ่านและเขียน

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

rules_version = '2';
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;
    }
  }
}

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

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

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

Screen Shot 06-07-2017 เวลา 12.45.38 PM.png

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

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

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

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

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

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

หลังจากจับคู่พจนานุกรมของเรากับ structs (ดู Restaurant.swift) แล้ว การแสดงข้อมูลเป็นเพียงการกำหนดค่าพร็อพเพอร์ตี้ข้อมูลพร็อพเพอร์ตี้ไม่กี่รายการ เพิ่มบรรทัดต่อไปนี้ไปยัง 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 /{document=**} {
      // Only authenticated users can read or write data
      allow read, write: if request.auth != null;
    }
  }
}

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

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

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

ข้อความการจับคู่แรกตรงกับคอลเล็กชันย่อยชื่อ 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 รวมถึงวิธีรักษาความปลอดภัยการเข้าถึงข้อมูลด้วยกฎความปลอดภัย คุณสามารถค้นหาโซลูชันแบบเต็มได้ที่สาขา codelab-complete

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