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 ควรทำงานในแอปของคุณแล้ว