حفظ البيانات

قبل البدء

قبل أن تتمكّن من استخدام Realtime Database، عليك إجراء ما يلي:

  • سجِّل مشروع Unity وأعدّه لاستخدام Firebase.

    • إذا كان مشروعك في Unity يستخدم Firebase في الوقت الحالي، يعني ذلك أنّه تم تسجيله وإعداده من أجل Firebase.

    • إذا لم يكن لديك مشروع Unity، يمكنك تنزيل نموذج تطبيق.

  • أضِف Firebase حزمة تطوير البرامج (SDK) Unity (على وجه التحديد، FirebaseDatabase.unitypackage) إلى مشروعك على Unity.

تجدر الإشارة إلى أنّ إضافة Firebase إلى مشروعك على Unity تتضمن مهامًا في كلّ من وحدة تحكّم Firebase ومشروع Unity المفتوح (على سبيل المثال، يمكنك تنزيل ملفات إعدادات Firebase من وحدة التحكّم، ثم نقلها إلى مشروع Unity).

حفظ البيانات

هناك خمس طرق لكتابة البيانات في Firebase Realtime Database:

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

الحصول على DatabaseReference

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

using Firebase;
using Firebase.Database;

public class MyScript: MonoBehaviour {
  void Start() {
    // Get the root reference location of the database.
    DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference;
  }
}

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

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

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

  • string
  • long
  • double
  • bool
  • Dictionary<string, Object>
  • List<Object>

إذا كنت تستخدِم عنصرًا من النوع C#، يمكنك استخدام JsonUtility.ToJson() المضمّنة لتحويل العنصر إلى ملف Json خام واستدعاء SetRawJsonValueAsync(). على سبيل المثال، قد يكون لديك فئة User بالشكل التالي:

public class User {
    public string username;
    public string email;

    public User() {
    }

    public User(string username, string email) {
        this.username = username;
        this.email = email;
    }
}

يمكنك إضافة مستخدم لديه SetRawJsonValueAsync() على النحو التالي:

private void writeNewUser(string userId, string name, string email) {
    User user = new User(name, email);
    string json = JsonUtility.ToJson(user);

    mDatabaseRef.Child("users").Child(userId).SetRawJsonValueAsync(json);
}

ويؤدي استخدام SetValueAsync() أو SetRawJsonValueAsync() بهذه الطريقة إلى استبدال البيانات في المكان المحدّد، بما في ذلك أي عُقد فرعية. ومع ذلك، لا يزال بإمكانك تعديل عنصر فرعي بدون إعادة كتابة العنصر بأكمله. إذا كنت تريد السماح للمستخدمين بتحديث ملفاتهم الشخصية، فيمكنك تحديث اسم المستخدم على النحو التالي:

mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);

إلحاق البيانات بقائمة بيانات

استخدِم الطريقة Push() لإلحاق البيانات بقائمة في تطبيقات متعددة المستخدمين. تنشئ الطريقة Push() مفتاحًا فريدًا في كل مرة تتم فيها إضافة عنصر ثانوي جديد إلى مرجع Firebase المحدد. باستخدام هذه المفاتيح التي يتم إنشاؤها تلقائيًا لكل عنصر جديد في القائمة، يمكن لعدة عملاء إضافة عناصر فرعية إلى الموقع الجغرافي نفسه في الوقت نفسه بدون حدوث تعارضات في الكتابة. ويستند المفتاح الفريد الذي يتم إنشاؤه من خلال Push() إلى طابع زمني، لذا يتم ترتيب عناصر القائمة تلقائيًا حسب التسلسل الزمني.

يمكنك استخدام إشارة إلى البيانات الجديدة التي تعرضها الطريقة Push() للحصول على قيمة مفتاح الطفل الذي تم إنشاؤه تلقائيًا أو ضبط بيانات للطفل. يؤدي استدعاء Key على مرجع Push() إلى عرض قيمة المفتاح الذي يتم إنشاؤه تلقائيًا.

تعديل حقول معيّنة

للكتابة في الوقت نفسه إلى عناصر ثانوية محدّدة لعقدة ما بدون استبدال العُقد الفرعية الأخرى، يمكنك استخدام الطريقة UpdateChildrenAsync().

عند استدعاء UpdateChildrenAsync()، يمكنك تعديل القيم الفرعية ذات المستوى الأدنى من خلال تحديد مسار للمفتاح. إذا تم تخزين البيانات في مواقع متعددة لتوسيع نطاقها بشكل أفضل، يمكنك تعديل جميع نُسخ تلك البيانات باستخدام ميزة توزيع البيانات. على سبيل المثال، قد تحتوي اللعبة على فئة LeaderboardEntry على النحو التالي:

public class LeaderboardEntry {
    public string uid;
    public int score = 0;

    public LeaderboardEntry() {
    }

    public LeaderboardEntry(string uid, int score) {
        this.uid = uid;
        this.score = score;
    }

    public Dictionary&ltstring, Object&gt ToDictionary() {
        Dictionary&ltstring, Object&gt result = new Dictionary&ltstring, Object&gt();
        result["uid"] = uid;
        result["score"] = score;

        return result;
    }
}

لإنشاء LeaderboardEntry وتعديله في الوقت نفسه إلى خلاصة النتيجة الحديثة وقائمة نتائج المستخدم، تستخدم اللعبة رمزًا برمجيًا على النحو التالي:

private void WriteNewScore(string userId, int score) {
    // Create new entry at /user-scores/$userid/$scoreid and at
    // /leaderboard/$scoreid simultaneously
    string key = mDatabase.Child("scores").Push().Key;
    LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
    Dictionary&ltstring, Object&gt entryValues = entry.ToDictionary();

    Dictionary&ltstring, Object&gt childUpdates = new Dictionary&ltstring, Object&gt();
    childUpdates["/scores/" + key] = entryValues;
    childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

    mDatabase.UpdateChildrenAsync(childUpdates);
}

يستخدم هذا المثال Push() لإنشاء إدخال في العقدة يحتوي على إدخالات لجميع المستخدمين في /scores/$key واسترداد المفتاح في الوقت نفسه باستخدام Key. ويمكن بعد ذلك استخدام المفتاح لإنشاء إدخال ثانٍ في نتائج المستخدم على /user-scores/$userid/$key.

باستخدام هذه المسارات، يمكنك إجراء تعديلات متزامنة على مواقع جغرافية متعددة في شجرة JSON من خلال طلب واحد إلى UpdateChildrenAsync()، مثل الطريقة التي ينشئ بها هذا المثال الإدخال الجديد في كلا الموقعَين الجغرافيَّين. إنّ التعديلات المتزامنة التي يتم إجراؤها بهذه الطريقة تكون شاملة: إما أن تنجح جميع التعديلات أو تُتعذّر جميعها.

حذف البيانات

إنّ أبسط طريقة لحذف البيانات هي استدعاء RemoveValue() باستخدام مرجع إلى موقع تلك البيانات.

يمكنك أيضًا إجراء عملية حذف من خلال تحديد null كقيمة لعملية كتابة أخرى، مثل SetValueAsync() أو UpdateChildrenAsync(). يمكنك استخدام هذه المحاولة مع UpdateChildrenAsync() لحذف عدة أطفال في طلب واحد لواجهة برمجة التطبيقات.

معرفة وقت التزام بياناتك

لمعرفة وقت التزام بياناتك بخادم Firebase Realtime Database، يمكنك إضافة عملية متابعة. يعرض كل من SetValueAsync() وUpdateChildrenAsync() خطأ Task يتيح لك معرفة وقت اكتمال العملية. إذا تعذّر الاستدعاء لأي سبب، ستكون مَعلمة "المهام" IsFaulted صحيحة مع تحديد الخاصية Exception سبب حدوث الخطأ.

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

عند العمل مع بيانات يمكن أن تتلف بسبب تعديلات مماثلة، مثل العدّادات المتزايدة، يمكنك استخدام عملية معاملة. يمكنك منح هذه العملية Func. تأخذ عملية التعديل هذه Func الحالة الحالية للبيانات كوسيطة وتُعرِض الحالة الجديدة المطلوبة التي تريد كتابتها. إذا كتب عميل آخر إلى الموقع قبل كتابة القيمة الجديدة بنجاح، يتم استدعاء دالة التحديث مرة أخرى بالقيمة الحالية الجديدة، وتتم إعادة محاولة الكتابة.

على سبيل المثال، في لعبة، يمكنك السماح للمستخدمين بتعديل قائمة الصدارة التي تتضمّن أعلى خمس نتائج:

private void AddScoreToLeaders(string email, 
                               long score,
                               DatabaseReference leaderBoardRef) {

    leaderBoardRef.RunTransaction(mutableData =&gt {
      List&ltobject&gt leaders = mutableData.Value as List&ltobject>

      if (leaders == null) {
        leaders = new List&ltobject&gt();
      } else if (mutableData.ChildrenCount &gt= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary&ltstring, object&gt)) continue;
          long childScore = (long)
                      ((Dictionary&ltstring, object&gt)child)["score"];
          if (childScore &lt minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore &gt score) {
          // The new score is lower than the existing 5 scores, abort.
          return TransactionResult.Abort();
        }

        // Remove the lowest score.
        leaders.Remove(minVal);
      }

      // Add the new high score.
      Dictionary&ltstring, object&gt newScoreMap =
                       new Dictionary&ltstring, object&gt();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

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

كتابة البيانات بلا إنترنت

إذا فقد العميل الاتصال بالشبكة، سيواصل تطبيقك العمل بشكل صحيح.

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

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

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

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