باستخدام Cloud Functions، يمكنك معالجة الأحداث في الـ
Firebase Realtime Database بدون الحاجة إلى تعديل رمز العميل.
تتيح لك Cloud Functions تنفيذ عمليات Realtime Database بامتيازات إدارية كاملة
، وتضمن معالجة كل تغيير في Realtime Database بشكل فردي. يمكنك إجراء Firebase Realtime Database تغييرات عبر
DataSnapshot
أو عبر حزمة تطوير البرامج (SDK) للمشرف.
في دورة حياة نموذجية، تنفّذ دالة Firebase Realtime Database ما يلي:
- تنتظر إجراء تغييرات في موقع معيّن في Realtime Database.
- يتم تشغيلها عند وقوع حدث وتنفّذ مهامها (راجِع ما يمكنني فعله باستخدام Cloud Functions؟ للاطّلاع على أمثلة على حالات الاستخدام).
- تتلقّى كائن بيانات يحتوي على لقطة للبيانات المخزّنة في المستند المحدّد.
تشغيل دالة Realtime Database
يمكنك إنشاء دوال جديدة لأحداث Realtime Database
باستخدام functions.database. للتحكّم في وقت تشغيل الدالة، حدِّد أحد معالِجات الأحداث، و
حدِّد مسار Realtime Database الذي ستستمع فيه إلى الأحداث.
ضبط معالِج الأحداث
تتيح لك الدوال معالجة أحداث Realtime Database على مستويَين من التفاصيل؛ يمكنك الاستماع إلى أحداث الإنشاء أو التعديل أو الحذف فقط، أو يمكنك الاستماع إلى أي تغيير من أي نوع في مسار. Cloud Functions تتوافق مع معالِجات الأحداث التالية في Realtime Database
onWrite(): يتم تشغيلها عند إنشاء بيانات أو تعديلها أو حذفها في Realtime Database.onCreate(): يتم تشغيلها عند إنشاء بيانات جديدة في Realtime Database.onUpdate(): يتم تشغيلها عند تعديل البيانات في Realtime Database .onDelete(): يتم تشغيلها عند حذف البيانات من 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 تشير فيه جميع
الطرق (مثل
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);
});