قبل البدء
قبل أن تتمكّن من استخدام Realtime Database، عليك إجراء ما يلي:
سجِّل مشروع Unity الخاص بك وأعدَّه لاستخدام Firebase.
أضِف حزمة 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. الفهرس أسرع وأكثر كفاءة بكثير من طلب البحث عن البيانات أو فحصها.