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

สิ่งที่คุณจะได้เรียนรู้
- อ่านและเขียนข้อมูลไปยัง Cloud Firestore จากเว็บแอป
- ฟังการเปลี่ยนแปลงข้อมูล Cloud Firestore แบบเรียลไทม์
- ใช้การตรวจสอบสิทธิ์ของ Firebase และกฎการรักษาความปลอดภัยเพื่อรักษาความปลอดภัยของข้อมูล Cloud Firestore
- เขียนการค้นหา Cloud Firestore ที่ซับซ้อน
สิ่งที่คุณต้องมี
โปรดตรวจสอบว่าคุณได้ติดตั้งสิ่งต่อไปนี้ก่อนเริ่ม Codelab นี้
2 สร้างและตั้งค่าโปรเจ็กต์ Firebase
สร้างโปรเจ็กต์ Firebase
- ลงชื่อเข้าใช้คอนโซล Firebase โดยใช้บัญชี Google
- คลิกปุ่มเพื่อสร้างโปรเจ็กต์ใหม่ แล้วป้อนชื่อโปรเจ็กต์ (เช่น
FriendlyEats)
- คลิกต่อไป
- หากได้รับแจ้ง ให้อ่านและยอมรับข้อกำหนดของ Firebase แล้วคลิกต่อไป
- (ไม่บังคับ) เปิดใช้ความช่วยเหลือจาก AI ในคอนโซล Firebase (เรียกว่า "Gemini ใน Firebase")
- สำหรับ Codelab นี้ คุณไม่จำเป็นต้องใช้ Google Analytics ดังนั้นให้ปิดตัวเลือก Google Analytics
- คลิกสร้างโปรเจ็กต์ รอให้ระบบจัดสรรโปรเจ็กต์ แล้วคลิกดำเนินการต่อ
ตั้งค่าผลิตภัณฑ์ Firebase
แอปพลิเคชันที่เราจะสร้างใช้บริการ Firebase บางอย่างที่มีให้บริการบนเว็บ ดังนี้
- การตรวจสอบสิทธิ์ Firebase เพื่อระบุผู้ใช้ได้อย่างง่ายดาย
- Cloud Firestore เพื่อบันทึกข้อมูลที่มีโครงสร้างไว้ในระบบคลาวด์และรับการแจ้งเตือนทันทีเมื่อมีการอัปเดตข้อมูล
- Firebase Hosting เพื่อโฮสต์และแสดงเนื้อหาแบบคงที่
สำหรับโค้ดแล็บนี้ เราได้กำหนดค่าโฮสติ้งของ Firebase ไว้แล้ว อย่างไรก็ตาม สำหรับ Firebase Auth และ Cloud Firestore เราจะแนะนำการกำหนดค่าและการเปิดใช้บริการโดยใช้คอนโซล Firebase
เปิดใช้การตรวจสอบสิทธิ์แบบไม่ระบุชื่อ
แม้ว่าการตรวจสอบสิทธิ์จะไม่ใช่จุดสนใจของโค้ดแล็บนี้ แต่การมีรูปแบบการตรวจสอบสิทธิ์ในแอปก็เป็นสิ่งสำคัญ เราจะใช้การเข้าสู่ระบบแบบไม่ระบุตัวตน ซึ่งหมายความว่าระบบจะลงชื่อเข้าใช้ผู้ใช้โดยอัตโนมัติโดยไม่ต้องแจ้งให้ทราบ
คุณจะต้องเปิดใช้การเข้าสู่ระบบแบบไม่ระบุชื่อ
- ในคอนโซล Firebase ให้หาส่วนสร้างในการนำทางด้านซ้าย
- คลิก Authentication แล้วคลิกแท็บวิธีการลงชื่อเข้าใช้ (หรือคลิกที่นี่เพื่อไปที่แท็บดังกล่าวโดยตรง)
- เปิดใช้ผู้ให้บริการลงชื่อเข้าใช้แบบไม่ระบุชื่อ แล้วคลิกบันทึก

ซึ่งจะช่วยให้แอปพลิเคชันลงชื่อเข้าใช้ผู้ใช้โดยอัตโนมัติเมื่อผู้ใช้เข้าถึงเว็บแอป โปรดอ่านเอกสารประกอบการตรวจสอบสิทธิ์แบบไม่ระบุตัวตนเพื่อดูข้อมูลเพิ่มเติม
เปิดใช้ Cloud Firestore
แอปใช้ Cloud Firestore เพื่อบันทึกและรับข้อมูลและคะแนนของร้านอาหาร
คุณจะต้องเปิดใช้ Cloud Firestore ในส่วนสร้างของคอนโซล Firebase ให้คลิกฐานข้อมูล Firestore คลิกสร้างฐานข้อมูลในแผง Cloud Firestore
การเข้าถึงข้อมูลใน Cloud Firestore จะควบคุมโดยกฎความปลอดภัย เราจะพูดถึงกฎเพิ่มเติมใน Codelab นี้ในภายหลัง แต่ก่อนอื่นเราต้องตั้งกฎพื้นฐานบางอย่างในข้อมูลเพื่อเริ่มต้น ในแท็บกฎของคอนโซล Firebase ให้เพิ่มกฎต่อไปนี้ แล้วคลิกเผยแพร่
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;
}
}
}
}
เราจะพูดถึงกฎเหล่านี้และวิธีการทำงานในภายหลังใน 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 ไดเรกทอรีนี้มีโค้ดเริ่มต้นสำหรับโค้ดแล็บ ซึ่งประกอบด้วยแอปแนะนำร้านอาหารที่ยังใช้งานไม่ได้ เราจะทำให้แอปใช้งานได้ตลอดทั้งโค้ดแล็บนี้ ดังนั้นคุณจะต้องแก้ไขโค้ดในไดเรกทอรีนั้นในเร็วๆ นี้
4 ติดตั้งอินเทอร์เฟซบรรทัดคำสั่งของ Firebase
อินเทอร์เฟซบรรทัดคำสั่ง (CLI) ของ Firebase ช่วยให้คุณแสดงเว็บแอปในเครื่องและทำให้ใช้งานได้กับโฮสติ้งของ Firebase
- ติดตั้ง CLI โดยเรียกใช้คำสั่ง npm ต่อไปนี้
npm -g install firebase-tools
- ยืนยันว่าติดตั้ง CLI อย่างถูกต้องแล้วโดยเรียกใช้คำสั่งต่อไปนี้
firebase --version
ตรวจสอบว่า Firebase CLI เป็นเวอร์ชัน v7.4.0 ขึ้นไป
- ให้สิทธิ์ Firebase CLI โดยการเรียกใช้คำสั่งต่อไปนี้
firebase login
เราได้ตั้งค่าเทมเพลตเว็บแอปเพื่อดึงการกำหนดค่าของแอปสำหรับโฮสติ้งของ Firebase จากไดเรกทอรีและไฟล์ในเครื่องของแอป แต่ในการดำเนินการนี้ เราต้องเชื่อมโยงแอปกับโปรเจ็กต์ Firebase ของคุณ
- ตรวจสอบว่าบรรทัดคำสั่งเข้าถึงไดเรกทอรีภายในของแอป
- เชื่อมโยงแอปกับโปรเจ็กต์ Firebase โดยเรียกใช้คำสั่งต่อไปนี้
firebase use --add
- เมื่อได้รับข้อความแจ้ง ให้เลือกรหัสโปรเจ็กต์ แล้วตั้งนามแฝงให้โปรเจ็กต์ Firebase
นามแฝงมีประโยชน์หากคุณมีหลายสภาพแวดล้อม (การใช้งานจริง การทดลองใช้ ฯลฯ) แต่สำหรับ Codelab นี้ ให้เราใช้นามแฝงของ default
- ทำตามวิธีการที่เหลือในบรรทัดคำสั่ง
5 เรียกใช้เซิร์ฟเวอร์ภายใน
เราพร้อมที่จะเริ่มทำงานกับแอปแล้ว มาเรียกใช้แอปในเครื่องกันเลย
- เรียกใช้คำสั่ง Firebase CLI ต่อไปนี้
firebase emulators:start --only hosting
- บรรทัดคำสั่งควรแสดงการตอบกลับต่อไปนี้
hosting: Local server: http://localhost:5000
เราใช้โปรแกรมจำลอง Firebase Hosting เพื่อแสดงแอปของเราในเครื่อง ตอนนี้เว็บแอปควรพร้อมใช้งานจาก http://localhost:5000 แล้ว
- เปิดแอปที่ http://localhost:5000
คุณควรเห็นสำเนาของ FriendlyEats ที่เชื่อมต่อกับโปรเจ็กต์ Firebase
แอปได้เชื่อมต่อกับโปรเจ็กต์ Firebase โดยอัตโนมัติและลงชื่อเข้าใช้คุณเป็นผู้ใช้ที่ไม่ระบุตัวตนโดยที่คุณไม่ทราบ

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

จากนั้นเราจะจัดเก็บรีวิวแต่ละรายการในคอลเล็กชันย่อยที่ชื่อ ratings ของร้านอาหารแต่ละร้าน

เพิ่มร้านอาหารลงใน Firestore
ออบเจ็กต์โมเดลหลักในแอปของเราคือร้านอาหาร มาเขียนโค้ดเพื่อเพิ่มเอกสารร้านอาหารลงในคอลเล็กชัน restaurants กัน
- เปิด
scripts/FriendlyEats.Data.jsจากไฟล์ที่ดาวน์โหลด - ค้นหาฟังก์ชัน
FriendlyEats.prototype.addRestaurant - แทนที่ฟังก์ชันทั้งหมดด้วยโค้ดต่อไปนี้
FriendlyEats.Data.js
FriendlyEats.prototype.addRestaurant = function(data) {
var collection = firebase.firestore().collection('restaurants');
return collection.add(data);
};
โค้ดด้านบนจะเพิ่มเอกสารใหม่ลงในrestaurantsคอลเล็กชัน ข้อมูลเอกสารมาจากออบเจ็กต์ JavaScript ธรรมดา เราทำเช่นนี้โดยเริ่มจากการรับการอ้างอิงไปยังคอลเล็กชัน Cloud Firestore restaurants จากนั้นจึงaddข้อมูล
มาเพิ่มร้านอาหารกัน
- กลับไปที่แอป FriendlyEats ในเบราว์เซอร์แล้วรีเฟรช
- คลิกเพิ่มข้อมูลจำลอง
แอปจะสร้างชุดออบเจ็กต์ร้านอาหารแบบสุ่มโดยอัตโนมัติ จากนั้นจะเรียกฟังก์ชัน addRestaurant อย่างไรก็ตาม คุณจะยังไม่เห็นข้อมูลในเว็บแอปจริง เนื่องจากเรายังต้องติดตั้งใช้งานการดึงข้อมูล (ส่วนถัดไปของโค้ดแล็บ)
อย่างไรก็ตาม หากไปที่แท็บ Cloud Firestore ในคอนโซล Firebase คุณควรเห็นเอกสารใหม่ในคอลเล็กชัน restaurants แล้ว

ขอแสดงความยินดี คุณเพิ่งเขียนข้อมูลลงใน Cloud Firestore จากเว็บแอป
ในส่วนถัดไป คุณจะได้ดูวิธีดึงข้อมูลจาก Cloud Firestore และแสดงในแอป
7 แสดงข้อมูลจาก Cloud Firestore
ในส่วนนี้ คุณจะได้เรียนรู้วิธีดึงข้อมูลจาก Cloud Firestore และแสดงข้อมูลในแอป ขั้นตอนสำคัญ 2 อย่างคือการสร้างการค้นหาและการเพิ่มเครื่องมือฟังภาพรวม ผู้ฟังนี้จะได้รับการแจ้งเตือนเกี่ยวกับข้อมูลที่มีอยู่ทั้งหมดที่ตรงกับคำค้นหาและจะได้รับการอัปเดตแบบเรียลไทม์
ก่อนอื่น มาสร้างคำค้นหาที่จะแสดงรายการร้านอาหารเริ่มต้นที่ไม่มีการกรองกัน
- กลับไปที่ไฟล์
scripts/FriendlyEats.Data.js - ค้นหาฟังก์ชัน
FriendlyEats.prototype.getAllRestaurants - แทนที่ฟังก์ชันทั้งหมดด้วยโค้ดต่อไปนี้
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 ซึ่งจัดเรียงตามคะแนนเฉลี่ย (ปัจจุบันเป็น 0 ทั้งหมด) หลังจากประกาศการค้นหานี้แล้ว เราจะส่งไปยังเมธอด getDocumentsInQuery() ซึ่งมีหน้าที่โหลดและแสดงผลข้อมูล
เราจะดำเนินการนี้โดยการเพิ่มเครื่องมือฟังภาพรวม
- กลับไปที่ไฟล์
scripts/FriendlyEats.Data.js - ค้นหาฟังก์ชัน
FriendlyEats.prototype.getDocumentsInQuery - แทนที่ฟังก์ชันทั้งหมดด้วยโค้ดต่อไปนี้
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
ตอนนี้เราได้ใช้ทั้ง 2 วิธีแล้ว ให้รีเฟรชแอปและตรวจสอบว่าร้านอาหารที่เราเห็นก่อนหน้านี้ในคอนโซล Firebase ปรากฏในแอปแล้ว หากคุณทำส่วนนี้สำเร็จ แสดงว่าตอนนี้แอปของคุณอ่านและเขียนข้อมูลด้วย Cloud Firestore ได้แล้ว
เมื่อรายชื่อร้านอาหารเปลี่ยนแปลง ผู้ฟังนี้จะอัปเดตโดยอัตโนมัติ ลองไปที่คอนโซล Firebase แล้วลบร้านอาหารหรือเปลี่ยนชื่อด้วยตนเอง คุณจะเห็นการเปลี่ยนแปลงปรากฏในเว็บไซต์ทันที

8 รับ() ข้อมูล
ที่ผ่านมาเราได้แสดงวิธีใช้ onSnapshot เพื่อดึงข้อมูลอัปเดตแบบเรียลไทม์แล้ว แต่บางครั้งเราก็ไม่ได้ต้องการแบบนั้น บางครั้งการดึงข้อมูลเพียงครั้งเดียวก็สมเหตุสมผลกว่า
เราจะต้องใช้วิธีการที่ทริกเกอร์เมื่อผู้ใช้คลิกร้านอาหารที่เฉพาะเจาะจงในแอป
- กลับไปที่ไฟล์
scripts/FriendlyEats.Data.js - ค้นหาฟังก์ชัน
FriendlyEats.prototype.getRestaurant - แทนที่ฟังก์ชันทั้งหมดด้วยโค้ดต่อไปนี้
FriendlyEats.Data.js
FriendlyEats.prototype.getRestaurant = function(id) {
return firebase.firestore().collection('restaurants').doc(id).get();
};
หลังจากใช้วิธีนี้แล้ว คุณจะดูหน้าเว็บของร้านอาหารแต่ละแห่งได้ เพียงคลิกร้านอาหารในรายการ คุณก็จะเห็นหน้ารายละเอียดของร้านอาหาร

ตอนนี้คุณยังเพิ่มคะแนนไม่ได้ เนื่องจากเรายังต้องติดตั้งใช้งานการเพิ่มคะแนนในภายหลังใน Codelab
9 จัดเรียงและกรองข้อมูล
ปัจจุบันแอปของเราแสดงรายชื่อร้านอาหาร แต่ผู้ใช้ไม่สามารถกรองตามความต้องการได้ ในส่วนนี้ คุณจะได้ใช้การค้นหาขั้นสูงของ Cloud Firestore เพื่อเปิดใช้การกรอง
ต่อไปนี้คือตัวอย่างคำค้นหาอย่างง่ายเพื่อดึงข้อมูลร้านอาหาร Dim Sum ทั้งหมด
var filteredQuery = query.where('category', '==', 'Dim Sum')
ตามชื่อของฟังก์ชัน where() เมธอดจะทำให้การดาวน์โหลดคำค้นหาของเรามีเฉพาะสมาชิกของคอลเล็กชันที่มีช่องตรงตามข้อจำกัดที่เราตั้งไว้ ในกรณีนี้ ระบบจะดาวน์โหลดเฉพาะร้านอาหารที่ category เป็น Dim Sum
ในแอปของเรา ผู้ใช้สามารถเชื่อมโยงตัวกรองหลายรายการเพื่อสร้างคำค้นหาที่เฉพาะเจาะจง เช่น "พิซซ่าในซานฟรานซิสโก" หรือ "อาหารทะเลในลอสแอนเจลิสที่จัดเรียงตามความนิยม"
เราจะสร้างวิธีการที่สร้างคำค้นหาซึ่งจะกรองร้านอาหารตามเกณฑ์หลายอย่างที่ผู้ใช้เลือก
- กลับไปที่ไฟล์
scripts/FriendlyEats.Data.js - ค้นหาฟังก์ชัน
FriendlyEats.prototype.getFilteredRestaurants - แทนที่ฟังก์ชันทั้งหมดด้วยโค้ดต่อไปนี้
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 Console ของเบราว์เซอร์ที่มีลักษณะดังนี้
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
- คุณจะเห็นไฟล์
firestore.indexes.jsonในไดเรกทอรีในเครื่องที่ดาวน์โหลดของแอป
ไฟล์นี้อธิบายดัชนีทั้งหมดที่จำเป็นสำหรับชุดค่าผสมที่เป็นไปได้ทั้งหมดของตัวกรอง
firestore.indexes.json
{
"indexes": [
{
"collectionGroup": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "city", "order": "ASCENDING" },
{ "fieldPath": "avgRating", "order": "DESCENDING" }
]
},
...
]
}
- โดยคุณสามารถติดตั้งใช้งานดัชนีเหล่านี้ได้ด้วยคำสั่งต่อไปนี้
firebase deploy --only firestore:indexes
หลังจากผ่านไป 2-3 นาที ดัชนีจะใช้งานได้และข้อความแสดงข้อผิดพลาดจะหายไป
11 เขียนข้อมูลในธุรกรรม
ในส่วนนี้ เราจะเพิ่มความสามารถให้ผู้ใช้ส่งรีวิวไปยังร้านอาหารได้ ที่ผ่านมา การเขียนทั้งหมดของเราเป็นแบบอะตอมมิกและค่อนข้างเรียบง่าย หากรายการใดมีข้อผิดพลาด เราอาจแจ้งให้ผู้ใช้ลองอีกครั้ง หรือแอปของเราจะลองเขียนอีกครั้งโดยอัตโนมัติ
แอปของเราจะมีผู้ใช้จำนวนมากที่ต้องการให้คะแนนร้านอาหาร ดังนั้นเราจึงต้องประสานงานการอ่านและการเขียนหลายรายการ โดยคุณต้องส่งรีวิวก่อน จากนั้นจึงอัปเดตคะแนนของร้านอาหาร count และ average rating หากการดำเนินการอย่างใดอย่างหนึ่งล้มเหลว แต่การดำเนินการอีกอย่างหนึ่งสำเร็จ เราจะอยู่ในสถานะที่ไม่สอดคล้องกัน ซึ่งข้อมูลในส่วนหนึ่งของฐานข้อมูลไม่ตรงกับข้อมูลในอีกส่วนหนึ่ง
โชคดีที่ Cloud Firestore มีฟังก์ชันการทำธุรกรรมที่ช่วยให้เราอ่านและเขียนหลายรายการได้ในการดำเนินการแบบอะตอมเดียว ซึ่งช่วยให้มั่นใจได้ว่าข้อมูลจะยังคงสอดคล้องกัน
- กลับไปที่ไฟล์
scripts/FriendlyEats.Data.js - ค้นหาฟังก์ชัน
FriendlyEats.prototype.addRating - แทนที่ฟังก์ชันทั้งหมดด้วยโค้ดต่อไปนี้
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 รักษาข้อมูลของคุณให้ปลอดภัย
ที่จุดเริ่มต้นของโค้ดแล็บนี้ เราได้ตั้งกฎความปลอดภัยของแอปเพื่อจำกัดการเข้าถึงแอปพลิเคชัน
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 CLI เพื่อทำให้กฎใช้งานได้ในโปรเจ็กต์ Firebase แทนการใช้คอนโซล Firebase ได้ ไฟล์ firestore.rules ในไดเรกทอรีการทำงานมีกฎจากด้านบนอยู่แล้ว หากต้องการนำกฎเหล่านี้ไปใช้งานจากระบบไฟล์ในเครื่อง (แทนการใช้ Firebase Console) ให้เรียกใช้คำสั่งต่อไปนี้
firebase deploy --only firestore:rules
13 บทสรุป
ในโค้ดแล็บนี้ คุณได้เรียนรู้วิธีอ่านและเขียนข้อมูลขั้นพื้นฐานและขั้นสูงด้วย Cloud Firestore รวมถึงวิธีรักษาความปลอดภัยในการเข้าถึงข้อมูลด้วยกฎความปลอดภัย ดูโซลูชันทั้งหมดได้ในที่เก็บ quickstarts-js
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Cloud Firestore ได้ที่แหล่งข้อมูลต่อไปนี้
14 [ไม่บังคับ] บังคับใช้ด้วย App Check
App Check ของ Firebase ช่วยปกป้องโดยการช่วยตรวจสอบและป้องกันการเข้าชมที่ไม่ต้องการในแอป ในขั้นตอนนี้ คุณจะรักษาความปลอดภัยในการเข้าถึงบริการโดยเพิ่ม App Check ด้วย reCAPTCHA Enterprise
ก่อนอื่น คุณจะต้องเปิดใช้ App Check และ reCaptcha
การเปิดใช้ reCAPTCHA Enterprise
- ใน Cloud Console ให้ค้นหาและเลือก reCAPTCHA Enterprise ในส่วนความปลอดภัย
- เปิดใช้บริการตามที่ได้รับแจ้ง แล้วคลิกสร้างคีย์
- ป้อนชื่อที่แสดงตามที่ได้รับแจ้ง แล้วเลือกเว็บไซต์เป็นประเภทแพลตฟอร์ม
- เพิ่ม URL ที่ติดตั้งใช้งานลงในรายการโดเมน และตรวจสอบว่าได้ยกเลิกการเลือกตัวเลือก "ใช้การท้าทายแบบช่องทําเครื่องหมาย" แล้ว
- คลิกสร้างคีย์ แล้วจัดเก็บคีย์ที่สร้างขึ้นไว้ในที่ปลอดภัย เนื่องจากคุณจะต้องใช้ในภายหลังในขั้นตอนนี้
การเปิดใช้ App Check
- ในคอนโซล Firebase ให้หาส่วนสร้างในแผงด้านซ้าย
- คลิกการตรวจสอบแอป แล้วคลิกปุ่มเริ่มต้นใช้งาน (หรือเปลี่ยนเส้นทางไปยัง คอนโซลโดยตรง)
- คลิกลงทะเบียน แล้วป้อนคีย์ reCAPTCHA Enterprise เมื่อมีข้อความแจ้ง จากนั้นคลิกบันทึก
- ในมุมมอง API ให้เลือก Storage แล้วคลิก Enforce ทำเช่นเดียวกันกับ 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 ควรใช้งานได้ในแอปแล้ว