Cloud Firestore Web Codelab

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

1. ภาพรวม

เป้าหมาย

ใน Codelab นี้ คุณจะสร้างเว็บแอปแนะนำร้านอาหารที่ขับเคลื่อนโดย Cloud Firestore

img5.png

สิ่งที่คุณจะได้เรียนรู้

  • อ่านและเขียนข้อมูลไปยัง Cloud Firestore จากเว็บแอป
  • ฟังการเปลี่ยนแปลงในข้อมูล Cloud Firestore แบบเรียลไทม์
  • ใช้ Firebase Authentication และกฎความปลอดภัยเพื่อรักษาความปลอดภัยข้อมูล Cloud Firestore
  • เขียนคำค้นหา Cloud Firestore ที่ซับซ้อน

สิ่งที่คุณต้องการ

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

  • npm ซึ่งมักจะมาพร้อมกับ Node.js - แนะนำให้ใช้ Node v8
  • IDE/ตัวแก้ไขข้อความที่คุณเลือก เช่น WebStorm , Atom , VS Code หรือ Sublime

2. สร้างและตั้งค่าโครงการ Firebase

สร้างโครงการ Firebase

  1. ใน คอนโซล Firebase ให้คลิก เพิ่มโครงการ จากนั้นตั้งชื่อโครงการ Firebase FriendlyEats

จำรหัสโครงการสำหรับโครงการ Firebase ของคุณ

  1. คลิก สร้างโครงการ

แอปพลิเคชันที่เราจะสร้างใช้บริการ Firebase บางอย่างที่มีอยู่บนเว็บ:

  • Firebase Authentication เพื่อระบุผู้ใช้ของคุณได้อย่างง่ายดาย
  • Cloud Firestore เพื่อบันทึกข้อมูลที่มีโครงสร้างบนคลาวด์และรับการแจ้งเตือนทันทีเมื่อมีการอัปเดตข้อมูล
  • Firebase Hosting เพื่อโฮสต์และให้บริการเนื้อหาคงที่ของคุณ

สำหรับ Codelab เฉพาะนี้ เราได้กำหนดค่าโฮสติ้งของ Firebase แล้ว อย่างไรก็ตาม สำหรับ Firebase Auth และ Cloud Firestore เราจะแนะนำคุณเกี่ยวกับการกำหนดค่าและการเปิดใช้งานบริการโดยใช้คอนโซล Firebase

เปิดใช้งานการตรวจสอบสิทธิ์แบบไม่ระบุชื่อ

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

คุณจะต้องเปิดใช้งานการ เข้าสู่ระบบแบบไม่ระบุตัวตน

  1. ในคอนโซล Firebase ค้นหาส่วน Build ในการนำทางด้านซ้าย
  2. คลิกการ รับรองความถูกต้อง จากนั้นคลิกแท็บ วิธีการลงชื่อเข้าใช้ (หรือ คลิกที่นี่ เพื่อไปที่นั่นโดยตรง)
  3. เปิดใช้งานผู้ให้บริการลงชื่อเข้าใช้แบบ ไม่ระบุตัวตน จากนั้นคลิก บันทึก

img7.png

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

เปิดใช้งาน Cloud Firestore

แอปนี้ใช้ Cloud Firestore เพื่อบันทึกและรับข้อมูลร้านอาหารและการให้คะแนน

คุณจะต้องเปิดใช้งาน Cloud Firestore ในส่วน Build ของคอนโซล Firebase ให้คลิก Firestore Database คลิก สร้างฐานข้อมูล ในบานหน้าต่าง Cloud Firestore

การเข้าถึงข้อมูลใน Cloud Firestore ถูกควบคุมโดยกฎความปลอดภัย เราจะพูดถึงกฎเพิ่มเติมในภายหลังใน Codelab นี้ แต่ก่อนอื่นเราต้องตั้งกฎพื้นฐานเกี่ยวกับข้อมูลของเราเพื่อเริ่มต้น ใน แท็บกฎ ของคอนโซล 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;
    }
  }
}

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

3. รับรหัสตัวอย่าง

โคลนที่ เก็บ GitHub จากบรรทัดคำสั่ง:

git clone https://github.com/firebase/friendlyeats-web

โค้ดตัวอย่างควรได้รับการโคลนในไดเร็กทอรี 📁 friendlyeats-web จากนี้ไป อย่าลืมรันคำสั่งทั้งหมดของคุณจากไดเร็กทอรีนี้:

cd friendlyeats-web

นำเข้าแอปเริ่มต้น

ใช้ IDE ของคุณ (WebStorm, Atom, Sublime, Visual Studio Code...) เปิดหรือนำเข้าไดเร็กทอรี 📁 friendlyeats-web ไดเร็กทอรีนี้มีโค้ดเริ่มต้นสำหรับ codelab ซึ่งประกอบด้วยแอปแนะนำร้านอาหารที่ยังใช้งานไม่ได้ เราจะทำให้มันใช้งานได้ทั่วทั้ง Codelab นี้ ดังนั้นคุณจะต้องแก้ไขโค้ดในไดเร็กทอรีนั้นในไม่ช้า

4. ติดตั้งอินเทอร์เฟซบรรทัดคำสั่ง Firebase

อินเทอร์เฟซบรรทัดคำสั่งของ Firebase (CLI) ช่วยให้คุณให้บริการเว็บแอปในเครื่องและทำให้เว็บแอปใช้งานได้กับโฮสติ้งของ Firebase

  1. ติดตั้ง CLI โดยรันคำสั่ง npm ต่อไปนี้:
npm -g install firebase-tools
  1. ตรวจสอบว่ามีการติดตั้ง CLI อย่างถูกต้องโดยรันคำสั่งต่อไปนี้:
firebase --version

ตรวจสอบว่าเวอร์ชันของ Firebase CLI เป็น v7.4.0 หรือใหม่กว่า

  1. อนุญาต Firebase CLI โดยเรียกใช้คำสั่งต่อไปนี้:
firebase login

เราได้ตั้งค่าเทมเพลตเว็บแอปเพื่อดึงการกำหนดค่าแอปของคุณสำหรับ Firebase Hosting จากไดเร็กทอรีและไฟล์ในเครื่องของแอป แต่ในการทำเช่นนี้ เราจำเป็นต้องเชื่อมโยงแอปของคุณกับโปรเจ็กต์ Firebase

  1. ตรวจสอบให้แน่ใจว่าบรรทัดคำสั่งของคุณเข้าถึงไดเร็กทอรีในเครื่องของแอป
  2. เชื่อมโยงแอปของคุณกับโครงการ Firebase โดยเรียกใช้คำสั่งต่อไปนี้:
firebase use --add
  1. เมื่อได้รับแจ้ง ให้เลือก Project ID ของคุณ จากนั้นตั้งนามแฝงให้กับโปรเจ็กต์ Firebase

นามแฝงมีประโยชน์หากคุณมีหลายสภาพแวดล้อม (การผลิต การจัดเตรียม ฯลฯ) อย่างไรก็ตาม สำหรับ Codelab นี้ เราจะใช้นามแฝงของ default

  1. ทำตามคำแนะนำที่เหลือในบรรทัดคำสั่งของคุณ

5. เรียกใช้เซิร์ฟเวอร์ภายในเครื่อง

เราพร้อมที่จะเริ่มทำงานในแอปของเราแล้ว! มาเรียกใช้แอพของเราในเครื่องกันเถอะ!

  1. เรียกใช้คำสั่ง Firebase CLI ต่อไปนี้:
firebase emulators:start --only hosting
  1. บรรทัดคำสั่งของคุณควรแสดงการตอบสนองต่อไปนี้:
hosting: Local server: http://localhost:5000

เรากำลังใช้โปรแกรมจำลอง โฮสติ้งของ Firebase เพื่อให้บริการแอปของเราในเครื่อง เว็บแอปควรพร้อมใช้งานจาก http://localhost:5000

  1. เปิดแอปของคุณที่ http://localhost:5000

คุณควรเห็นสำเนา FriendlyEats ที่เชื่อมต่อกับโครงการ Firebase ของคุณ

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

img2.png

6. เขียนข้อมูลไปยัง Cloud Firestore

ในส่วนนี้ เราจะเขียนข้อมูลบางส่วนไปยัง Cloud Firestore เพื่อให้เราสามารถเติมข้อมูลใน UI ของแอปได้ ซึ่งสามารถทำได้ด้วยตนเองผ่าน คอนโซล 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 ของคุณ อย่างไรก็ตาม คุณจะยังไม่เห็นข้อมูลในเว็บแอปจริงของคุณ เนื่องจากเรายังต้องใช้การ ดึง ข้อมูล (ส่วนถัดไปของ Codelab)

หากคุณไปที่ แท็บ 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
  • เมื่อเอกสารถูก removed change.type จะเท่ากับ Remove ในกรณีนี้ เราจะเรียกฟังก์ชันที่ลบร้านอาหารออกจาก UI

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

เมื่อรายชื่อร้านอาหารของคุณเปลี่ยนแปลง ผู้ฟังนี้จะคอยอัปเดตโดยอัตโนมัติ ลองไปที่คอนโซล Firebase แล้วลบร้านอาหารหรือเปลี่ยนชื่อด้วยตัวเอง คุณจะเห็นการเปลี่ยนแปลงปรากฏบนไซต์ของคุณทันที!

img5.png

8. รับ () ข้อมูล

จนถึงตอนนี้ เราได้แสดงวิธีใช้ 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/project-id/database/firestore/indexes?create_composite=...

ข้อผิดพลาดเหล่านี้เป็นเพราะ Cloud Firestore ต้องการดัชนีสำหรับข้อความค้นหาแบบผสมส่วนใหญ่ การกำหนดดัชนีในการสืบค้นทำให้ Cloud Firestore รวดเร็วตามขนาด

การเปิดลิงก์จากข้อความแสดงข้อผิดพลาดจะเปิด UI การสร้างดัชนีโดยอัตโนมัติในคอนโซล 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. รักษาความปลอดภัยข้อมูลของคุณ

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

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

กฎเหล่านี้จำกัดการเข้าถึงเพื่อให้แน่ใจว่าไคลเอนต์ทำการเปลี่ยนแปลงอย่างปลอดภัยเท่านั้น ตัวอย่างเช่น:

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

คุณสามารถใช้ Firebase CLI เพื่อปรับใช้กฎกับโปรเจ็กต์ Firebase หรือใช้คอนโซล Firebase ไฟล์ firestore.rules ในไดเร็กทอรีการทำงานของคุณมีกฎจากด้านบนอยู่แล้ว ในการปรับใช้กฎเหล่านี้จากระบบไฟล์ในเครื่องของคุณ (แทนที่จะใช้คอนโซล Firebase) คุณต้องเรียกใช้คำสั่งต่อไปนี้:

firebase deploy --only firestore:rules

13. บทสรุป

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

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