درس تطبيقي حول ترميز الويب في Cloud Firestore

1. نظرة عامة

الأهداف

في هذا الدرس التطبيقي حول الترميز، ستنشئ تطبيق ويب لاقتراح المطاعم يستند إلى Cloud Firestore.

img5.png

ما ستتعلمه

  • قراءة البيانات وكتابتها في Cloud Firestore من تطبيق ويب
  • الاستماع إلى التغييرات في بيانات Cloud Firestore في الوقت الفعلي
  • استخدام خدمة Firebase Authentication وقواعد الأمان لتأمين بيانات Cloud Firestore
  • كتابة طلبات بحث معقّدة في Cloud Firestore

المتطلبات

قبل البدء في هذا الدرس العملي، تأكَّد من تثبيت ما يلي:

  • npm الذي يأتي عادةً مع Node.js - يُنصح باستخدام Node 16 أو إصدار أحدث
  • بيئة التطوير المتكاملة (IDE) أو محرّر النصوص الذي تختاره، مثل WebStorm أو VS Code أو Sublime

2. إنشاء مشروع Firebase وإعداده

إنشاء مشروع Firebase

  1. سجِّل الدخول إلى وحدة تحكّم Firebase باستخدام حسابك على Google.
  2. انقر على الزر لإنشاء مشروع جديد، ثم أدخِل اسم المشروع (على سبيل المثال، FriendlyEats).
  3. انقر على متابعة.
  4. إذا طُلب منك ذلك، راجِع بنود Firebase واقبلها، ثم انقر على متابعة.
  5. (اختياري) فعِّل ميزة "المساعدة المستندة إلى الذكاء الاصطناعي" في وحدة تحكّم Firebase (المعروفة باسم "Gemini في Firebase").
  6. في هذا الدرس العملي، لا تحتاج إلى "إحصاءات Google"، لذا أوقِف خيار "إحصاءات Google".
  7. انقر على إنشاء مشروع، وانتظِر إلى أن يتم توفير مشروعك، ثم انقر على متابعة.

إعداد منتجات Firebase

يستخدم التطبيق الذي سننشئه بعض خدمات Firebase المتاحة على الويب:

  • خدمة "مصادقة Firebase" لتحديد هوية المستخدمين بسهولة
  • ‫Cloud Firestore لحفظ البيانات المنظَّمة على السحابة الإلكترونية وتلقّي إشعار فوري عند تعديل البيانات
  • استضافة Firebase لاستضافة مواد العرض الثابتة وتقديمها

في هذا الدرس العملي المبرمَج، سبق أن أعددنا خدمة Firebase Hosting. ومع ذلك، بالنسبة إلى Firebase Auth وCloud Firestore، سنشرح لك كيفية إعداد الخدمات وتفعيلها باستخدام وحدة تحكّم Firebase.

تفعيل ميزة "المصادقة بدون تحديد الهوية"

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

عليك تفعيل خيار تسجيل الدخول بدون الكشف عن الهوية.

  1. في وحدة تحكّم Firebase، ابحث عن قسم الإنشاء في شريط التنقّل الأيمن.
  2. انقر على المصادقة، ثم انقر على علامة التبويب طريقة تسجيل الدخول (أو انقر هنا للانتقال مباشرةً إلى هناك).
  3. فعِّل موفّر تسجيل الدخول بدون اسم، ثم انقر على حفظ.

img7.png

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

تفعيل Cloud Firestore

يستخدم التطبيق Cloud Firestore لحفظ معلومات المطاعم وتقييماتها وتلقّيها.

عليك تفعيل Cloud Firestore. في قسم إنشاء في وحدة تحكّم Firebase، انقر على قاعدة بيانات Firestore. انقر على إنشاء قاعدة بيانات في لوحة Cloud Firestore.

يتم التحكّم في الوصول إلى البيانات في Cloud Firestore من خلال "قواعد الأمان". سنتحدّث أكثر عن القواعد لاحقًا في هذا الدرس العملي، ولكن علينا أولاً ضبط بعض القواعد الأساسية على بياناتنا للبدء. في علامة التبويب "القواعد" في وحدة تحكّم Firebase، أضِف القواعد التالية ثم انقر على نشر.

rules_version = '2';
service cloud.firestore {

  // Determine if the value of the field "key" is the same
  // before and after the request.
  function unchanged(key) {
    return (key in resource.data)
      && (key in request.resource.data)
      && (resource.data[key] == request.resource.data[key]);
  }

  match /databases/{database}/documents {
    // Restaurants:
    //   - Authenticated user can read
    //   - Authenticated user can create/update (for demo purposes only)
    //   - Updates are allowed if no fields are added and name is unchanged
    //   - Deletes are not allowed (default)
    match /restaurants/{restaurantId} {
      allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys())
                    && unchanged("name");

      // Ratings:
      //   - Authenticated user can read
      //   - Authenticated user can create if userId matches
      //   - Deletes and updates are not allowed (default)
      match /ratings/{ratingId} {
        allow read: if request.auth != null;
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;
      }
    }
  }
}

سنتناول هذه القواعد وكيفية عملها لاحقًا في هذا الدرس العملي.

3- الحصول على الرمز النموذجي

استنسِخ مستودع GitHub من سطر الأوامر:

git clone https://github.com/firebase/friendlyeats-web

من المفترض أن تكون عينة التعليمات البرمجية قد تم استنساخها في الدليل 📁friendlyeats-web. من الآن فصاعدًا، احرص على تنفيذ جميع الأوامر من هذا الدليل:

cd friendlyeats-web/vanilla-js

استيراد تطبيق البداية

باستخدام بيئة التطوير المتكاملة (WebStorm أو Atom أو Sublime أو Visual Studio Code أو غيرها)، افتح الدليل 📁friendlyeats-web أو استورِده. يحتوي هذا الدليل على الرمز البرمجي الأولي لبرنامج التدريب العملي الذي يتألف من تطبيق لا يعمل بعد لاقتراح المطاعم. وسنعمل على جعله يعمل خلال برنامج التدريب العملي هذا، لذا ستحتاج إلى تعديل الرمز البرمجي في هذا الدليل قريبًا.

4. تثبيت واجهة سطر الأوامر في Firebase

تتيح لك واجهة سطر الأوامر (CLI) في Firebase عرض تطبيق الويب محليًا ونشره على "استضافة Firebase".

  1. ثبِّت واجهة سطر الأوامر من خلال تنفيذ أمر npm التالي:
npm -g install firebase-tools
  1. تأكَّد من تثبيت واجهة سطر الأوامر بشكلٍ صحيح من خلال تنفيذ الأمر التالي:
firebase --version

تأكَّد من أنّ إصدار واجهة سطر الأوامر (CLI) لمنصة Firebase هو 7.4.0 أو إصدار أحدث.

  1. امنح الإذن لواجهة سطر الأوامر في Firebase من خلال تنفيذ الأمر التالي:
firebase login

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

  1. تأكَّد من أنّ سطر الأوامر يصل إلى الدليل المحلي لتطبيقك.
  2. اربط تطبيقك بمشروعك على Firebase من خلال تنفيذ الأمر التالي:
firebase use --add
  1. عندما يُطلب منك ذلك، اختَر رقم تعريف المشروع، ثم امنح مشروعك على Firebase اسمًا مستعارًا.

يكون الاسم المستعار مفيدًا إذا كان لديك بيئات متعددة (مثل بيئة الإصدار العلني وبيئة التقسيم المرحلي وما إلى ذلك). ومع ذلك، سنستخدم في هذا الدرس العملي اسم default المستعار.

  1. اتّبِع التعليمات المتبقية في سطر الأوامر.

5- تشغيل الخادم المحلي

نحن على استعداد لبدء العمل على تطبيقنا فعليًا. لننفّذ تطبيقنا على الجهاز.

  1. نفِّذ أمر Firebase CLI التالي:
firebase emulators:start --only hosting
  1. يجب أن يعرض سطر الأوامر الردّ التالي:
hosting: Local server: http://localhost:5000

نستخدم محاكي استضافة Firebase لعرض تطبيقنا محليًا. من المفترض أن يكون تطبيق الويب متاحًا الآن من خلال http://localhost:5000.

  1. افتح تطبيقك على http://localhost:5000.

من المفترض أن تظهر لك نسختك من FriendlyEats التي تم ربطها بمشروعك على Firebase.

تم ربط التطبيق تلقائيًا بمشروعك على Firebase وتم تسجيل دخولك بصفتك مستخدمًا مجهول الهوية بدون إشعارك.

img2.png

6. كتابة البيانات في Cloud Firestore

في هذا القسم، سنكتب بعض البيانات في Cloud Firestore حتى نتمكّن من ملء واجهة مستخدم التطبيق. يمكن إجراء ذلك يدويًا من خلال وحدة تحكّم Firebase، ولكن سننفّذه في التطبيق نفسه لتوضيح عملية كتابة أساسية في Cloud Firestore.

نموذج البيانات

يتم تقسيم بيانات Firestore إلى مجموعات ومستندات وحقول ومجموعات فرعية. سنخزّن كل مطعم كمستند في مجموعة على أعلى مستوى تُسمى restaurants.

img3.png

لاحقًا، سنخزّن كل مراجعة في مجموعة فرعية باسم ratings ضمن كل مطعم.

img4.png

إضافة مطاعم إلى Firestore

عنصر النموذج الرئيسي في تطبيقنا هو مطعم. لنكتب بعض الرموز التي تضيف مستند مطعم إلى المجموعة restaurants.

  1. من الملفات التي نزّلتها، افتح scripts/FriendlyEats.Data.js.
  2. ابحث عن الدالة FriendlyEats.prototype.addRestaurant.
  3. استبدِل الدالة بأكملها بالرمز التالي.

FriendlyEats.Data.js

FriendlyEats.prototype.addRestaurant = function(data) {
  var collection = firebase.firestore().collection('restaurants');
  return collection.add(data);
};

يضيف الرمز أعلاه مستندًا جديدًا إلى المجموعة restaurants. تأتي بيانات المستند من عنصر JavaScript عادي. ننفّذ ذلك من خلال الحصول أولاً على مرجع إلى مجموعة Cloud Firestore restaurants ثم add البيانات.

لنضِف مطاعم.

  1. ارجع إلى تطبيق FriendlyEats في المتصفّح وأعِد تحميله.
  2. انقر على إضافة بيانات تجريبية.

سينشئ التطبيق تلقائيًا مجموعة عشوائية من عناصر المطاعم، ثم يستدعي الدالة addRestaurant. ومع ذلك، لن تظهر البيانات بعد في تطبيق الويب الفعلي لأنّنا ما زلنا بحاجة إلى تنفيذ عملية استرداد البيانات (القسم التالي من الدرس العملي).

إذا انتقلت إلى علامة التبويب Cloud Firestore في "وحدة تحكّم Firebase"، من المفترض أن تظهر لك الآن مستندات جديدة في المجموعة restaurants.

img6.png

تهانينا، لقد كتبت البيانات للتو في Cloud Firestore من تطبيق ويب.

في القسم التالي، ستتعرّف على كيفية استرداد البيانات من Cloud Firestore وعرضها في تطبيقك.

7. عرض البيانات من Cloud Firestore

في هذا القسم، ستتعرّف على كيفية استرداد البيانات من Cloud Firestore وعرضها في تطبيقك. الخطوتان الرئيسيتان هما إنشاء طلب بحث وإضافة أداة معالجة لبيانات اللقطة. سيتم إعلام المستمع بجميع البيانات الحالية التي تتطابق مع طلب البحث، وسيتلقّى التعديلات في الوقت الفعلي.

أولاً، لننشئ طلب البحث الذي سيعرض القائمة التلقائية غير المفلترة للمطاعم.

  1. ارجع إلى الملف scripts/FriendlyEats.Data.js.
  2. ابحث عن الدالة FriendlyEats.prototype.getAllRestaurants.
  3. استبدِل الدالة بأكملها بالرمز التالي.

FriendlyEats.Data.js

FriendlyEats.prototype.getAllRestaurants = function(renderer) {
  var query = firebase.firestore()
      .collection('restaurants')
      .orderBy('avgRating', 'desc')
      .limit(50);

  this.getDocumentsInQuery(query, renderer);
};

في الرمز أعلاه، ننشئ طلب بحث يسترد ما يصل إلى 50 مطعمًا من المجموعة ذات المستوى الأعلى المسماة restaurants، والتي يتم ترتيبها حسب متوسط التقييم (كلها أصفار حاليًا). بعد تعريف طلب البحث هذا، نمرّره إلى طريقة getDocumentsInQuery() المسؤولة عن تحميل البيانات وعرضها.

سننفّذ ذلك من خلال إضافة أداة معالجة لبيانات اللقطة.

  1. ارجع إلى الملف scripts/FriendlyEats.Data.js.
  2. ابحث عن الدالة FriendlyEats.prototype.getDocumentsInQuery.
  3. استبدِل الدالة بأكملها بالرمز التالي.

FriendlyEats.Data.js

FriendlyEats.prototype.getDocumentsInQuery = function(query, renderer) {
  query.onSnapshot(function(snapshot) {
    if (!snapshot.size) return renderer.empty(); // Display "There are no restaurants".

    snapshot.docChanges().forEach(function(change) {
      if (change.type === 'removed') {
        renderer.remove(change.doc);
      } else {
        renderer.display(change.doc);
      }
    });
  });
};

في الرمز البرمجي أعلاه، سيؤدي query.onSnapshot إلى تشغيل دالة معاودة الاتصال كلما حدث تغيير في نتيجة طلب البحث.

  • في المرة الأولى، يتم تشغيل دالة معاودة الاتصال مع مجموعة النتائج الكاملة لطلب البحث، أي مجموعة restaurants بأكملها من Cloud Firestore. بعد ذلك، يتم تمرير جميع المستندات الفردية إلى الدالة renderer.display.
  • عند حذف مستند، تكون قيمة change.type مساوية لقيمة removed. في هذه الحالة، سنستدعي دالة تزيل المطعم من واجهة المستخدم.

بعد تنفيذ الطريقتَين، أعِد تحميل التطبيق وتأكَّد من أنّ المطاعم التي رأيناها سابقًا في وحدة تحكّم Firebase تظهر الآن في التطبيق. إذا أكملت هذا القسم بنجاح، يعني ذلك أنّ تطبيقك يقرأ البيانات ويكتبها باستخدام Cloud Firestore.

ومع تغيُّر قائمة المطاعم، سيواصل هذا المستمع التحديث تلقائيًا. جرِّب الانتقال إلى وحدة تحكّم Firebase وحذف أحد المطاعم يدويًا أو تغيير اسمه، وستظهر التغييرات على موقعك الإلكتروني فورًا.

img5.png

8. Get() data

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

يجب تنفيذ طريقة يتم تشغيلها عندما ينقر المستخدم على مطعم معيّن في تطبيقك.

  1. ارجع إلى ملفك scripts/FriendlyEats.Data.js.
  2. ابحث عن الدالة FriendlyEats.prototype.getRestaurant.
  3. استبدِل الدالة بأكملها بالرمز التالي.

FriendlyEats.Data.js

FriendlyEats.prototype.getRestaurant = function(id) {
  return firebase.firestore().collection('restaurants').doc(id).get();
};

بعد تنفيذ هذه الطريقة، ستتمكّن من عرض صفحات لكل مطعم. ما عليك سوى النقر على مطعم في القائمة، وستظهر لك صفحة تفاصيل المطعم:

img1.png

في الوقت الحالي، لا يمكنك إضافة تقييمات لأنّنا ما زلنا بحاجة إلى تنفيذ إضافة التقييمات لاحقًا في هذا الدرس العملي.

9. ترتيب البيانات وتصفيتها

يعرض تطبيقنا حاليًا قائمة بالمطاعم، ولكن لا يمكن للمستخدم فلترة النتائج استنادًا إلى احتياجاته. في هذا القسم، ستستخدم ميزة طلب البحث المتقدّم في Cloud Firestore لتفعيل الفلترة.

في ما يلي مثال على طلب بحث بسيط لاسترداد جميع المطاعم التي تحمل الاسم Dim Sum:

var filteredQuery = query.where('category', '==', 'Dim Sum')

وكما يوحي الاسم، ستجعل طريقة where() الاستعلام ينزّل فقط عناصر المجموعة التي تستوفي حقولها القيود التي وضعناها. في هذه الحالة، سيتم تنزيل المطاعم التي تكون فيها قيمة category هي Dim Sum فقط.

في تطبيقنا، يمكن للمستخدم ربط فلاتر متعددة لإنشاء طلبات بحث محدّدة، مثل "بيتزا في القاهرة" أو "مأكولات بحرية في الإسكندرية حسب الترتيب حسب الشهرة".

سننشئ طريقة لإنشاء طلب بحث يفلتر مطاعمنا استنادًا إلى معايير متعددة يختارها المستخدمون.

  1. ارجع إلى ملفك scripts/FriendlyEats.Data.js.
  2. ابحث عن الدالة FriendlyEats.prototype.getFilteredRestaurants.
  3. استبدِل الدالة بأكملها بالرمز التالي.

FriendlyEats.Data.js

FriendlyEats.prototype.getFilteredRestaurants = function(filters, renderer) {
  var query = firebase.firestore().collection('restaurants');

  if (filters.category !== 'Any') {
    query = query.where('category', '==', filters.category);
  }

  if (filters.city !== 'Any') {
    query = query.where('city', '==', filters.city);
  }

  if (filters.price !== 'Any') {
    query = query.where('price', '==', filters.price.length);
  }

  if (filters.sort === 'Rating') {
    query = query.orderBy('avgRating', 'desc');
  } else if (filters.sort === 'Reviews') {
    query = query.orderBy('numRatings', 'desc');
  }

  this.getDocumentsInQuery(query, renderer);
};

يضيف الرمز البرمجي أعلاه فلاتر where متعددة وعبارة orderBy واحدة لإنشاء طلب بحث مركّب استنادًا إلى إدخال المستخدم. لن يعرض طلب البحث الآن سوى المطاعم التي تستوفي متطلبات المستخدم.

أعِد تحميل تطبيق FriendlyEats في المتصفّح، ثم تأكَّد من إمكانية الفلترة حسب السعر والمدينة والفئة. أثناء الاختبار، ستظهر لك أخطاء في "وحدة تحكّم JavaScript" في المتصفّح على النحو التالي:

The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=...

تحدث هذه الأخطاء لأنّ Cloud Firestore تتطلّب فهارس لمعظم طلبات البحث المركّبة. يضمن طلب الفهارس في طلبات البحث أن تظلّ Cloud Firestore سريعة عند توسيع نطاقها.

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

10. نشر الفهارس

إذا لم نكن نريد استكشاف كل مسار في تطبيقنا واتّباع كل روابط إنشاء الفهرس، يمكننا بسهولة نشر العديد من الفهارس في وقت واحد باستخدام Firebase CLI.

  1. في الدليل المحلي الذي تم تنزيله في تطبيقك، ستجد ملف firestore.indexes.json.

يصف هذا الملف جميع الفهارس اللازمة لجميع المجموعات المحتملة من الفلاتر.

firestore.indexes.json

{
 "indexes": [
   {
     "collectionGroup": "restaurants",
     "queryScope": "COLLECTION",
     "fields": [
       { "fieldPath": "city", "order": "ASCENDING" },
       { "fieldPath": "avgRating", "order": "DESCENDING" }
     ]
   },

   ...

 ]
}
  1. يمكنك نشر هذه الفهارس باستخدام الأمر التالي:
firebase deploy --only firestore:indexes

بعد بضع دقائق، سيتم نشر الفهارس وستختفي رسائل الخطأ.

11. كتابة البيانات في إحدى المعاملات

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

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

لحسن الحظ، توفّر Cloud Firestore وظيفة المعاملات التي تتيح لنا إجراء عمليات قراءة وكتابة متعددة في عملية ذرية واحدة، ما يضمن بقاء بياناتنا متسقة.

  1. ارجع إلى ملفك scripts/FriendlyEats.Data.js.
  2. ابحث عن الدالة FriendlyEats.prototype.addRating.
  3. استبدِل الدالة بأكملها بالرمز التالي.

FriendlyEats.Data.js

FriendlyEats.prototype.addRating = function(restaurantID, rating) {
  var collection = firebase.firestore().collection('restaurants');
  var document = collection.doc(restaurantID);
  var newRatingDocument = document.collection('ratings').doc();

  return firebase.firestore().runTransaction(function(transaction) {
    return transaction.get(document).then(function(doc) {
      var data = doc.data();

      var newAverage =
          (data.numRatings * data.avgRating + rating.rating) /
          (data.numRatings + 1);

      transaction.update(document, {
        numRatings: data.numRatings + 1,
        avgRating: newAverage
      });
      return transaction.set(newRatingDocument, rating);
    });
  });
};

في الفقرة أعلاه، نفعّل عملية لتعديل القيم الرقمية avgRating وnumRatings في مستند المطعم. في الوقت نفسه، نضيف rating الجديد إلى المجموعة الفرعية ratings.

12. تأمين بياناتك

في بداية هذا الدرس العملي، ضبطنا قواعد الأمان في تطبيقنا لتقييد الوصول إليه.

firestore.rules

rules_version = '2';
service cloud.firestore {

  // Determine if the value of the field "key" is the same
  // before and after the request.
  function unchanged(key) {
    return (key in resource.data)
      && (key in request.resource.data)
      && (resource.data[key] == request.resource.data[key]);
  }

  match /databases/{database}/documents {
    // Restaurants:
    //   - Authenticated user can read
    //   - Authenticated user can create/update (for demo purposes only)
    //   - Updates are allowed if no fields are added and name is unchanged
    //   - Deletes are not allowed (default)
    match /restaurants/{restaurantId} {
      allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys())
                    && unchanged("name");

      // Ratings:
      //   - Authenticated user can read
      //   - Authenticated user can create if userId matches
      //   - Deletes and updates are not allowed (default)
      match /ratings/{ratingId} {
        allow read: if request.auth != null;
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;
      }
    }
  }
}

وتقيّد هذه القواعد إمكانية الوصول لضمان عدم إجراء العملاء إلا للتغييرات الآمنة. على سبيل المثال:

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

بدلاً من استخدام وحدة تحكّم Firebase، يمكنك استخدام واجهة سطر الأوامر (CLI) في Firebase لنشر القواعد في مشروعك على Firebase. يحتوي ملف firestore.rules في دليل العمل على القواعد الواردة أعلاه. لنشر هذه القواعد من نظام الملفات المحلي (بدلاً من استخدام وحدة تحكّم Firebase)، عليك تنفيذ الأمر التالي:

firebase deploy --only firestore:rules

13. الخاتمة

في هذا الدرس التطبيقي حول الترميز، تعلّمت كيفية إجراء عمليات قراءة وكتابة أساسية ومتقدّمة باستخدام Cloud Firestore، بالإضافة إلى كيفية تأمين الوصول إلى البيانات باستخدام قواعد الأمان. يمكنك العثور على الحل الكامل في مستودع quickstarts-js.

لمزيد من المعلومات حول Cloud Firestore، يُرجى الاطّلاع على المراجع التالية:

14. [اختياري] فرض التحقّق باستخدام App Check

توفّر خدمة فحص التطبيقات من Firebase الحماية من خلال المساعدة في التحقّق من صحة الزيارات غير المرغوب فيها إلى تطبيقك ومنعها. في هذه الخطوة، ستؤمّن الوصول إلى خدماتك من خلال إضافة خدمة "فحص التطبيقات" باستخدام reCAPTCHA Enterprise.

عليك أولاً تفعيل App Check وreCaptcha.

تفعيل reCaptcha Enterprise

  1. في Cloud Console، ابحث عن reCaptcha Enterprise واختَرها ضمن "الأمان".
  2. فعِّل الخدمة حسب التعليمات، ثم انقر على إنشاء مفتاح.
  3. أدخِل اسمًا معروضًا حسب الطلب، واختَر موقعًا إلكترونيًا كنوع المنصّة.
  4. أضِف عناوين URL التي تم نشرها إلى قائمة النطاقات، وتأكَّد من أنّ الخيار "استخدام تحدّي مربّع الاختيار" غير محدّد.
  5. انقر على إنشاء مفتاح، وخزِّن المفتاح الذي تم إنشاؤه في مكان آمن. ستحتاج إليه لاحقًا في هذه الخطوة.

تفعيل خدمة App Check

  1. في وحدة تحكّم Firebase، ابحث عن قسم الإنشاء في اللوحة اليمنى.
  2. انقر على App Check، ثم انقر على زر البدء (أو سيتم إعادة توجيهك مباشرةً إلى وحدة التحكّم).
  3. انقر على تسجيل وأدخِل مفتاح reCaptcha Enterprise عندما يُطلب منك ذلك، ثم انقر على حفظ.
  4. في "عرض واجهات برمجة التطبيقات" (APIs View)، اختَر مساحة التخزين وانقر على فرض. كرِّر الإجراء نفسه مع Cloud Firestore.

من المفترض أن يتم الآن فرض استخدام App Check. أعِد تحميل التطبيق وحاوِل إنشاء مطعم أو الاطّلاع عليه. من المفترض أن تظهر لك رسالة الخطأ التالية:

Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

وهذا يعني أنّ خدمة App Check تحظر الطلبات غير الصالحة تلقائيًا. لنضِف الآن عملية التحقّق من صحة البيانات إلى تطبيقك.

انتقِل إلى ملف FriendlyEats.View.js، وعدِّل الدالة initAppCheck وأضِف مفتاح reCaptcha لتهيئة App Check.

FriendlyEats.prototype.initAppCheck = function() {
    var appCheck = firebase.appCheck();
    appCheck.activate(
    new firebase.appCheck.ReCaptchaEnterpriseProvider(
      /* reCAPTCHA Enterprise site key */
    ),
    true // Set to true to allow auto-refresh.
  );
};

يتمّ تهيئة مثيل appCheck باستخدام ReCaptchaEnterpriseProvider مع مفتاحك، ويتيح isTokenAutoRefreshEnabled إعادة تحميل الرموز المميّزة تلقائيًا في تطبيقك.

لتفعيل الاختبار المحلي، ابحث عن القسم الذي يتم فيه تهيئة التطبيق في ملف FriendlyEats.js، وأضِف السطر التالي إلى الدالة FriendlyEats.prototype.initAppCheck:

if(isLocalhost) {
  self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}

سيؤدي ذلك إلى تسجيل رمز تصحيح الأخطاء في وحدة تحكّم تطبيق الويب المحلي على النحو التالي:

App Check debug token: 8DBDF614-649D-4D22-B0A3-6D489412838B. You will need to add it to your app's App Check settings in the Firebase console for it to work.

الآن، انتقِل إلى عرض التطبيقات في ميزة "فحص التطبيقات" ضِمن "وحدة تحكّم Firebase".

انقر على قائمة الخيارات الإضافية، ثم اختَر إدارة رموز تصحيح الأخطاء.

بعد ذلك، انقر على إضافة رمز تصحيح الأخطاء والصِق رمز تصحيح الأخطاء من وحدة التحكّم كما هو مطلوب.

تهانينا! من المفترض أن تعمل خدمة App Check الآن في تطبيقك.