حفظ البيانات باستخدام قاعدة بيانات Firebase في الوقت الفعلي للغة C++

البدء

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

الحصول على DatabaseReference

لكتابة بيانات إلى قاعدة البيانات، أنت بحاجة إلى نسخة افتراضية من DatabaseReference:

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

حفظ البيانات

هناك أربع طرق لكتابة البيانات في قاعدة بيانات Firebase في الوقت الفعلي:

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

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

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

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

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

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

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

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

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

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