قاعدة بيانات Firestore: الإضافة والقراءة والتحديث الفوري
Cloud Firestore هي قاعدة بيانات Firebase: تخزن بيانات تطبيقك على خوادم Google، ويقرؤها ويكتبها تطبيقك مباشرة دون أن تكتب أي كود خادم. وأجمل ميزاتها التحديث الفوري: إذا غيّر مستخدم شيئاً، تظهر النتيجة على شاشات كل المستخدمين الآخرين خلال لحظات. في هذا الدرس ستبني قاعدة بيانات طلاب، وتجرب كل العمليات الأساسية.
المجموعات والمستندات
Section titled “المجموعات والمستندات”Firestore قاعدة بيانات NoSQL مثل MongoDB، وتنظم البيانات هكذا:
- المجموعة (Collection): مثل
students، تحتوي مستندات. - المستند (Document): بيانات طالب واحد بحقول وقيم، وله معرّف فريد.
students (مجموعة)├── omar (مستند) → { name: 'عمر', age: 15, city: 'الإسكندرية', score: 78 }├── mona (مستند) → { name: 'منى', age: 13, city: 'طنطا', score: 85 }└── ...تخيّلها مثل خزانة ملفات: كل درج مجموعة، وكل ملف داخله مستند له رقم على غلافه.
ولإنشاء قاعدة البيانات: من Firebase Console اختر Firestore Database ثم Create database. وكل الأمثلة التالية تستخدم db التي جهزناها في ملف firebase.js في الدرس السابق.
الإضافة: addDoc و setDoc
Section titled “الإضافة: addDoc و setDoc”addDoc: يضيف مستنداً بمعرّف تلقائي:
import { collection, addDoc } from 'firebase/firestore';
const ref = await addDoc(collection(db, 'students'), { name: 'سارة', age: 14, city: 'القاهرة', score: 92});console.log(ref.id);ref.id نص عشوائي فريد من 20 حرفاً يولّده Firestore، مثل Xk9p2LmQr7vT4wZa1bCd.
setDoc: تختار المعرّف بنفسك:
import { doc, setDoc } from 'firebase/firestore';
await setDoc(doc(db, 'students', 'omar'), { name: 'عمر', age: 15, city: 'الإسكندرية', score: 78});doc(db, 'students', 'omar') تشير إلى المستند omar داخل مجموعة students. ولاحظ await: كل عمليات Firestore غير متزامنة، لأنها تتصل بالإنترنت، تماماً كما تعلمت عن async و await في JavaScript.
قراءة مستند: getDoc
Section titled “قراءة مستند: getDoc”import { getDoc } from 'firebase/firestore';
const snap = await getDoc(doc(db, 'students', 'omar'));console.log(snap.exists(), snap.id, snap.data());true omar { name: 'عمر', age: 15, city: 'الإسكندرية', score: 78 }getDocتُرجع لقطة (Snapshot) للمستند.exists(): هل المستند موجود؟ لمستند غير موجود ستُرجعfalse.data(): الحقول ككائن JavaScript عادي.
الاستعلام: where و orderBy
Section titled “الاستعلام: where و orderBy”بعد إضافة سارة وعمر ومنى ويوسف (من القاهرة بدرجة 64)، نريد طلاب القاهرة مرتبين بالدرجة:
import { query, where, orderBy, getDocs } from 'firebase/firestore';
const q = query( collection(db, 'students'), where('city', '==', 'القاهرة'), orderBy('score', 'desc'));const result = await getDocs(q);result.forEach((d) => console.log(d.data().name, d.data().score));سارة 92يوسف 64where(الحقل, المقارنة, القيمة): المقارنات==و!=و<و<=و>و>=، وinوarray-containsللمصفوفات.orderBy('score', 'desc'): الترتيب، و'asc'للتصاعدي.limit(n): أقصى عدد للنتائج.getDocs: تُرجع مجموعة لقطات، نمر عليها بـforEach.
التعديل: updateDoc
Section titled “التعديل: updateDoc”import { updateDoc, increment, arrayUnion } from 'firebase/firestore';
await updateDoc(doc(db, 'students', 'omar'), { score: increment(5), hobbies: arrayUnion('كرة القدم')});بعدها يصبح مستند عمر:
{ name: 'عمر', age: 15, city: 'الإسكندرية', score: 83, hobbies: [ 'كرة القدم' ] }updateDoc: يعدّل الحقول المذكورة فقط.increment(5): يزيد الرقم 5، وهو آمن عندما يعدّل عدة مستخدمين نفس الرقم في نفس اللحظة.arrayUnion: يضيف عنصراً لمصفوفة دون تكرار، وينشئها إن لم تكن موجودة.
الحذف: deleteDoc
Section titled “الحذف: deleteDoc”import { deleteDoc } from 'firebase/firestore';await deleteDoc(doc(db, 'students', 'youssef'));السحر: التحديث الفوري بـ onSnapshot
Section titled “السحر: التحديث الفوري بـ onSnapshot”بدلاً من قراءة البيانات مرة واحدة، يمكنك الاستماع لها:
import { onSnapshot } from 'firebase/firestore';
const unsubscribe = onSnapshot(doc(db, 'students', 'mona'), (snap) => { console.log('الدرجة الآن:', snap.data().score);});الدالة تُنفَّذ فوراً بالقيمة الحالية (85)، ثم تُنفَّذ تلقائياً كلما تغير المستند. عندما عدّلنا درجة منى إلى 90 من مكان آخر، وصلت القيمتان بالترتيب: 85 ثم 90، دون أي طلب جديد منا.
هكذا تُبنى تطبيقات المحادثة ولوحات النتائج المباشرة: كل مستخدم «يستمع» للبيانات، والتغييرات تصل للجميع فوراً. وعندما لا تحتاج الاستماع، مثلاً عند مغادرة الصفحة، استدعِ unsubscribe() لإيقافه.
قواعد الأمان: لمحة أولى
Section titled “قواعد الأمان: لمحة أولى”افتراضياً، يجب أن تحدد من يقرأ ويكتب. قواعد الأمان تُكتب في Console أو في ملف firestore.rules:
rules_version = '2';service cloud.firestore { match /databases/{database}/documents { match /students/{id} { allow read: if true; allow write: if request.auth != null; } }}القراءة متاحة للجميع، والكتابة للمستخدمين المسجلين فقط. ستفهم request.auth في الدرس القادم عن المصادقة.
جرّب بنفسك
Section titled “جرّب بنفسك”- أنشئ Firestore في مشروعك، أو شغّل المحاكي.
- أضف 5 كتب لمجموعة
books، اثنان منها بـsetDocبمعرّفات من اختيارك. - اعرض الكتب التي سعرها أقل من 100 مرتبة بالسعر.
- زِد عدد نسخ كتاب بـ
increment. - اكتب
onSnapshotعلى كتاب، ثم عدّله من Firebase Console، وشاهد التحديث يصل فوراً.
أخطاء شائعة
Section titled “أخطاء شائعة”- نسيان await: تحصل على Promise بدلاً من البيانات، أو تُنفَّذ الأسطر بترتيب غير متوقع.
- setDoc بدل updateDoc: يمسح الحقول غير المذكورة.
- ترك القواعد مفتوحة للجميع: القاعدة
allow read, write: if trueعلى بيانات حقيقية تعني أن أي شخص يستطيع حذف كل بياناتك.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما الفرق بين addDoc و setDoc؟
addDoc يضيف مستنداً بمعرّف عشوائي، و setDoc يكتب في مستند بمعرّف تختاره، ويستبدله إن كان موجوداً.
سؤال
ما الذي يميز onSnapshot عن getDoc؟
getDoc تقرأ مرة واحدة، أما onSnapshot فتستمع وتصلك كل التحديثات فوراً.
الخلاصة
Section titled “الخلاصة”- Firestore تنظم البيانات في مجموعات ومستندات، وكل العمليات غير متزامنة مع
await. addDocبمعرّف تلقائي، وsetDocبمعرّف تختاره، وupdateDocلتعديل حقول محددة.getDocلمستند واحد، وqueryمعwhereوorderByوlimitثمgetDocsللاستعلام.incrementوarrayUnionتعديلات آمنة، وdeleteDocللحذف.onSnapshotللتحديث الفوري، وقواعد الأمان تحمي البيانات.