قبل البدء
قبل أن تتمكّن من استخدام Realtime Database، عليك إجراء ما يلي:
سجِّل مشروع Unity الخاص بك وأعدَّه لاستخدام Firebase.
إذا كان مشروع Unity يستخدم Firebase، يكون قد تم تسجيله وإعداده لاستخدام Firebase.
إذا لم يكن لديك مشروع Unity، يمكنك تنزيل نموذج تطبيق.
أضِف حزمة Firebase Unity SDK (تحديدًا
FirebaseDatabase.unitypackage) إلى مشروع Unity.
يُرجى العِلم أنّ إضافة Firebase إلى مشروع Unity تتضمّن مهامًا في كلّ من الـ Firebase console ومشروع Unity المفتوح (على سبيل المثال، يمكنك تنزيل ملفات إعداد Firebase من وحدة التحكّم، ثم نقل هذه الملفات إلى مشروع Unity).
تنظيم البيانات
يغطّي هذا الدليل بعض المفاهيم الرئيسية في بنية البيانات وأفضل الممارسات لتنظيم بيانات JSON في Firebase Realtime Database.
يتطلّب إنشاء قاعدة بيانات منظَّمة بشكل صحيح قدرًا كبيرًا من التفكير المسبق. الأهم من ذلك هو التخطيط لكيفية حفظ البيانات واستردادها لاحقًا لتسهيل هذه العملية قدر الإمكان.
كيفية تنظيم البيانات: هي عبارة عن شجرة JSON
يتم تخزين جميع بيانات Firebase Realtime Database ككائنات JSON. يمكنك اعتبار قاعدة البيانات كشجرة JSON مستضافة على السحابة الإلكترونية. على عكس قاعدة بيانات SQL، لا توجد جداول أو سجلات. عند إضافة بيانات إلى شجرة JSON، تصبح عقدة في بنية JSON الحالية مع مفتاح مرتبط. يمكنك تقديم مفاتيحك الخاصة، مثل أرقام تعريف المستخدمين أو الأسماء الدلالية، أو يمكن توفيرها لك باستخدام طريقة Push().
على سبيل المثال، لنفترض أنّ لديك تطبيق محادثة يتيح للمستخدمين تخزين ملف شخصي أساسي وقائمة جهات اتصال. يقع الملف الشخصي النموذجي للمستخدم في مسار، مثل /users/$uid. قد يكون للمستخدم alovelace إدخال في قاعدة البيانات يبدو على النحو التالي:
{ "users": { "alovelace": { "name": "Ada Lovelace", "contacts": { "ghopper": true }, }, "ghopper": { "..." }, "eclarke": { "..." } } }
على الرغم من أنّ قاعدة البيانات تستخدم شجرة JSON، يمكن تمثيل البيانات المخزّنة في قاعدة البيانات كأنواع أصلية معيّنة تتوافق مع أنواع JSON المتاحة لمساعدتك في كتابة رمز أكثر قابلية للصيانة.
أفضل الممارسات لبنية البيانات
تجنُّب البيانات المتداخلة
بما أنّ Firebase Realtime Database يسمح بتداخل البيانات حتى 32 مستوى، قد تميل إلى الاعتقاد بأنّ هذا يجب أن يكون البنية التلقائية. ومع ذلك، عند جلب البيانات في موقع معيّن في قاعدة البيانات، يتم أيضًا استرداد جميع العُقد الفرعية. بالإضافة إلى ذلك، عند منح شخص ما إذن الوصول للقراءة أو الكتابة في عقدة في قاعدة البيانات، يتم أيضًا منحه إذن الوصول إلى جميع البيانات ضِمن تلك العقدة. لذلك، من الأفضل في الواقع الحفاظ على بنية البيانات مسطّحة قدر الإمكان.
للحصول على مثال على سبب سوء البيانات المتداخلة، اطّلِع على البنية المتداخلة المتعددة التالية:
{ // This is a poorly nested data architecture, because iterating the children // of the "chats" node to get a list of conversation titles requires // potentially downloading hundreds of megabytes of messages "chats": { "one": { "title": "Historical Tech Pioneers", "messages": { "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." }, "m2": { ... }, // a very long list of messages } }, "two": { "..." } } }
باستخدام هذا التصميم المتداخل، يصبح تكرار البيانات أمرًا إشكاليًا. على سبيل المثال، يتطلّب إدراج عناوين محادثات الدردشة تنزيل شجرة chats بالكامل، بما في ذلك جميع الأعضاء والرسائل، إلى العميل.
تبسيط بنى البيانات
إذا تم بدلاً من ذلك تقسيم البيانات إلى مسارات منفصلة، يُطلق عليها أيضًا اسم إلغاء التسوية، يمكن تنزيلها بكفاءة في طلبات منفصلة، حسب الحاجة. اطّلِع على هذه البنية المسطّحة:
{ // Chats contains only meta info about each conversation // stored under the chats's unique ID "chats": { "one": { "title": "Historical Tech Pioneers", "lastMessage": "ghopper: Relay malfunction found. Cause: moth.", "timestamp": 1459361875666 }, "two": { "..." }, "three": { "..." } }, // Conversation members are easily accessible // and stored by chat conversation ID "members": { // we'll talk about indices like this below "one": { "ghopper": true, "alovelace": true, "eclarke": true }, "two": { "..." }, "three": { "..." } }, // Messages are separate from data we may want to iterate quickly // but still easily paginated and queried, and organized by chat // conversation ID "messages": { "one": { "m1": { "name": "eclarke", "message": "The relay seems to be malfunctioning.", "timestamp": 1459361875337 }, "m2": { "..." }, "m3": { "..." } }, "two": { "..." }, "three": { "..." } } }
أصبح من الممكن الآن تكرار قائمة الغرف من خلال تنزيل بضعة بايت فقط لكل محادثة، ما يؤدي إلى جلب بيانات وصفية بسرعة لإدراج الغرف أو عرضها في واجهة مستخدم. يمكن جلب الرسائل بشكل منفصل وعرضها عند وصولها، ما يسمح للواجهة بالبقاء سريعة الاستجابة.
إنشاء بيانات قابلة للتوسّع
عند إنشاء التطبيقات، من الأفضل غالبًا تنزيل مجموعة فرعية من القائمة. يكون هذا الإجراء شائعًا بشكل خاص إذا كانت القائمة تحتوي على آلاف السجلات. عندما تكون هذه العلاقة ثابتة وأحادية الاتجاه، يمكنك ببساطة تداخل الكائنات الفرعية ضِمن الكائن الرئيسي.
في بعض الأحيان، تكون هذه العلاقة أكثر ديناميكية، أو قد يكون من الضروري إلغاء تسوية هذه البيانات. في كثير من الأحيان، يمكنك إلغاء تسوية البيانات باستخدام طلب بحث لاسترداد مجموعة فرعية من البيانات، كما هو موضّح في استرداد البيانات.
ولكن حتى هذا قد لا يكون كافيًا. على سبيل المثال، لنفترض أنّ هناك علاقة ثنائية الاتجاه بين المستخدمين والمجموعات. يمكن للمستخدمين الانضمام إلى مجموعة، وتتضمّن المجموعات قائمة بالمستخدمين. عند تحديد المجموعات التي ينتمي إليها المستخدم، يصبح الأمر معقدًا.
ما نحتاج إليه هو طريقة أنيقة لإدراج المجموعات التي ينتمي إليها المستخدم وجلب بيانات هذه المجموعات فقط. يمكن أن يساعد فهرس المجموعات كثيرًا في هذه الحالة:
// An index to track Ada's memberships { "users": { "alovelace": { "name": "Ada Lovelace", // Index Ada's groups in her profile "groups": { // the value here doesn't matter, just that the key exists "techpioneers": true, "womentechmakers": true } }, // ... }, "groups": { "techpioneers": { "name": "Historical Tech Pioneers", "members": { "alovelace": true, "ghopper": true, "eclarke": true } }, // ... } }
قد تلاحظ أنّ هذا الإجراء يكرّر بعض البيانات من خلال تخزين العلاقة ضِمن سجلّ "آدا" وضِمن المجموعة. تم الآن فهرسة alovelace ضِمن مجموعة، وتم إدراج techpioneers في الملف الشخصي لـ "آدا". لذلك، لحذف "آدا" من المجموعة، يجب تعديلها في مكانَين.
هذه عملية تكرار ضرورية للعلاقات ثنائية الاتجاه. تسمح لك بـ جلب عضويات "آدا" بسرعة وكفاءة، حتى عندما يتوسّع نطاق قائمة المستخدمين أو المجموعات إلى الملايين أو عندما تمنع قواعد الأمان في Realtime Database الوصول إلى بعض السجلات.
يؤدي هذا النهج، الذي يعكس البيانات من خلال إدراج المعرّفات كمفاتيح وضبط القيمة على "صحيح"، إلى تسهيل التحقّق من المفتاح من خلال قراءة /users/$uid/groups/$group_id والتحقّق مما إذا كانت القيمة null. الفهرس أسرع وأكثر كفاءة من طلب البحث عن البيانات أو فحصها.