เกี่ยวกับ Codelab นี้
1 ภาพรวม
เป้าหมาย
ในโค้ดแล็บนี้ คุณจะได้สร้างเว็บแอปแนะนำร้านอาหารที่ทำงานด้วย Cloud Firestore
สิ่งที่คุณจะได้เรียนรู้
- อ่านและเขียนข้อมูลไปยัง Cloud Firestore จากเว็บแอป
- ฟังการเปลี่ยนแปลงข้อมูล Cloud Firestore แบบเรียลไทม์
- ใช้การตรวจสอบสิทธิ์ Firebase และกฎการรักษาความปลอดภัยเพื่อรักษาความปลอดภัยของข้อมูล Cloud Firestore
- เขียนการค้นหา Cloud Firestore ที่ซับซ้อน
สิ่งที่ต้องมี
ก่อนเริ่มใช้งาน Codelab นี้ โปรดตรวจสอบว่าคุณได้ติดตั้งสิ่งต่อไปนี้แล้ว
2 สร้างและตั้งค่าโปรเจ็กต์ Firebase
สร้างโปรเจ็กต์ Firebase
- ในคอนโซล Firebase ให้คลิกเพิ่มโปรเจ็กต์ แล้วตั้งชื่อโปรเจ็กต์ Firebase เป็น FriendlyEats
จดรหัสโปรเจ็กต์ Firebase ไว้
- คลิกสร้างโปรเจ็กต์
แอปพลิเคชันที่เรากำลังจะสร้างใช้บริการ Firebase 2-3 อย่างที่มีให้บริการบนเว็บ ดังนี้
- การตรวจสอบสิทธิ์ Firebase เพื่อระบุผู้ใช้ได้อย่างง่ายดาย
- Cloud Firestore เพื่อบันทึก Structured Data ในระบบคลาวด์และรับการแจ้งเตือนทันทีเมื่อมีอัปเดตข้อมูล
- โฮสติ้งของ Firebase เพื่อโฮสต์และแสดงชิ้นงานแบบคงที่
สําหรับโค้ดแล็บนี้โดยเฉพาะ เราได้กําหนดค่าโฮสติ้งของ Firebase ไว้แล้ว แต่สำหรับ Firebase Auth และ Cloud Firestore เราจะแนะนำวิธีกำหนดค่าและเปิดใช้บริการโดยใช้คอนโซล Firebase
เปิดใช้การตรวจสอบสิทธิ์แบบไม่ระบุตัวตน
แม้ว่าการตรวจสอบสิทธิ์จะไม่ใช่ประเด็นหลักของโค้ดแล็บนี้ แต่การตรวจสอบสิทธิ์รูปแบบใดรูปแบบหนึ่งในแอปก็เป็นสิ่งที่สําคัญ เราจะใช้การเข้าสู่ระบบแบบไม่ระบุตัวตน ซึ่งหมายความว่าระบบจะลงชื่อเข้าใช้ให้ผู้ใช้โดยอัตโนมัติโดยไม่ต้องมีการแจ้ง
คุณจะต้องเปิดใช้การเข้าสู่ระบบแบบไม่ระบุชื่อ
- ในคอนโซล Firebase ให้ค้นหาส่วนบิลด์ในการนําทางด้านซ้าย
- คลิกการตรวจสอบสิทธิ์ แล้วคลิกแท็บวิธีการลงชื่อเข้าใช้ (หรือคลิกที่นี่เพื่อไปที่แท็บนั้นโดยตรง)
- เปิดใช้ผู้ให้บริการการลงชื่อเข้าใช้แบบไม่ระบุชื่อ แล้วคลิกบันทึก
ซึ่งจะช่วยให้แอปพลิเคชันลงชื่อเข้าใช้ผู้ใช้โดยอัตโนมัติเมื่อเข้าถึงเว็บแอป โปรดอ่านเอกสารประกอบการตรวจสอบสิทธิ์แบบไม่ระบุตัวตนเพื่อดูข้อมูลเพิ่มเติม
เปิดใช้ Cloud Firestore
แอปใช้ Cloud Firestore เพื่อบันทึกและรับข้อมูลและคะแนนของร้านอาหาร
คุณต้องเปิดใช้ Cloud Firestore คลิกฐานข้อมูล Firestore ในส่วนสร้างของคอนโซล Firebase คลิกสร้างฐานข้อมูลในแผง Cloud Firestore
สิทธิ์เข้าถึงข้อมูลใน Cloud Firestore ควบคุมโดยกฎความปลอดภัย เราจะพูดถึงกฎเพิ่มเติมในภายหลังในโค้ดแล็บนี้ แต่ก่อนอื่นเราต้องตั้งกฎพื้นฐานบางอย่างเกี่ยวกับข้อมูลเพื่อเริ่มต้นใช้งาน ในแท็บกฎของคอนโซล 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; } } } }
เราจะพูดถึงกฎเหล่านี้และวิธีการทํางานในภายหลังในโค้ดแล็บ
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 เป็นเวอร์ชัน 7.4.0 ขึ้นไป
- ให้สิทธิ์ Firebase CLI โดยการเรียกใช้คำสั่งต่อไปนี้
firebase login
เราได้ตั้งค่าเทมเพลตเว็บแอปเพื่อดึงการกำหนดค่าของแอปสำหรับโฮสติ้ง Firebase จากไดเรกทอรีและไฟล์ในเครื่องของแอป แต่เราต้องเชื่อมโยงแอปของคุณกับโปรเจ็กต์ Firebase ก่อน
- ตรวจสอบว่าบรรทัดคำสั่งเข้าถึงไดเรกทอรีในเครื่องของแอป
- เชื่อมโยงแอปกับโปรเจ็กต์ Firebase โดยเรียกใช้คําสั่งต่อไปนี้
firebase use --add
- เมื่อได้รับข้อความแจ้ง ให้เลือกรหัสโปรเจ็กต์ แล้วตั้งชื่อแทนให้โปรเจ็กต์ Firebase
การใช้อีเมลแทนจะมีประโยชน์หากคุณมีสภาพแวดล้อมหลายอย่าง (เวอร์ชันที่ใช้งานจริง การทดลองใช้ ฯลฯ) แต่สำหรับโค้ดแล็บนี้ เราจะใช้อีเมลแทน default
- ทําตามวิธีการที่เหลือในบรรทัดคําสั่ง
5 เรียกใช้เซิร์ฟเวอร์ภายใน
เราพร้อมที่จะเริ่มทํางานกับแอปแล้ว มาเรียกใช้แอปในเครื่องกัน
- เรียกใช้คำสั่ง Firebase CLI ต่อไปนี้
firebase emulators:start --only hosting
- บรรทัดคำสั่งควรแสดงการตอบกลับต่อไปนี้
hosting: Local server: http://localhost:5000
เราใช้โปรแกรมจำลองโฮสติ้งของ Firebase เพื่อแสดงแอปในเครื่อง ตอนนี้เว็บแอปควรพร้อมใช้งานจาก 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 ขั้นตอนคือการสร้างการค้นหาและการเพิ่มเครื่องมือรับฟังข้อมูลสแนปชอต Listener นี้จะได้รับการแจ้งเตือนเกี่ยวกับข้อมูลที่มีอยู่ทั้งหมดที่ตรงกับการค้นหาและจะได้รับการอัปเดตแบบเรียลไทม์
ก่อนอื่นมาสร้างการค้นหาที่จะแสดงรายการร้านอาหารเริ่มต้นแบบไม่กรองกัน
- กลับไปที่ไฟล์
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 ข้อมูล Get()
ที่ผ่านมาเราได้แสดงวิธีใช้ onSnapshot
เพื่อดึงข้อมูลอัปเดตแบบเรียลไทม์ แต่นั่นไม่ใช่สิ่งที่เราต้องการเสมอไป บางครั้งการดึงข้อมูลเพียงครั้งเดียวจะเหมาะสมกว่า
เราต้องการใช้เมธอดที่จะทริกเกอร์เมื่อผู้ใช้คลิกร้านอาหารที่เฉพาะเจาะจงในแอป
- กลับไปที่ไฟล์
scripts/FriendlyEats.Data.js
- ค้นหาฟังก์ชัน
FriendlyEats.prototype.getRestaurant
- แทนที่ฟังก์ชันทั้งหมดด้วยรหัสต่อไปนี้
FriendlyEats.Data.js
FriendlyEats.prototype.getRestaurant = function(id) { return firebase.firestore().collection('restaurants').doc(id).get(); };
หลังจากใช้วิธีการนี้แล้ว คุณจะดูหน้าของร้านอาหารแต่ละร้านได้ เพียงคลิกร้านอาหารในรายการ แล้วคุณจะเห็นหน้ารายละเอียดของร้านอาหาร
ขณะนี้คุณเพิ่มการให้คะแนนไม่ได้เนื่องจากเรายังต้องติดตั้งใช้งานการเพิ่มการให้คะแนนในภายหลังในโค้ดแล็บ
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 ของเบราว์เซอร์ที่มีลักษณะดังนี้
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) ให้เรียกใช้คําสั่งต่อไปนี้
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
- ในคอนโซลระบบคลาวด์ ให้ค้นหาและเลือก reCaptcha Enterprise ในส่วนความปลอดภัย
- เปิดใช้บริการตามข้อความแจ้ง แล้วคลิกสร้างคีย์
- ป้อนชื่อที่แสดงตามข้อความแจ้ง แล้วเลือกเว็บไซต์เป็นประเภทแพลตฟอร์ม
- เพิ่ม URL ที่ติดตั้งใช้งานลงในรายการโดเมน และตรวจสอบว่าไม่ได้เลือกตัวเลือก "ใช้การยืนยันผ่านช่องทําเครื่องหมาย"
- คลิกสร้างคีย์ แล้วเก็บคีย์ที่สร้างขึ้นไว้ที่ใดที่หนึ่งเพื่อความปลอดภัย เนื่องจากคุณจะต้องใช้ในขั้นตอนนี้ในภายหลัง
การเปิดใช้ App Check
- ในคอนโซล Firebase ให้ค้นหาส่วนบิลด์ในแผงด้านซ้าย
- คลิกการตรวจสอบแอป แล้วคลิกปุ่มเริ่มต้นใช้งาน (หรือเปลี่ยนเส้นทางไปยัง คอนโซลโดยตรง)
- คลิกลงทะเบียน แล้วป้อนคีย์ reCaptcha Enterprise เมื่อได้รับข้อความแจ้ง จากนั้นคลิกบันทึก
- ในมุมมอง 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 ควรทำงานในแอปของคุณแล้ว