قراءة وكتابة البيانات على الويب

(اختياري) النموذج الأولي والاختبار باستخدام Firebase Local Emulator Suite

قبل التحدث عن كيفية قراءة تطبيقك من قاعدة بيانات Realtime والكتابة إليها ، دعنا نقدم مجموعة من الأدوات التي يمكنك استخدامها لتصميم واختبار وظيفة Realtime Database: Firebase Local Emulator Suite. إذا كنت تجرب نماذج بيانات مختلفة ، أو تقوم بتحسين قواعد الأمان الخاصة بك ، أو تعمل على إيجاد الطريقة الأكثر فعالية من حيث التكلفة للتفاعل مع النهاية الخلفية ، فإن القدرة على العمل محليًا دون نشر الخدمات المباشرة يمكن أن تكون فكرة رائعة.

يعد محاكي Realtime Database جزءًا من Local Emulator Suite ، والذي يمكّن تطبيقك من التفاعل مع محتوى قاعدة البيانات التي تمت محاكاتها وتكوينها ، بالإضافة إلى موارد المشروع التي تمت محاكاتها اختياريًا (الوظائف وقواعد البيانات الأخرى وقواعد الأمان).

يتضمن استخدام محاكي Realtime Database بضع خطوات فقط:

  1. إضافة سطر من التعليمات البرمجية إلى التكوين التجريبي لتطبيقك للاتصال بالمحاكي.
  2. من جذر دليل المشروع المحلي ، قم بتشغيل firebase emulators:start .
  3. إجراء مكالمات من رمز النموذج الأولي لتطبيقك باستخدام SDK للنظام الأساسي لقاعدة البيانات Realtime كالمعتاد ، أو باستخدام Realtime Database REST API.

تتوفر إرشادات تفصيلية تتضمن Realtime Database و Cloud Functions . يجب عليك أيضًا إلقاء نظرة على مقدمة Local Emulator Suite .

احصل على مرجع قاعدة البيانات

لقراءة البيانات أو كتابتها من قاعدة البيانات ، تحتاج إلى مثيل لـ firebase.database.Reference :

Web modular API

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web namespaced API

var database = firebase.database();

اكتب البيانات

يغطي هذا المستند أساسيات استرداد البيانات وكيفية طلب بيانات Firebase وتصفيتها.

يتم استرداد بيانات Firebase عن طريق إرفاق مستمع غير متزامن بـ firebase.database.Reference . يتم تشغيل المستمع مرة واحدة للحالة الأولية للبيانات ومرة ​​أخرى في أي وقت تتغير فيه البيانات.

عمليات الكتابة الأساسية

لعمليات الكتابة الأساسية ، يمكنك استخدام set() لحفظ البيانات في مرجع محدد ، واستبدال أي بيانات موجودة في ذلك المسار. على سبيل المثال ، قد يضيف تطبيق التدوين الاجتماعي مستخدمًا بـ set() على النحو التالي:

Web modular API

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 namespaced API

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 modular API

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 namespaced API

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 modular API

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 namespaced API

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 modular API

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 namespaced API

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() ، يمكنك تحديث القيم الفرعية ذات المستوى الأدنى من خلال تحديد مسار للمفتاح. إذا تم تخزين البيانات في مواقع متعددة لتوسيع نطاقها بشكل أفضل ، يمكنك تحديث جميع مثيلات تلك البيانات باستخدام توزيع البيانات .

على سبيل المثال ، قد يقوم تطبيق التدوين الاجتماعي بإنشاء منشور وتحديثه في نفس الوقت إلى موجز النشاط الأخير وخلاصة نشاط المستخدم المنشور باستخدام رمز مثل هذا:

Web modular API

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 namespaced API

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 modular API

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 namespaced API

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() لحذف عدة توابع في استدعاء واحد لواجهة برمجة التطبيقات.

احصل على Promise

لمعرفة وقت التزام بياناتك بخادم قاعدة بيانات Firebase Realtime ، يمكنك استخدام Promise . يمكن لكل من set() و update() إرجاع Promise يمكنك استخدامه لمعرفة متى تلتزم الكتابة بقاعدة البيانات.

افصل المستمعين

تتم إزالة عمليات الاسترجاعات عن طريق استدعاء طريقة off() في مرجع قاعدة بيانات Firebase.

يمكنك إزالة مستمع واحد عن طريق تمريره كمعامل إلى off() . يؤدي استدعاء off() على الموقع بدون وسيطات إلى إزالة جميع المستمعين في ذلك الموقع.

لا يؤدي استدعاء off() على المستمع الأصل إلى إزالة المستمعين المسجلين في العقد الفرعية الخاصة به تلقائيًا ؛ يجب أيضًا استدعاء off() على أي مستمعين أطفال لإزالة رد النداء.

حفظ البيانات كعمليات

عند العمل مع البيانات التي يمكن أن تتلف بسبب التعديلات المتزامنة ، مثل العدادات المتزايدة ، يمكنك استخدام عملية معاملة . يمكنك إعطاء هذه العملية وظيفة تحديث واستدعاء إكمال اختياري. تأخذ وظيفة التحديث الحالة الحالية للبيانات كوسيطة وتعيد الحالة الجديدة المرغوبة التي ترغب في كتابتها. إذا كتب عميل آخر إلى الموقع قبل كتابة القيمة الجديدة بنجاح ، فسيتم استدعاء وظيفة التحديث مرة أخرى بالقيمة الحالية الجديدة ، وتتم إعادة محاولة الكتابة.

على سبيل المثال ، في مثال تطبيق التدوين الاجتماعي ، يمكنك السماح للمستخدمين بتمييز المنشورات بنجمة وإلغاء التمييز بنجمة وتتبع عدد النجوم التي تلقاها المنشور على النحو التالي:

Web modular API

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 namespaced API

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;
  });
}

يمنع استخدام المعاملة عدد النجوم من أن يكون غير صحيح إذا كان هناك عدة مستخدمين يميزون المنشور نفسه في نفس الوقت أو كان لدى العميل بيانات قديمة. إذا تم رفض المعاملة ، يقوم الخادم بإرجاع القيمة الحالية إلى العميل ، والذي يقوم بتشغيل المعاملة مرة أخرى بالقيمة المحدثة. يتكرر هذا حتى يتم قبول المعاملة أو تقوم بإيقافها.

الزيادات الذرية من جانب الخادم

في حالة الاستخدام المذكورة أعلاه ، نكتب قيمتين إلى قاعدة البيانات: معرّف المستخدم الذي يميز / يزيل النجوم عن المشاركة ، وعدد النجوم المتزايد. إذا علمنا بالفعل أن المستخدم يقوم بتمثيل المنشور ، فيمكننا استخدام عملية الزيادة الذرية بدلاً من المعاملة.

Web modular API

function addStar(uid, key) {
  import { getDatabase, increment, ref, update } from "firebase/database";
  const dbRef = ref(getDatabase());

  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = increment(1);
  update(dbRef, updates);
}

Web namespaced API

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 بعد ذلك بمزامنة تلك البيانات مع خوادم قاعدة البيانات البعيدة ومع العملاء الآخرين على أساس "أفضل جهد".

نتيجة لذلك ، تقوم جميع عمليات الكتابة في قاعدة البيانات بتشغيل الأحداث المحلية على الفور ، قبل كتابة أي بيانات إلى الخادم. هذا يعني أن تطبيقك يظل مستجيبًا بغض النظر عن زمن انتقال الشبكة أو الاتصال.

بمجرد إعادة إنشاء الاتصال ، يتلقى تطبيقك مجموعة الأحداث المناسبة بحيث يتزامن العميل مع حالة الخادم الحالية ، دون الحاجة إلى كتابة أي رمز مخصص.

سنتحدث أكثر عن السلوك في وضع عدم الاتصال في معرفة المزيد حول إمكانات الاتصال بالإنترنت وغير المتصلة ..

الخطوات التالية