حفظ البيانات

قبل البدء

قبل أن تتمكّن من استخدام Realtime Database، عليك تنفيذ ما يلي:

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

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

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

  • أضِف حزمة تطوير البرامج (SDK) لـ Firebase 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<string, Object> ToDictionary() {
        Dictionary<string, Object> result = new Dictionary<string, Object>();
        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<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 Realtime Database، يمكنك إضافة استمرارية. تُرجع كل من SetValueAsync() وUpdateChildrenAsync() Task تتيح لك معرفة وقت اكتمال العملية. إذا لم تنجح المكالمة لأي سبب، ستكون قيمة Tasks 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 هذه البيانات مع خوادم قواعد البيانات البعيدة ومع العملاء الآخرين على أساس "بذل قصارى الجهد".

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

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

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