Check out what’s new from Firebase at Google I/O 2022. Learn more

حفظ البيانات باستخدام Firebase Realtime Database لـ C ++

البدء

راجع دليل Get Started أولاً إذا لم تقم بإعداد تطبيقك والوصول إلى قاعدة البيانات.

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

لكتابة البيانات إلى قاعدة البيانات ، تحتاج إلى مثيل DatabaseReference :

    // Get the root reference location of the database.
    firebase::database::DatabaseReference dbref = database->GetReference();

حفظ البيانات

توجد أربع طرق لكتابة البيانات إلى قاعدة بيانات Firebase Realtime:

طريقة الاستخدامات الشائعة
SetValue() اكتب البيانات أو استبدلها بمسار محدد ، مثل users/<user-id>/<username> .
PushChild() أضف إلى قائمة البيانات. في كل مرة تتصل فيها بـ Push() ، يُنشئ Firebase مفتاحًا فريدًا يمكن استخدامه أيضًا كمعرف فريد ، مثل user-scores/<user-id>/<unique-score-id> .
UpdateChildren() قم بتحديث بعض المفاتيح لمسار محدد دون استبدال كافة البيانات.
RunTransaction() تحديث البيانات المعقدة التي قد تتلف بسبب التحديثات المتزامنة.

كتابة أو تحديث أو حذف البيانات في مرجع

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

لعمليات الكتابة الأساسية ، يمكنك استخدام SetValue() لحفظ البيانات في مرجع محدد ، واستبدال أي بيانات موجودة في ذلك المسار. يمكنك استخدام هذه الطريقة لتمرير الأنواع المقبولة بواسطة JSON من خلال نوع متغير يدعم:

  • لاغية (هذا يحذف البيانات)
  • الأعداد الصحيحة (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 ، تحقق من نتيجة المستقبل للنجاح.

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

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

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

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

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