อ่านและเขียนข้อมูลบนเว็บ

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

(ไม่บังคับ) ต้นแบบและทดสอบด้วย Firebase Local Emulator Suite

ก่อนที่จะพูดถึงวิธีที่แอปของคุณอ่านและเขียนไปยัง Realtime Database เรามาแนะนำชุดเครื่องมือที่คุณสามารถใช้เพื่อสร้างต้นแบบและทดสอบการทำงานของ Realtime Database: Firebase Local Emulator Suite หากคุณกำลังทดลองใช้โมเดลข้อมูลต่างๆ เพิ่มประสิทธิภาพกฎความปลอดภัย หรือพยายามหาวิธีที่คุ้มค่าที่สุดในการโต้ตอบกับแบ็กเอนด์ ความสามารถในการทำงานในพื้นที่โดยไม่ต้องปรับใช้บริการสดอาจเป็นแนวคิดที่ดี

โปรแกรมจำลองฐานข้อมูลเรียลไทม์เป็นส่วนหนึ่งของ Local Emulator Suite ซึ่งช่วยให้แอปของคุณสามารถโต้ตอบกับเนื้อหาและการกำหนดค่าฐานข้อมูลที่จำลองได้ รวมถึงทรัพยากรโครงการจำลอง (ฟังก์ชัน ฐานข้อมูลอื่นๆ และกฎความปลอดภัย)

การใช้โปรแกรมจำลองฐานข้อมูลเรียลไทม์เกี่ยวข้องกับขั้นตอนเพียงไม่กี่ขั้นตอน:

  1. การเพิ่มบรรทัดโค้ดในการกำหนดค่าการทดสอบของแอปเพื่อเชื่อมต่อกับโปรแกรมจำลอง
  2. จากรูทของไดเร็กทอรีโปรเจ็กต์ในเครื่องของคุณ ให้รัน firebase emulators:start
  3. การโทรจากโค้ดต้นแบบของแอปโดยใช้ SDK แพลตฟอร์ม Realtime Database ตามปกติ หรือใช้ Realtime Database REST API

มี คำแนะนำโดยละเอียดเกี่ยวกับ Realtime Database และ Cloud Functions คุณควรดูการ แนะนำ Local Emulator Suite ด้วย

รับการอ้างอิงฐานข้อมูล

หากต้องการอ่านหรือเขียนข้อมูลจากฐานข้อมูล คุณต้องมีอินสแตนซ์ของ firebase.database.Reference :

Web version 9

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web version 8

var database = firebase.database();

เขียนข้อมูล

เอกสารนี้ครอบคลุมถึงพื้นฐานของการดึงข้อมูลและวิธีสั่งซื้อและกรองข้อมูล Firebase

ข้อมูล Firebase ถูกดึงมาโดยแนบ Listener แบบอะซิงโครนัสกับ firebase.database.Reference Listener จะถูกทริกเกอร์หนึ่งครั้งสำหรับสถานะเริ่มต้นของข้อมูล และอีกครั้งทุกครั้งที่ข้อมูลเปลี่ยนแปลง

การดำเนินการเขียนขั้นพื้นฐาน

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

Web version 9

import { getDatabase, ref, set } from "firebase/database";

function writeUserData(userId, name, email, imageUrl) {
  const db = getDatabase();
  set(ref(db, 'users/' + userId), {
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

Web version 8

function writeUserData(userId, name, email, imageUrl) {
  firebase.database().ref('users/' + userId).set({
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

การใช้ set() จะเขียนทับข้อมูลในตำแหน่งที่ระบุ รวมถึงโหนดย่อย

อ่านข้อมูล

ฟังเหตุการณ์อันทรงคุณค่า

หากต้องการอ่านข้อมูลที่เส้นทางและรับฟังการเปลี่ยนแปลง ให้ใช้ onValue() เพื่อสังเกตเหตุการณ์ คุณสามารถใช้เหตุการณ์นี้เพื่ออ่านสแนปชอตแบบคงที่ของเนื้อหาในเส้นทางที่กำหนด เนื่องจากมีอยู่ในเวลาที่เกิดเหตุการณ์ วิธีการนี้จะทริกเกอร์หนึ่งครั้งเมื่อมีการแนบผู้ฟัง และอีกครั้งทุกครั้งที่ข้อมูล รวมถึงลูกๆ มีการเปลี่ยนแปลง การเรียกกลับของเหตุการณ์จะถูกส่งผ่านสแน็ปช็อตที่มีข้อมูลทั้งหมด ณ ตำแหน่งนั้น รวมถึงข้อมูลย่อย หากไม่มีข้อมูล สแนปชอตจะคืนค่า false เมื่อคุณเรียกใช้ exists() และ null เมื่อคุณเรียกใช้ val()

ตัวอย่างต่อไปนี้สาธิตแอปพลิเคชันบล็อกโซเชียลที่ดึงจำนวนดาวของโพสต์จากฐานข้อมูล:

Web version 9

import { getDatabase, ref, onValue} from "firebase/database";

const db = getDatabase();
const starCountRef = ref(db, 'posts/' + postId + '/starCount');
onValue(starCountRef, (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

Web version 8

var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

ผู้ฟังจะได้รับส snapshot อตที่มีข้อมูลที่ตำแหน่งที่ระบุในฐานข้อมูล ณ เวลาที่เกิดเหตุการณ์ คุณสามารถดึงข้อมูลในส snapshot อตด้วยเมธอด val()

อ่านข้อมูลครั้งเดียว

อ่านข้อมูลหนึ่งครั้งด้วย get()

SDK ได้รับการออกแบบมาเพื่อจัดการการโต้ตอบกับเซิร์ฟเวอร์ฐานข้อมูล ไม่ว่าแอปของคุณจะออนไลน์หรือออฟไลน์

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

หากคุณต้องการข้อมูลเพียงครั้งเดียว คุณสามารถใช้ get() เพื่อรับสแน็ปช็อตของข้อมูลจากฐานข้อมูล ถ้าด้วยเหตุผลใดก็ตาม get() ไม่สามารถคืนค่าเซิร์ฟเวอร์ได้ ไคลเอ็นต์จะตรวจสอบแคชที่จัดเก็บในเครื่องและส่งคืนข้อผิดพลาดหากไม่พบค่าดังกล่าว

การใช้ get() โดยไม่จำเป็นอาจเพิ่มการใช้แบนด์วิดท์และทำให้ประสิทธิภาพลดลง ซึ่งสามารถป้องกันได้โดยใช้ตัวฟังแบบเรียลไทม์ดังที่แสดงด้านบน

Web version 9

import { getDatabase, ref, child, get } from "firebase/database";

const dbRef = ref(getDatabase());
get(child(dbRef, `users/${userId}`)).then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

Web version 8

const dbRef = firebase.database().ref();
dbRef.child("users").child(userId).get().then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

อ่านข้อมูลครั้งเดียวกับผู้สังเกตการณ์

ในบางกรณี คุณอาจต้องการให้คืนค่าจากแคชในเครื่องทันที แทนที่จะตรวจสอบค่าที่อัปเดตบนเซิร์ฟเวอร์ ในกรณีเหล่านี้ คุณสามารถใช้ once() เพื่อรับข้อมูลจากแคชดิสก์ในเครื่องทันที

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

Web version 9

import { getDatabase, ref, onValue } from "firebase/database";
import { getAuth } from "firebase/auth";

const db = getDatabase();
const auth = getAuth();

const userId = auth.currentUser.uid;
return onValue(ref(db, '/users/' + userId), (snapshot) => {
  const username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
}, {
  onlyOnce: true
});

Web version 8

var userId = firebase.auth().currentUser.uid;
return firebase.database().ref('/users/' + userId).once('value').then((snapshot) => {
  var username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
});

กำลังอัปเดตหรือลบข้อมูล

อัพเดทเฉพาะช่อง

หากต้องการเขียนไปยังโหนดย่อยที่ระบุพร้อมกันโดยไม่เขียนทับโหนดย่อยอื่น ให้ใช้เมธอด update()

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

ตัวอย่างเช่น แอปบล็อกโซเชียลอาจสร้างโพสต์และอัปเดตเป็นฟีดกิจกรรมล่าสุดและฟีดกิจกรรมของผู้ใช้ที่โพสต์พร้อมกันโดยใช้โค้ดดังนี้

Web version 9

import { getDatabase, ref, child, push, update } from "firebase/database";

function writeNewPost(uid, username, picture, title, body) {
  const db = getDatabase();

  // A post entry.
  const postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  const newPostKey = push(child(ref(db), 'posts')).key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  const updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return update(ref(db), updates);
}

Web version 8

function writeNewPost(uid, username, picture, title, body) {
  // A post entry.
  var postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  var newPostKey = firebase.database().ref().child('posts').push().key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  var updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return firebase.database().ref().update(updates);
}

ตัวอย่างนี้ใช้ push() เพื่อสร้างโพสต์ในโหนดที่มีโพสต์สำหรับผู้ใช้ทั้งหมดที่ /posts/$postid และดึงคีย์พร้อมกัน คีย์สามารถใช้เพื่อสร้างรายการที่สองในโพสต์ของผู้ใช้ที่ /user-posts/$userid/$postid

เมื่อใช้พาธเหล่านี้ คุณสามารถดำเนินการอัปเดตหลายตำแหน่งพร้อมกันในแผนผัง JSON ได้ด้วยการเรียก update() เพียงครั้งเดียว เช่น วิธีที่ตัวอย่างนี้สร้างโพสต์ใหม่ในทั้งสองตำแหน่ง การอัปเดตพร้อมกันในลักษณะนี้เป็นแบบอะตอมมิก: การอัปเดตทั้งหมดสำเร็จหรือการอัปเดตทั้งหมดล้มเหลว

เพิ่มการโทรกลับให้เสร็จสิ้น

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

Web version 9

import { getDatabase, ref, set } from "firebase/database";

const db = getDatabase();
set(ref(db, 'users/' + userId), {
  username: name,
  email: email,
  profile_picture : imageUrl
})
.then(() => {
  // Data saved successfully!
})
.catch((error) => {
  // The write failed...
});

Web version 8

firebase.database().ref('users/' + userId).set({
  username: name,
  email: email,
  profile_picture : imageUrl
}, (error) => {
  if (error) {
    // The write failed...
  } else {
    // Data saved successfully!
  }
});

ลบข้อมูล

วิธีที่ง่ายที่สุดในการลบข้อมูลคือการเรียก remove() โดยอ้างอิงถึงตำแหน่งของข้อมูลนั้น

คุณยังสามารถลบโดยระบุ null เป็นค่าสำหรับการดำเนินการเขียนอื่น เช่น set() หรือ update() คุณสามารถใช้เทคนิคนี้กับ update() เพื่อลบลูกหลายคนในการเรียก API ครั้งเดียว

รับ Promise

หากต้องการทราบว่าข้อมูลของคุณถูกส่งไปยังเซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase เมื่อใด คุณสามารถใช้ Promise ทั้ง set() และ update() สามารถส่งคืน Promise ที่คุณสามารถใช้เพื่อให้ทราบเมื่อเขียนไปยังฐานข้อมูล

แยกผู้ฟัง

การเรียกกลับจะถูกลบออกโดยการเรียกเมธอด off() ในการอ้างอิงฐานข้อมูล Firebase ของคุณ

คุณสามารถลบ Listener ตัวเดียวได้โดยส่งผ่านเป็นพารามิเตอร์ off() การโทร off() ในตำแหน่งที่ไม่มีอาร์กิวเมนต์จะลบผู้ฟังทั้งหมดออกจากตำแหน่งนั้น

การโทร off() บนตัวฟังหลักจะไม่ลบผู้ฟังที่ลงทะเบียนในโหนดย่อยโดยอัตโนมัติ off() จะต้องถูกเรียกไปยังผู้ฟังที่เป็นเด็กเพื่อลบการโทรกลับ

บันทึกข้อมูลเป็นธุรกรรม

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

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

Web version 9

import { getDatabase, ref, runTransaction } from "firebase/database";

function toggleStar(uid) {
  const db = getDatabase();
  const postRef = ref(db, '/posts/foo-bar-123');

  runTransaction(postRef, (post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

Web version 8

function toggleStar(postRef, uid) {
  postRef.transaction((post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

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

Atomic Server-side เพิ่มขึ้น

ในกรณีการใช้งานข้างต้น เรากำลังเขียนค่าสองค่าลงในฐานข้อมูล: ID ของผู้ใช้ที่ติดดาว/เลิกติดดาวโพสต์ และจำนวนดาวที่เพิ่มขึ้น หากเราทราบแล้วว่าผู้ใช้กำลังติดดาวโพสต์ เราสามารถใช้การดำเนินการ atomic increment แทนการทำธุรกรรม

function addStar(uid, key) {
  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  firebase.database().ref().update(updates);
}

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

หากคุณต้องการตรวจจับและปฏิเสธข้อขัดแย้งเฉพาะแอปพลิเคชัน เช่น ผู้ใช้ที่ติดดาวโพสต์ที่พวกเขาติดดาวไว้แล้วก่อนหน้านี้ คุณควรเขียนกฎความปลอดภัยที่กำหนดเองสำหรับกรณีการใช้งานนั้น

ทำงานกับข้อมูลออฟไลน์

หากไคลเอ็นต์ขาดการเชื่อมต่อเครือข่าย แอปของคุณจะทำงานต่อไปอย่างถูกต้อง

ลูกค้าทุกรายที่เชื่อมต่อกับฐานข้อมูล Firebase จะรักษาเวอร์ชันภายในของข้อมูลที่ใช้งานอยู่ เมื่อมีการเขียนข้อมูล ข้อมูลนั้นจะถูกเขียนลงในเวอร์ชันภายในเครื่องก่อน จากนั้นไคลเอนต์ Firebase จะซิงโครไนซ์ข้อมูลนั้นกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกลและกับไคลเอนต์อื่น ๆ ด้วย "ความพยายามอย่างดีที่สุด"

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

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

เราจะพูดถึงพฤติกรรมออฟไลน์เพิ่มเติมใน เรียนรู้เพิ่มเติมเกี่ยวกับความสามารถออนไลน์และออฟไลน์ ..

ขั้นตอนถัดไป

,

(ไม่บังคับ) ต้นแบบและทดสอบด้วย Firebase Local Emulator Suite

ก่อนที่จะพูดถึงวิธีที่แอปของคุณอ่านและเขียนไปยัง Realtime Database เรามาแนะนำชุดเครื่องมือที่คุณสามารถใช้เพื่อสร้างต้นแบบและทดสอบการทำงานของ Realtime Database: Firebase Local Emulator Suite หากคุณกำลังทดลองใช้โมเดลข้อมูลต่างๆ เพิ่มประสิทธิภาพกฎความปลอดภัย หรือพยายามหาวิธีที่คุ้มค่าที่สุดในการโต้ตอบกับแบ็กเอนด์ ความสามารถในการทำงานในพื้นที่โดยไม่ต้องปรับใช้บริการสดอาจเป็นแนวคิดที่ดี

โปรแกรมจำลองฐานข้อมูลเรียลไทม์เป็นส่วนหนึ่งของ Local Emulator Suite ซึ่งช่วยให้แอปของคุณสามารถโต้ตอบกับเนื้อหาและการกำหนดค่าฐานข้อมูลที่จำลองได้ รวมถึงทรัพยากรโครงการจำลอง (ฟังก์ชัน ฐานข้อมูลอื่นๆ และกฎความปลอดภัย)

การใช้โปรแกรมจำลองฐานข้อมูลเรียลไทม์เกี่ยวข้องกับขั้นตอนเพียงไม่กี่ขั้นตอน:

  1. การเพิ่มบรรทัดโค้ดในการกำหนดค่าการทดสอบของแอปเพื่อเชื่อมต่อกับโปรแกรมจำลอง
  2. จากรูทของไดเร็กทอรีโปรเจ็กต์ในเครื่องของคุณ ให้รัน firebase emulators:start
  3. การโทรจากโค้ดต้นแบบของแอปโดยใช้ SDK แพลตฟอร์ม Realtime Database ตามปกติ หรือใช้ Realtime Database REST API

มี คำแนะนำโดยละเอียดเกี่ยวกับ Realtime Database และ Cloud Functions คุณควรดูการ แนะนำ Local Emulator Suite ด้วย

รับการอ้างอิงฐานข้อมูล

หากต้องการอ่านหรือเขียนข้อมูลจากฐานข้อมูล คุณต้องมีอินสแตนซ์ของ firebase.database.Reference :

Web version 9

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web version 8

var database = firebase.database();

เขียนข้อมูล

เอกสารนี้ครอบคลุมถึงพื้นฐานของการดึงข้อมูลและวิธีสั่งซื้อและกรองข้อมูล Firebase

ข้อมูล Firebase ถูกดึงมาโดยแนบ Listener แบบอะซิงโครนัสกับ firebase.database.Reference Listener จะถูกทริกเกอร์หนึ่งครั้งสำหรับสถานะเริ่มต้นของข้อมูล และอีกครั้งทุกครั้งที่ข้อมูลเปลี่ยนแปลง

การดำเนินการเขียนขั้นพื้นฐาน

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

Web version 9

import { getDatabase, ref, set } from "firebase/database";

function writeUserData(userId, name, email, imageUrl) {
  const db = getDatabase();
  set(ref(db, 'users/' + userId), {
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

Web version 8

function writeUserData(userId, name, email, imageUrl) {
  firebase.database().ref('users/' + userId).set({
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

การใช้ set() จะเขียนทับข้อมูลในตำแหน่งที่ระบุ รวมถึงโหนดย่อย

อ่านข้อมูล

ฟังเหตุการณ์อันทรงคุณค่า

หากต้องการอ่านข้อมูลที่เส้นทางและรับฟังการเปลี่ยนแปลง ให้ใช้ onValue() เพื่อสังเกตเหตุการณ์ คุณสามารถใช้เหตุการณ์นี้เพื่ออ่านสแนปชอตแบบคงที่ของเนื้อหาในเส้นทางที่กำหนด เนื่องจากมีอยู่ในเวลาที่เกิดเหตุการณ์ วิธีการนี้จะทริกเกอร์หนึ่งครั้งเมื่อมีการแนบผู้ฟัง และอีกครั้งทุกครั้งที่ข้อมูล รวมถึงลูกๆ มีการเปลี่ยนแปลง การเรียกกลับของเหตุการณ์จะถูกส่งผ่านสแน็ปช็อตที่มีข้อมูลทั้งหมด ณ ตำแหน่งนั้น รวมถึงข้อมูลย่อย หากไม่มีข้อมูล สแนปชอตจะคืนค่า false เมื่อคุณเรียกใช้ exists() และ null เมื่อคุณเรียกใช้ val()

ตัวอย่างต่อไปนี้สาธิตแอปพลิเคชันบล็อกโซเชียลที่ดึงจำนวนดาวของโพสต์จากฐานข้อมูล:

Web version 9

import { getDatabase, ref, onValue} from "firebase/database";

const db = getDatabase();
const starCountRef = ref(db, 'posts/' + postId + '/starCount');
onValue(starCountRef, (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

Web version 8

var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

ผู้ฟังจะได้รับส snapshot อตที่มีข้อมูลที่ตำแหน่งที่ระบุในฐานข้อมูล ณ เวลาที่เกิดเหตุการณ์ คุณสามารถดึงข้อมูลในส snapshot อตด้วยเมธอด val()

อ่านข้อมูลครั้งเดียว

อ่านข้อมูลหนึ่งครั้งด้วย get()

SDK ได้รับการออกแบบมาเพื่อจัดการการโต้ตอบกับเซิร์ฟเวอร์ฐานข้อมูล ไม่ว่าแอปของคุณจะออนไลน์หรือออฟไลน์

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

หากคุณต้องการข้อมูลเพียงครั้งเดียว คุณสามารถใช้ get() เพื่อรับสแน็ปช็อตของข้อมูลจากฐานข้อมูล ถ้าด้วยเหตุผลใดก็ตาม get() ไม่สามารถคืนค่าเซิร์ฟเวอร์ได้ ไคลเอ็นต์จะตรวจสอบแคชที่จัดเก็บในเครื่องและส่งคืนข้อผิดพลาดหากไม่พบค่าดังกล่าว

การใช้ get() โดยไม่จำเป็นอาจเพิ่มการใช้แบนด์วิดท์และทำให้ประสิทธิภาพลดลง ซึ่งสามารถป้องกันได้โดยใช้ตัวฟังแบบเรียลไทม์ดังที่แสดงด้านบน

Web version 9

import { getDatabase, ref, child, get } from "firebase/database";

const dbRef = ref(getDatabase());
get(child(dbRef, `users/${userId}`)).then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

Web version 8

const dbRef = firebase.database().ref();
dbRef.child("users").child(userId).get().then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

อ่านข้อมูลครั้งเดียวกับผู้สังเกตการณ์

ในบางกรณี คุณอาจต้องการให้คืนค่าจากแคชในเครื่องทันที แทนที่จะตรวจสอบค่าที่อัปเดตบนเซิร์ฟเวอร์ ในกรณีเหล่านี้ คุณสามารถใช้ once() เพื่อรับข้อมูลจากแคชดิสก์ในเครื่องทันที

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

Web version 9

import { getDatabase, ref, onValue } from "firebase/database";
import { getAuth } from "firebase/auth";

const db = getDatabase();
const auth = getAuth();

const userId = auth.currentUser.uid;
return onValue(ref(db, '/users/' + userId), (snapshot) => {
  const username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
}, {
  onlyOnce: true
});

Web version 8

var userId = firebase.auth().currentUser.uid;
return firebase.database().ref('/users/' + userId).once('value').then((snapshot) => {
  var username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
});

กำลังอัปเดตหรือลบข้อมูล

อัพเดทเฉพาะช่อง

หากต้องการเขียนไปยังโหนดย่อยที่ระบุพร้อมกันโดยไม่เขียนทับโหนดย่อยอื่น ให้ใช้เมธอด update()

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

ตัวอย่างเช่น แอปบล็อกโซเชียลอาจสร้างโพสต์และอัปเดตเป็นฟีดกิจกรรมล่าสุดและฟีดกิจกรรมของผู้ใช้ที่โพสต์พร้อมกันโดยใช้โค้ดดังนี้

Web version 9

import { getDatabase, ref, child, push, update } from "firebase/database";

function writeNewPost(uid, username, picture, title, body) {
  const db = getDatabase();

  // A post entry.
  const postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  const newPostKey = push(child(ref(db), 'posts')).key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  const updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return update(ref(db), updates);
}

Web version 8

function writeNewPost(uid, username, picture, title, body) {
  // A post entry.
  var postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  var newPostKey = firebase.database().ref().child('posts').push().key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  var updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return firebase.database().ref().update(updates);
}

ตัวอย่างนี้ใช้ push() เพื่อสร้างโพสต์ในโหนดที่มีโพสต์สำหรับผู้ใช้ทั้งหมดที่ /posts/$postid และดึงคีย์พร้อมกัน คีย์สามารถใช้เพื่อสร้างรายการที่สองในโพสต์ของผู้ใช้ที่ /user-posts/$userid/$postid

เมื่อใช้พาธเหล่านี้ คุณสามารถดำเนินการอัปเดตหลายตำแหน่งพร้อมกันในแผนผัง JSON ได้ด้วยการเรียก update() เพียงครั้งเดียว เช่น วิธีที่ตัวอย่างนี้สร้างโพสต์ใหม่ในทั้งสองตำแหน่ง การอัปเดตพร้อมกันในลักษณะนี้เป็นแบบอะตอมมิก: การอัปเดตทั้งหมดสำเร็จหรือการอัปเดตทั้งหมดล้มเหลว

เพิ่มการโทรกลับให้เสร็จสิ้น

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

Web version 9

import { getDatabase, ref, set } from "firebase/database";

const db = getDatabase();
set(ref(db, 'users/' + userId), {
  username: name,
  email: email,
  profile_picture : imageUrl
})
.then(() => {
  // Data saved successfully!
})
.catch((error) => {
  // The write failed...
});

Web version 8

firebase.database().ref('users/' + userId).set({
  username: name,
  email: email,
  profile_picture : imageUrl
}, (error) => {
  if (error) {
    // The write failed...
  } else {
    // Data saved successfully!
  }
});

ลบข้อมูล

วิธีที่ง่ายที่สุดในการลบข้อมูลคือการเรียก remove() โดยอ้างอิงถึงตำแหน่งของข้อมูลนั้น

คุณยังสามารถลบโดยระบุ null เป็นค่าสำหรับการดำเนินการเขียนอื่น เช่น set() หรือ update() คุณสามารถใช้เทคนิคนี้กับ update() เพื่อลบลูกหลายคนในการเรียก API ครั้งเดียว

รับ Promise

หากต้องการทราบว่าข้อมูลของคุณถูกส่งไปยังเซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase เมื่อใด คุณสามารถใช้ Promise ทั้ง set() และ update() สามารถส่งคืน Promise ที่คุณสามารถใช้เพื่อให้ทราบเมื่อเขียนไปยังฐานข้อมูล

แยกผู้ฟัง

การเรียกกลับจะถูกลบออกโดยการเรียกเมธอด off() ในการอ้างอิงฐานข้อมูล Firebase ของคุณ

คุณสามารถลบ Listener ตัวเดียวได้โดยส่งผ่านเป็นพารามิเตอร์ off() การโทร off() ในตำแหน่งที่ไม่มีอาร์กิวเมนต์จะลบผู้ฟังทั้งหมดออกจากตำแหน่งนั้น

การโทร off() บนตัวฟังหลักจะไม่ลบผู้ฟังที่ลงทะเบียนในโหนดย่อยโดยอัตโนมัติ off() จะต้องถูกเรียกไปยังผู้ฟังที่เป็นเด็กเพื่อลบการโทรกลับ

บันทึกข้อมูลเป็นธุรกรรม

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

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

Web version 9

import { getDatabase, ref, runTransaction } from "firebase/database";

function toggleStar(uid) {
  const db = getDatabase();
  const postRef = ref(db, '/posts/foo-bar-123');

  runTransaction(postRef, (post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

Web version 8

function toggleStar(postRef, uid) {
  postRef.transaction((post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

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

Atomic Server-side เพิ่มขึ้น

ในกรณีการใช้งานข้างต้น เรากำลังเขียนค่าสองค่าลงในฐานข้อมูล: ID ของผู้ใช้ที่ติดดาว/เลิกติดดาวโพสต์ และจำนวนดาวที่เพิ่มขึ้น หากเราทราบแล้วว่าผู้ใช้กำลังติดดาวโพสต์ เราสามารถใช้การดำเนินการ atomic increment แทนการทำธุรกรรม

function addStar(uid, key) {
  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  firebase.database().ref().update(updates);
}

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

หากคุณต้องการตรวจจับและปฏิเสธข้อขัดแย้งเฉพาะแอปพลิเคชัน เช่น ผู้ใช้ที่ติดดาวโพสต์ที่พวกเขาติดดาวไว้แล้วก่อนหน้านี้ คุณควรเขียนกฎความปลอดภัยที่กำหนดเองสำหรับกรณีการใช้งานนั้น

ทำงานกับข้อมูลออฟไลน์

หากไคลเอ็นต์ขาดการเชื่อมต่อเครือข่าย แอปของคุณจะทำงานต่อไปอย่างถูกต้อง

ลูกค้าทุกรายที่เชื่อมต่อกับฐานข้อมูล Firebase จะรักษาเวอร์ชันภายในของข้อมูลที่ใช้งานอยู่ เมื่อมีการเขียนข้อมูล ข้อมูลนั้นจะถูกเขียนลงในเวอร์ชันภายในเครื่องก่อน จากนั้นไคลเอนต์ Firebase จะซิงโครไนซ์ข้อมูลนั้นกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกลและกับไคลเอนต์อื่น ๆ ด้วย "ความพยายามอย่างดีที่สุด"

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

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

เราจะพูดถึงพฤติกรรมออฟไลน์เพิ่มเติมใน เรียนรู้เพิ่มเติมเกี่ยวกับความสามารถออนไลน์และออฟไลน์ ..

ขั้นตอนถัดไป