البدء
يُرجى الاطّلاع أولاً على دليل 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()
على مرجع إلى موقع تلك البيانات.
يمكنك أيضًا الحذف من خلال تحديد null
Variant
كقيمة لعملية كتابة أخرى، مثل 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 هذه البيانات مع خوادم قاعدة البيانات البعيدة ومع العملاء الآخرين على أساس "بذل أفضل جهد".
نتيجةً لذلك، تؤدي جميع عمليات الكتابة إلى قاعدة البيانات إلى تشغيل الأحداث المحلية على الفور، قبل كتابة أي بيانات على الخادم. وهذا يعني أنّ تطبيقك سيظل متجاوبًا بغض النظر عن وقت استجابة الشبكة أو الاتصال بها.
وبعد إعادة إنشاء الاتصال، يتلقّى تطبيقك مجموعة الأحداث المناسبة لكي تتم مزامنة العميل مع حالة الخادم الحالية، بدون الحاجة إلى كتابة أي رمز مخصّص.