باستخدام Cloud Functions، يمكنك معالجة الأحداث في
Firebase Realtime Database بدون الحاجة إلى تعديل رمز العميل.
يتيح لك Cloud Functions تنفيذ عمليات Realtime Database باستخدام امتيازات إدارية كاملة، ويضمن معالجة كل تغيير في Realtime Database بشكلٍ فردي. يمكنك إجراء Firebase Realtime Database تغييرات من خلال
DataSnapshot
أو من خلال Admin SDK.
في دورة حياة نموذجية، تُجري دالة Firebase Realtime Database ما يلي:
- تنتظر التغييرات في موقع Realtime Database معيّن.
- يتم تشغيلها عند حدوث حدث وتنفيذ مهامها (راجِع ما الذي يمكنني فعله باستخدام Cloud Functions؟ للحصول على أمثلة على حالات الاستخدام).
- تتلقّى عنصر بيانات يحتوي على لقطة للبيانات المخزّنة في المستند المحدّد.
بدء دالة Realtime Database
أنشئ دوالّ جديدة لأحداث Realtime Database
باستخدام functions.database
. للتحكّم في وقت بدء الدالة، حدِّد أحد معالِجات الأحداث،
وحدِّد مسار Realtime Database الذي ستستمع فيه إلى الأحداث.
ضبط معالِج الحدث
تتيح لك الدوالّ التعامل مع أحداث Realtime Database على مستويَين من النوعية: يمكنك الاستماع تحديدًا إلى أحداث الإنشاء أو التعديل أو الحذف فقط، أو يمكنك الاستماع إلى أي تغيير من أي نوع على مسار. تتوافق Cloud Functions مع معالِجات الأحداث التالية في Realtime Database:
onWrite()
، الذي يتم تشغيله عند إنشاء البيانات أو تعديلها أو حذفها في Realtime DatabaseonCreate()
، الذي يتم تشغيله عند إنشاء بيانات جديدة في Realtime DatabaseonUpdate()
، الذي يتم تشغيله عند تعديل البيانات في Realtime DatabaseonDelete()
، الذي يتم تشغيله عند حذف البيانات من Realtime Database
تحديد العنصر والمسار
للتحكّم في وقت بدء الدالة ومكانها، يمكنك استدعاء ref(path)
لتحديد مسار، ويمكنك اختياريًا تحديد مثيل Realtime Database
باستخدام instance('INSTANCE_NAME')
. في حال عدم تحديد مثيل، يتم نشر الدالة على مثيل Realtime Database التلقائي لمشروع Firebase. على سبيل المثال:
- مثيل Realtime Database التلقائي:
functions.database.ref('/foo/bar')
- النسخة التي تحمل الاسم "my-app-db-2":
functions.database.instance('my-app-db-2').ref('/foo/bar')
توجّه هذه الطرق دالتك للتعامل مع عمليات الكتابة في مسار معيّن ضمن
مثيل Realtime Database. تتطابق مواصفات المسار مع كل عمليات الكتابة التي تؤثر في مسار،
بما في ذلك عمليات الكتابة
التي تحدث في أي مكان تحته. إذا ضبطت المسار
لوظيفتك على /foo/bar
، تتم مطابقة الأحداث في كلا الموقعَين التاليَين:
/foo/bar
/foo/bar/baz/really/deep/path
في كلتا الحالتَين، تفسّر Firebase أنّ الحدث يحدث في /foo/bar
،
وتتضمّن بيانات الحدث
البيانات القديمة والجديدة في /foo/bar
. إذا كانت بيانات الحدث قد تكون كبيرة،
ننصح باستخدام دوال متعددة في مسارات أعمق بدلاً من دالة واحدة
بالقرب من جذر قاعدة بياناتك. للحصول على أفضل أداء، اطلب فقط
البيانات على أدنى مستوى ممكن.
يمكنك تحديد مكوّن مسار كحرف بدل من خلال إحاطته بقوسين ملتويين، ويتطابق ref('foo/{bar}')
مع أي عنصر فرعي من /foo
. تتوفّر قيم مكونات مسار العلامة النائبة هذه ضمن عنصر
EventContext.params
لدالة. في هذا المثال، تتوفّر القيمة على النحو التالي:
context.params.bar
.
يمكن أن تتطابق المسارات التي تحتوي على أحرف متسلسلة بديلة مع أحداث متعددة من عملية كتابة واحدة. إدراج
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
يطابق المسار "/foo/{bar}"
مرتين: مرة مع "hello": "world"
ومرّة أخرى مع "firebase": "functions"
.
التعامل مع بيانات الأحداث
عند معالجة حدث Realtime Database، يكون عنصر البيانات الذي يتم إرجاعه هو
DataSnapshot
.
بالنسبة إلى أحداث onWrite
أو onUpdate
، تكون المَعلمة الأولى هي عنصر Change
يحتوي على لقطات مصوّرة تُمثّل حالة البيانات قبل الحدث المشغِّل وبعده. بالنسبة إلى حدثَي onCreate
وonDelete
،
يكون عنصر البيانات الذي يتم إرجاعه عبارة عن لقطة شاشة للبيانات التي تم إنشاؤها أو حذفها.
في هذا المثال، تسترجع الدالة اللقطة لملف السجلّ في المسار المحدّد، وتحوّل السلسلة في هذا الموضع إلى أحرف كبيرة، ثم تكتب هذه السلسلة المعدّلة في قاعدة البيانات:
// Listens for new messages added to /messages/:pushId/original and creates an // uppercase version of the message to /messages/:pushId/uppercase exports.makeUppercase = functions.database.ref('/messages/{pushId}/original') .onCreate((snapshot, context) => { // Grab the current value of what was written to the Realtime Database. const original = snapshot.val(); functions.logger.log('Uppercasing', context.params.pushId, original); const uppercase = original.toUpperCase(); // You must return a Promise when performing asynchronous tasks inside a Functions such as // writing to the Firebase Realtime Database. // Setting an "uppercase" sibling in the Realtime Database returns a Promise. return snapshot.ref.parent.child('uppercase').set(uppercase); });
الوصول إلى معلومات مصادقة المستخدم
من EventContext.auth
وEventContext.authType
،
يمكنك الوصول إلى
معلومات المستخدم، بما في ذلك الأذونات، للمستخدم الذي بدأ
إحدى الوظائف. يمكن أن يكون ذلك مفيدًا لفرض قواعد الأمان،
مما يسمح لدالة نموذجك بإكمال عمليات مختلفة استنادًا إلى
مستوى أذونات المستخدم:
const functions = require('firebase-functions/v1');
const admin = require('firebase-admin');
exports.simpleDbFunction = functions.database.ref('/path')
.onCreate((snap, context) => {
if (context.authType === 'ADMIN') {
// do something
} else if (context.authType === 'USER') {
console.log(snap.val(), 'written by', context.auth.uid);
}
});
يمكنك أيضًا الاستفادة من معلومات مصادقة المستخدمين "لمحاكاة" هوية مستخدم وتنفيذ عمليات الكتابة نيابةً عن المستخدم. احرص على حذف مثيل التطبيق كما هو موضّح أدناه لتجنُّب مشاكل التوافق:
exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
.onCreate((snap, context) => {
const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
appOptions.databaseAuthVariableOverride = context.auth;
const app = admin.initializeApp(appOptions, 'app');
const uppercase = snap.val().toUpperCase();
const ref = snap.ref.parent.child('uppercase');
const deleteApp = () => app.delete().catch(() => null);
return app.database().ref(ref).set(uppercase).then(res => {
// Deleting the app is necessary for preventing concurrency leaks
return deleteApp().then(() => res);
}).catch(err => {
return deleteApp().then(() => Promise.reject(err));
});
});
قراءة القيمة السابقة
يحتوي عنصر Change
على سمة
before
تسمح لك بفحص ما تم حفظه في Realtime Database قبل
الحدث. تعرض السمة before
DataSnapshot
حيث تشير كل ال methods (على سبيل المثال،
val()
و
exists()
)
إلى القيمة السابقة. يمكنك قراءة القيمة الجديدة مرة أخرى إما باستخدام
السمة الأصلية DataSnapshot
أو قراءة السمة
after
. هذه السمة في أيّ Change
هي DataSnapshot
أخرى تمثّل
حالة البيانات بعد وقوع الحدث.
على سبيل المثال، يمكن استخدام السمة before
للتأكّد من أنّ الدالة تحول النص إلى أحرف كبيرة فقط عند إنشائه لأول مرة:
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite((change, context) => {
// Only edit data when it is first created.
if (change.before.exists()) {
return null;
}
// Exit when the data is deleted.
if (!change.after.exists()) {
return null;
}
// Grab the current value of what was written to the Realtime Database.
const original = change.after.val();
console.log('Uppercasing', context.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return change.after.ref.parent.child('uppercase').set(uppercase);
});