حفظ البيانات

قبل البدء

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

  • سجِّل مشروعك على Unity واضبطه لاستخدام Firebase.

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

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

  • أضِف حزمة تطوير برامج Unity من Firebase (خصوصًا FirebaseDatabase.unitypackage) إلى مشروع Unity.

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

حفظ البيانات

توجد خمس طرق لكتابة البيانات في قاعدة بيانات Firebase في الوقت الفعلي:

الطريقة طرق الاستخدام الشائعة
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(). على سبيل المثال، قد يكون لديك فئة مستخدم بدت كما يلي:

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<string, Object> ToDictionary() {
        Dictionary<string, Object> result = new Dictionary<string, Object>();
        result["uid"] = uid;
        result["score"] = score;

        return result;
    }
}

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

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<string, Object> entryValues = entry.ToDictionary();

    Dictionary<string, Object> childUpdates = new Dictionary<string, Object>();
    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 في الوقت الفعلي، يمكنك إضافة متابعة. يعرض كل من SetValueAsync() وUpdateChildrenAsync() خطأ Task يتيح لك معرفة وقت اكتمال العملية. إذا لم يتم تنفيذ الطلب لأي سبب من الأسباب، ستكون قيمة IsFaulted صحيحة مع السمة Exception التي تشير إلى سبب التعذُّر.

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

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

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

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

    leaderBoardRef.RunTransaction(mutableData => {
      List<object> leaders = mutableData.Value as List<object>

      if (leaders == null) {
        leaders = new List<object>();
      } else if (mutableData.ChildrenCount >= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary<string, object>)) continue;
          long childScore = (long)
                      ((Dictionary<string, object>)child)["score"];
          if (childScore < minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore > 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<string, object> newScoreMap =
                       new Dictionary<string, object>();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

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

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

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

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

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

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

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