البدء
يُرجى الاطّلاع أولاً على دليل Get Started إذا لم يسبق لك إعداد تطبيقك والوصول إلى قاعدة البيانات.
الحصول على مرجع DatabaseReference
لكتابة البيانات في قاعدة البيانات، تحتاج إلى نسخة من DatabaseReference:
// Get the root reference location of the database. firebase::database::DatabaseReference dbref = database->GetReference();
حفظ البيانات
هناك أربع طرق لكتابة البيانات في Firebase Realtime Database
| الطريقة | طرق الاستخدام الشائعة |
|---|---|
SetValue() |
كتابة البيانات أو استبدالها في مسار محدّد، مثل
users/<user-id>/<username>. |
PushChild() |
الإضافة إلى قائمة بيانات في كل مرة تستدعي فيها
Push()، تنشئ Firebase مفتاحًا فريدًا يمكن استخدامه أيضًا
كمُعرّف فريد، مثل
user-scores/<user-id>/<unique-score-id> |
UpdateChildren() |
تعديل بعض المفاتيح لمسار محدّد بدون استبدال جميع البيانات |
RunTransaction() |
تعديل البيانات المعقّدة التي قد تتلف بسبب التعديلات المتزامنة |
كتابة البيانات أو تعديلها أو حذفها في مرجع
عمليات الكتابة الأساسية
بالنسبة إلى عمليات الكتابة الأساسية، يمكنك استخدام SetValue() لحفظ البيانات في مرجع محدّد، ما يؤدي إلى استبدال أي بيانات حالية في هذا المسار. يمكنك استخدام هذه الطريقة لتمرير الأنواع التي يقبلها JSON من خلال نوع Variant الذي يتيح ما يلي:
- قيمة فارغة (يؤدي ذلك إلى حذف البيانات)
- أعداد صحيحة (64 بت)
- أعداد الفاصلة العائمة ذات الدقة المزدوجة
- قيم منطقية
- أوتار
- متجهات من المتغيرات
- خرائط من الأوتار إلى المتغيرات
يؤدي استخدام SetValue() بهذه الطريقة إلى الكتابة فوق البيانات في الموقع الجغرافي المحدّد، بما في ذلك أي عقد فرعية. ومع ذلك، لا يزال بإمكانك تعديل عقدة فرعية بدون إعادة كتابة الكائن بأكمله. إذا أردت السماح للمستخدمين بتعديل ملفاتهم الشخصية، يمكنك تعديل اسم المستخدم على النحو التالي:
dbref.Child("users").Child(userId).Child("username").SetValue(name);
إلحاق البيانات بقائمة
استخدِم طريقة PushChild() لإلحاق البيانات بقائمة في التطبيقات المتعددة المستخدمين.
تنشئ طريقة PushChild() مفتاحًا فريدًا في كل مرة تتم فيها إضافة عقدة فرعية جديدة إلى مرجع Firebase المحدّد. باستخدام هذه المفاتيح التي يتم إنشاؤها تلقائيًا لكل عنصر جديد في القائمة، يمكن لعدة عملاء إضافة عقد فرعية إلى الموقع الجغرافي نفسه في الوقت نفسه بدون حدوث تعارضات في الكتابة. يستند المفتاح الفريد الذي تم إنشاؤه بواسطة PushChild() إلى طابع زمني، لذا يتم ترتيب عناصر القائمة تلقائيًا بترتيب زمني.
يمكنك استخدام المرجع للبيانات الجديدة التي تعرضها طريقة PushChild() للحصول على قيمة المفتاح الذي تم إنشاؤه تلقائيًا للعقدة الفرعية أو ضبط بيانات العقدة الفرعية.
يؤدي استدعاء GetKey() على مرجع PushChild() إلى عرض قيمة المفتاح الذي تم إنشاؤه تلقائيًا.
تعديل حقول معيّنة
للكتابة في العقد الفرعية المحدّدة لعقدة في الوقت نفسه بدون الكتابة فوق العقد الفرعية الأخرى، استخدِم طريقة UpdateChildren().
عند استدعاء UpdateChildren()، يمكنك تعديل قيم العقد الفرعية ذات المستوى الأدنى من خلال تحديد مسار للمفتاح. إذا تم تخزين البيانات في مواقع جغرافية متعددة لتحسين إمكانية التوسّع
، يمكنك تعديل جميع نُسخ هذه البيانات باستخدام
توزيع موسَّع للبيانات. على سبيل المثال، قد تتضمّن لعبة فئة LeaderboardEntry على النحو التالي:
class LeaderboardEntry { std::string uid; int score = 0; public: LeaderboardEntry() { } LeaderboardEntry(std::string uid, int score) { this->uid = uid; this->score = score; } std::map<std::string, Object> ToMap() { std::map<string, Variant> result = new std::map<string, Variant>(); result["uid"] = Variant(uid); result["score"] = Variant(score); return result; } }
لإنشاء LeaderboardEntry وتعديلها في الوقت نفسه في خلاصة النتائج الأخيرة وقائمة نتائج المستخدم الخاصة، تستخدِم اللعبة الرمز التالي:
void WriteNewScore(std::string userId, int score) { // Create new entry at /user-scores/$userid/$scoreid and at // /leaderboard/$scoreid simultaneously std::string key = dbref.Child("scores").PushChild().GetKey(); LeaderBoardEntry entry = new LeaderBoardEntry(userId, score); std::map<std::string, Variant> entryValues = entry.ToMap(); std::map<string, Variant> childUpdates = new std::map<string, Variant>(); childUpdates["/scores/" + key] = entryValues; childUpdates["/user-scores/" + userId + "/" + key] = entryValues; dbref.UpdateChildren(childUpdates); }
يستخدِم هذا المثال PushChild() لإنشاء إدخال في العقدة التي تحتوي على إدخالات لجميع المستخدمين في /scores/$key واسترداد المفتاح في الوقت نفسه باستخدام key(). يمكن بعد ذلك استخدام المفتاح لإنشاء إدخال ثانٍ في نتائج المستخدم في /user-scores/$userid/$key.
باستخدام هذه المسارات، يمكنك إجراء تعديلات متزامنة على مواقع جغرافية متعددة في شجرة JSON من خلال استدعاء UpdateChildren() مرة واحدة، مثل الطريقة التي ينشئ بها هذا المثال الإدخال الجديد في كلا الموقعَين الجغرافية. تكون التعديلات المتزامنة التي يتم إجراؤها بهذه الطريقة كلّية: إما أن تنجح جميع التعديلات أو تفشل جميعها.
حذف البيانات
أسهل طريقة لحذف البيانات هي استدعاء RemoveValue() على مرجع لموقع هذه البيانات.
يمكنك أيضًا الحذف من خلال تحديد Variant بقيمة null كقيمة لعملية كتابة أخرى، مثل SetValue() أو UpdateChildren(). يمكنك استخدام هذه الطريقة مع UpdateChildren() لحذف عدة عقد فرعية في طلب واجهة برمجة تطبيقات واحد.
معرفة متى يتم إرسال بياناتك
لمعرفة متى يتم إرسال بياناتك إلى خادم Firebase Realtime Database، تحقَّق من نتيجة Future لمعرفة ما إذا كانت العملية ناجحة.
حفظ البيانات كمعاملات
عند التعامل مع البيانات التي قد تتلف بسبب التعديلات المتزامنة
، مثل العدادات التزايدية، يمكنك استخدام عملية
معاملة.
يمكنك منح هذه العملية دالة DoTransaction. تأخذ دالة التعديل هذه الحالة الحالية للبيانات كمعلَمة وتعرض الحالة الجديدة المطلوبة التي تريد كتابتها. إذا كتب عميل آخر في الموقع الجغرافي قبل كتابة القيمة الجديدة بنجاح، يتم استدعاء دالة التعديل مرة أخرى بالقيمة الحالية الجديدة، وتتم إعادة محاولة الكتابة.
على سبيل المثال، في إحدى الألعاب، يمكنك السماح للمستخدمين بتعديل لوحة الصدارة باستخدام أعلى خمس نتائج:
void AddScoreToLeaders(std::string email, long score, DatabaseReference leaderBoardRef) { leaderBoardRef.RunTransaction([](firebase::database::MutableData* mutableData) { if (mutableData.children_count() >= MaxScores) { long minScore = LONG_MAX; MutableData *minVal = null; std::vector<MutableData> children = mutableData.children(); std::vector<MutableData>::iterator it; for (it = children.begin(); it != children.end(); ++it) { if (!it->value().is_map()) continue; long childScore = (long)it->Child("score").value().int64_value(); if (childScore < minScore) { minScore = childScore; minVal = &*it; } } if (minScore > score) { // The new score is lower than the existing 5 scores, abort. return kTransactionResultAbort; } // Remove the lowest score. children.Remove(minVal); } // Add the new high score. std::map<std::string, Variant> newScoreMap = new std::map<std::string, Variant>(); newScoreMap["score"] = score; newScoreMap["email"] = email; children.Add(newScoreMap); mutableData->set_value(children); return kTransactionResultSuccess; }); }
يمنع استخدام المعاملة لوحة الصدارة من أن تكون غير صحيحة إذا سجّل عدة مستخدمين نتائج في الوقت نفسه أو إذا كان لدى العميل بيانات قديمة. إذا تم رفض المعاملة، يعرض الخادم القيمة الحالية للعميل، الذي يُجري المعاملة مرة أخرى بالقيمة المعدَّلة. ويتكرّر ذلك إلى أن يتم قبول المعاملة أو يتم إجراء عدد كبير جدًا من المحاولات.
كتابة البيانات بلا إنترنت
إذا فقد العميل الاتصال بالشبكة، سيستمر تطبيقك في العمل بشكل صحيح.
يحتفظ كل عميل متصل بقاعدة بيانات Firebase بنسخته الداخلية الخاصة من أي بيانات نشطة. عند كتابة البيانات، تتم كتابتها في هذه النسخة المحلية أولاً. بعد ذلك، يزامن عميل Firebase هذه البيانات مع خوادم قاعدة البيانات البعيدة ومع العملاء الآخرين بأفضل ما يمكنه.
نتيجةً لذلك، تؤدي جميع عمليات الكتابة في قاعدة البيانات إلى إطلاق أحداث محلية على الفور، قبل كتابة أي بيانات على الخادم. وهذا يعني أنّ تطبيقك يظل سريع الاستجابة بغض النظر عن وقت استجابة الشبكة أو الاتصال.
بمجرد استعادة الاتصال، يتلقّى تطبيقك المجموعة المناسبة من الأحداث حتى تتم مزامنة العميل مع حالة الخادم الحالية، بدون الحاجة إلى كتابة أي رمز مخصّص.