Codelab เว็บ Cloud Firestore

1. ภาพรวม

เป้าหมาย

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

img5.png

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

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

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

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

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

2. สร้างและตั้งค่าโปรเจ็กต์ Firebase

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

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

จำรหัสโปรเจ็กต์สำหรับโปรเจ็กต์ Firebase ของคุณ

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

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

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

สำหรับ Codelab นี้ เราได้กำหนดค่า Firebase Hosting แล้ว อย่างไรก็ตาม สำหรับ 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/vanilla-js

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

การใช้ 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 เป็นเวอร์ชัน 7.4.0 หรือใหม่กว่า

  1. ให้สิทธิ์ Firebase CLI โดยการรันคำสั่งต่อไปนี้:
firebase login

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

  1. ตรวจสอบให้แน่ใจว่าบรรทัดคำสั่งของคุณเข้าถึงไดเร็กทอรีในเครื่องของแอป
  2. เชื่อมโยงแอปของคุณกับโปรเจ็กต์ Firebase โดยเรียกใช้คำสั่งต่อไปนี้:
firebase use --add
  1. เมื่อได้รับแจ้ง ให้เลือก รหัสโปรเจ็กต์ ของคุณ จากนั้นตั้งชื่อแทนโปรเจ็กต์ 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 'ing data'

มาเพิ่มร้านอาหารกันเถอะ!

  1. กลับไปที่แอป FriendlyEats ในเบราว์เซอร์แล้วรีเฟรช
  2. คลิก เพิ่มข้อมูลจำลอง

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

หากคุณไปที่ แท็บ Cloud Firestore ในคอนโซล Firebase คุณจะเห็นเอกสารใหม่ในคอลเลกชั่น restaurants !

img6.png

ยินดีด้วย คุณเพิ่งเขียนข้อมูลไปยัง Cloud Firestore จากเว็บแอป!

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

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

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

ขั้นแรก เรามาสร้างคำค้นหาที่จะแสดงรายการร้านอาหารเริ่มต้นที่ไม่มีการกรอง

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

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

เมื่อรายชื่อร้านอาหารของคุณเปลี่ยนแปลง Listener นี้จะคอยอัปเดตโดยอัตโนมัติ ลองไปที่คอนโซล 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;
      }
    }
  }
}

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

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

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

firebase deploy --only firestore:rules

13. บทสรุป

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

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

14. [ไม่บังคับ] บังคับใช้ด้วย App Check

Firebase App Check ให้การปกป้องโดยช่วยตรวจสอบและป้องกันการรับส่งข้อมูลที่ไม่พึงประสงค์ไปยังแอปของคุณ ในขั้นตอนนี้ คุณจะเข้าถึงบริการได้อย่างปลอดภัยโดยเพิ่ม App Check ด้วย reCAPTCHA Enterprise

ขั้นแรก คุณจะต้องเปิดใช้งาน App Check และ reCaptcha

เปิดใช้งาน reCaptcha Enterprise

  1. ใน Cloud Console ให้ค้นหาและเลือก reCaptcha Enterprise ภายใต้ความปลอดภัย
  2. เปิดใช้บริการตามที่ได้รับแจ้ง และคลิก สร้างคีย์
  3. ป้อนชื่อที่แสดงเมื่อได้รับแจ้ง และเลือก เว็บไซต์ เป็นประเภทแพลตฟอร์มของคุณ
  4. เพิ่ม URL ที่ใช้งานของคุณลงใน รายการโดเมน และตรวจสอบให้แน่ใจว่าไม่ได้ เลือก ตัวเลือก "ใช้ช่องทำเครื่องหมาย"
  5. คลิก สร้างคีย์ และเก็บคีย์ที่สร้างขึ้นไว้ที่ไหนสักแห่งเพื่อความปลอดภัย คุณจะต้องใช้ในภายหลังในขั้นตอนนี้

กำลังเปิดใช้งานการตรวจสอบแอป

  1. ในคอนโซล Firebase ให้ค้นหาส่วน Build ในแผงด้านซ้าย
  2. คลิก App Check จากนั้นคลิกปุ่ม เริ่มต้นใช้งาน (หรือเปลี่ยนเส้นทางไปยัง คอนโซล โดยตรง)
  3. คลิก ลงทะเบียน แล้วป้อนคีย์ reCaptcha Enterprise ของคุณเมื่อได้รับแจ้ง จากนั้นคลิก บันทึก
  4. ในมุมมอง API ให้เลือก ที่เก็บข้อมูล แล้วคลิก บังคับใช้ ทำเช่นเดียวกันกับ Cloud Firestore

ตอนนี้ควรบังคับใช้ App Check แล้ว! รีเฟรชแอปของคุณแล้วลองสร้าง/ดูร้านอาหาร คุณควรได้รับข้อความแสดงข้อผิดพลาด:

Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

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

ไปที่ไฟล์ FriendlyEats.View.js และอัปเดตฟังก์ชัน initAppCheck และเพิ่มคีย์ reCaptcha ของคุณเพื่อเริ่มต้น App Check

FriendlyEats.prototype.initAppCheck = function() {
    var appCheck = firebase.appCheck();
    appCheck.activate(
    new firebase.appCheck.ReCaptchaEnterpriseProvider(
      /* reCAPTCHA Enterprise site key */
    ),
    true // Set to true to allow auto-refresh.
  );
};

อินสแตนซ์ appCheck เริ่มต้นได้ด้วย ReCaptchaEnterpriseProvider ด้วยคีย์ของคุณ และ isTokenAutoRefreshEnabled อนุญาตให้โทเค็นรีเฟรชอัตโนมัติในแอปของคุณ

หากต้องการเปิดใช้งานการทดสอบในพื้นที่ ให้ค้นหาส่วนที่เริ่มต้นแอปในไฟล์ FriendlyEats.js และเพิ่มบรรทัดต่อไปนี้ในฟังก์ชัน FriendlyEats.prototype.initAppCheck :

if(isLocalhost) {
  self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}

สิ่งนี้จะบันทึกโทเค็นการดีบักในคอนโซลของเว็บแอปในพื้นที่ของคุณซึ่งคล้ายกับ:

App Check debug token: 8DBDF614-649D-4D22-B0A3-6D489412838B. You will need to add it to your app's App Check settings in the Firebase console for it to work.

ตอนนี้ไปที่มุม มองแอป ของ App Check ในคอนโซล Firebase

คลิกเมนูรายการเพิ่มเติม และเลือก จัดการโทเค็นการแก้ไขข้อบกพร่อง

จากนั้นคลิก เพิ่มโทเค็นการแก้ไขข้อบกพร่อง และวางโทเค็นการแก้ไขข้อบกพร่องจากคอนโซลของคุณเมื่อได้รับแจ้ง

ยินดีด้วย! App Check ควรใช้งานได้ในแอปของคุณแล้ว