دمج Firebase مع تطبيق Next.js

1. قبل البدء

في هذا الدرس التطبيقي حول الترميز، ستتعرّف على كيفية دمج Firebase مع تطبيق ويب على Next.js يُسمى Friendly Eats، وهو موقع إلكتروني لمراجعات المطاعم.

تطبيق الويب Friendly Eats

يوفّر تطبيق الويب المكتمل ميزات مفيدة توضّح كيف يمكن أن تساعدك Firebase في إنشاء تطبيقات Next.js. وتشمل هذه الميزات ما يلي:

  • الإنشاء والنشر التلقائيان: يستخدِم هذا الدليل التعليمي حول رموز البرامج استضافة التطبيقات في Firebase لإنشاء رمز Next.js ونشره تلقائيًا في كل مرة يتم فيها الدفع إلى فرع تم ضبطه.
  • تسجيل الدخول وتسجيل الخروج: يتيح لك تطبيق الويب المكتمل تسجيل الدخول باستخدام حساب Google وتسجيل الخروج. تتم إدارة تسجيل دخول المستخدم واستمرارية تسجيله بالكامل من خلال Firebase Authentication.
  • الصور: يتيح تطبيق الويب المكتمل للمستخدمين الذين سجّلوا الدخول تحميل صور المطاعم. يتم تخزين مواد عرض الصور في Cloud Storage لبرنامج Firebase. توفّر حزمة تطوير البرامج (SDK) لبرنامج Firebase JavaScript عنوان URL متاحًا للجميع للصور المحمَّلة. ويتم بعد ذلك تخزين عنوان URL العام هذا في مستند المطعم ذي الصلة في Cloud Firestore.
  • المراجعات: يتيح تطبيق الويب المكتمل للمستخدمين الذين سجّلوا الدخول نشر مراجعات عن المطاعم تتألف من تقييم بالنجوم ورسالة نصية. يتم تخزين معلومات المراجعة في Cloud Firestore.
  • الفلاتر: يتيح تطبيق الويب المكتمل للمستخدمين الذين سجّلوا الدخول فلترة قائمة المطاعم حسب الفئة والموقع الجغرافي والسعر. يمكنك أيضًا تخصيص طريقة الترتيب المستخدَمة. يتم الوصول إلى البيانات من Cloud Firestore، ويتم تطبيق طلبات البحث في Firestore استنادًا إلى الفلاتر المستخدَمة.

المتطلبات الأساسية

  • حساب على GitHub
  • معرفة Next.js وJavaScript

ما ستتعرّف عليه

  • كيفية استخدام Firebase مع "مسار التطبيق" في Next.js والعرض من جهة الخادم
  • كيفية الاحتفاظ بالصور في "مساحة تخزين سحابية لـ Firebase"
  • كيفية قراءة البيانات وكتابتها في قاعدة بيانات Cloud Firestore
  • كيفية استخدام ميزة "تسجيل الدخول باستخدام حساب Google" مع حزمة Firebase JavaScript SDK

المتطلبات

  • Git
  • أحدث إصدار ثابت من Node.js
  • متصفّح من اختيارك، مثل Google Chrome
  • بيئة تطوير تتضمّن محرِّر رموز ووحدة تحكّم
  • حساب Google لإنشاء مشروعك على Firebase وإدارته
  • إمكانية ترقية مشروعك على Firebase إلى خطة أسعار Blaze

2- إعداد بيئة التطوير ومستودع GitHub

يوفّر هذا الدرس التطبيقي حول الترميز قاعدة بيانات التطبيق الأساسية ويعتمد على واجهة برمجة التطبيقات Firebase CLI.

إنشاء مستودع على GitHub

يمكن العثور على مصدر ورشة رموز البرامج على الرابط https://github.com/firebase/friendlyeats-web. يحتوي المستودع على نماذج مشاريع لمنصّات متعدّدة. ومع ذلك، لا يستخدم هذا الدليل التعريفي سوى الدليل nextjs-start. يُرجى ملاحظة الدلائل التالية:

* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.

انسخ مجلد nextjs-start إلى مستودعك:

  1. باستخدام وحدة طرفية، أنشئ مجلدًا جديدًا على جهاز الكمبيوتر وانتقِل إلى الدليل الجديد:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. استخدِم حزمة npm giget لجلب مجلد nextjs-start فقط:
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. تتبُّع التغييرات محليًا باستخدام git:
    git init
    
    git add .
    
    git commit -m "codelab starting point"
    
    git branch -M main
    
  4. أنشئ مستودعًا جديدًا على GitHub: https://github.com/new. يمكنك اختيار أي اسم تريده.
  5. انسخ عنوان URL الجديد الذي ينشئه لك GitHub. سيظهر على النحو التالي:
    • https://github.com/<USER_NAME>/<REPOSITORY_NAME>.git أو
    • git@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
  6. ادفع التغييرات المحلية إلى مستودع GitHub الجديد من خلال تنفيذ الأمر التالي. استبدِل العنصر النائب <REPOSITORY_URL> بعنوان URL الفعلي للمستودع.
    git remote add origin <REPOSITORY_URL>
    
    git push -u origin main
    
  7. من المفترض أن يظهر لك الرمز المبدئي في مستودع GitHub.

تثبيت واجهة برمجة التطبيقات Firebase CLI أو تحديثها

شغِّل الأمر التالي للتأكّد من تثبيت واجهة برمجة التطبيقات Firebase CLI وأنّها الإصدار 14.1.0 أو إصدار أحدث:

firebase --version

إذا ظهر لك إصدار أقل أو لم يكن لديك Firebase CLI مثبّتًا، شغِّل الأمر install:

npm install -g firebase-tools@latest

إذا لم تتمكّن من تثبيت واجهة سطر أوامر Firebase بسبب أخطاء في الأذونات، اطّلِع على مستندات npm أو استخدِم خيار تثبيت آخر.

تسجيل الدخول إلى Firebase

  1. نفِّذ الأمر التالي لتسجيل الدخول إلى واجهة برمجة التطبيقات Firebase CLI:
    firebase login
    
  2. بناءً على ما إذا كنت تريد أن تجمع Firebase البيانات، أدخِل Y أو N.
  3. في المتصفّح، اختَر حسابك على Google، ثم انقر على السماح.

3- إعداد مشروعك على Firebase

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

إنشاء مشروع على Firebase

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

ترقية خطة أسعار Firebase

لاستخدام ميزتَي "استضافة التطبيقات" و"مساحة التخزين في السحابة الإلكترونية" من Firebase، يجب أن يكون مشروعك على Firebase مُدرَجًا في خطة الأسعار "الدفع حسب الاستخدام" (Blaze)، ما يعني أنّه مرتبط بحساب على "الفوترة في السحابة الإلكترونية".

لترقية مشروعك إلى خطة Blaze، اتّبِع الخطوات التالية:

  1. في "وحدة تحكّم Firebase"، اختَر ترقية خطتك.
  2. اختَر خطة Blaze. اتّبِع التعليمات الظاهرة على الشاشة لربط حساب "فوترة على Cloud" بمشروعك.
    إذا كنت بحاجة إلى إنشاء حساب "فوترة على Cloud" كجزء من هذه الترقية، قد تحتاج إلى الرجوع إلى مسار الترقية في وحدة تحكّم Firebase لإكمال الترقية.

إضافة تطبيق ويب إلى مشروعك على Firebase

  1. انتقِل إلى نظرة عامة على المشروع في مشروعك على Firebase، ثم انقر على e41f2efdd9539c31.png الموقع الإلكتروني.

    إذا كانت لديك تطبيقات مسجّلة في مشروعك، انقر على إضافة تطبيق لعرض رمز الموقع الإلكتروني.
  2. في مربّع نص لقب التطبيق، أدخِل لقبًا سهل التذكر للتطبيق، مثل My Next.js app.
  3. لا تضع علامة في مربّع الاختيار إعداد ميزة "استضافة Firebase" لهذا التطبيق أيضًا.
  4. انقر على تسجيل التطبيق > متابعة إلى وحدة التحكّم.

إعداد خدمات Firebase في وحدة تحكُّم Firebase

إعداد المصادقة

  1. في وحدة تحكُّم Firebase، انتقِل إلى المصادقة.
  2. انقر على البدء.
  3. في عمود موفّري الخدمات الإضافية، انقر على Google > تفعيل.
  4. في مربّع نص الاسم المعروض للجمهور للمشروع، أدخِل اسمًا يسهل تذكُّره، مثل My Next.js app.
  5. من القائمة المنسدلة عنوان البريد الإلكتروني لفريق الدعم المعني بالمشروع، اختَر عنوان بريدك الإلكتروني.
  6. انقر على حفظ.

إعداد Cloud Firestore

  1. في اللوحة اليمنى من وحدة تحكّم Firebase، وسِّع الإنشاء، ثم اختَر قاعدة بيانات Firestore.
  2. انقر على إنشاء قاعدة بيانات.
  3. اترك رقم تعريف قاعدة البيانات مضبوطًا على (default).
  4. اختَر موقعًا لقاعدة بياناتك، ثم انقر على التالي.
    بالنسبة إلى التطبيق الحقيقي، عليك اختيار موقع قريب من المستخدمين.
  5. انقر على بدء التطبيق في وضع الاختبار. اقرأ بيان إخلاء المسؤولية عن قواعد الأمان.
    في وقت لاحق من هذا الدليل التعليمي، ستضيف قواعد أمان لتأمين بياناتك. لا توزِّع تطبيقًا أو تعرضه للجميع بدون إضافة قواعد أمان لقاعدة بياناتك.
  6. انقر على إنشاء.

إعداد Cloud Storage لـ Firebase

  1. في اللوحة اليمنى من وحدة تحكّم Firebase، وسِّع الإصدار، ثم اختَر مساحة التخزين.
  2. انقر على البدء.
  3. اختَر موقعًا جغرافيًا لحزمة التخزين التلقائية.
    يمكن للحِزم في US-WEST1 وUS-CENTRAL1 وUS-EAST1 الاستفادة من المستوى"مجاني دائمًا" في Google Cloud Storage. تخضع الحِزم في جميع المواقع الجغرافية الأخرى لأسعار Google Cloud Storage واستخدامها.
  4. انقر على بدء التطبيق في وضع الاختبار. اقرأ بيان إخلاء المسؤولية بشأن قواعد الأمان.
    في وقت لاحق من هذا الدليل التعليمي، ستضيف قواعد أمان لتأمين بياناتك. لا توزِّع تطبيقًا علنًا أو تعرضه بدون إضافة قواعد أمان لحزمة التخزين.
  5. انقر على إنشاء.

نشر قواعد الأمان

يتضمّن الرمز مجموعة من قواعد الأمان لكلّ من Firestore و"مساحة التخزين السحابي لـ Firebase". بعد نشر "قواعد الأمان"، تتم حماية البيانات في قاعدة بياناتك وحزمة التخزين بشكل أفضل من إساءة الاستخدام.

  1. في المحطة الطرفية، اضبط سطر الأوامر لاستخدام مشروع Firebase الذي أنشأته في وقت سابق:
    firebase use --add
    
    أدخِل friendlyeats-codelab عندما يُطلب منك إدخال اسم معرِّف.
  2. لنشر قواعد الأمان هذه (بالإضافة إلى الفهارس التي ستحتاج إليها لاحقًا)، شغِّل الأمر التالي في الوحدة الطرفية:
    firebase deploy --only firestore,storage
    
  3. إذا طُلب منك: "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?"، اضغط على Enter لاختيار نعم.

4. مراجعة قاعدة بيانات التطبيق الأساسي

في هذا القسم، ستراجع بعض أقسام قاعدة بيانات التطبيق الأساسية التي ستضيف إليها وظائف في هذا الدرس التطبيقي حول الترميز.

بنية المجلدات والملفات

يحتوي الجدول التالي على نظرة عامة حول بنية المجلدات والملفات في التطبيق:

المجلدات والملفات

الوصف

src/components

مكوّنات React للفلاتر والعناوين وتفاصيل المطاعم والمراجعات

src/lib

دوالّ المرافق التي لا تكون مرتبطة بالضرورة بـ React أو Next.js

src/lib/firebase

الرمز البرمجي الخاص بمنصّة Firebase وإعداداتها

public

مواد العرض الثابتة في تطبيق الويب، مثل الرموز

src/app

التوجيه باستخدام "موجِّه تطبيقات Next.js"

package.json وpackage-lock.json

تبعيات المشروع باستخدام npm

next.config.js

الإعدادات الخاصة بخدمة Next.js (إجراءات الخادم مفعَّلة)

jsconfig.json

إعدادات خدمة لغات JavaScript

مكوّنات الخادم والعميل

التطبيق هو تطبيق ويب يستخدم Next.js ويستخدم App Router. يتم استخدام ميزة العرض من جهة الخادم في جميع أنحاء التطبيق. على سبيل المثال، ملف src/app/page.js هو مكوّن خادم مسؤول عن الصفحة الرئيسية. ملف src/components/RestaurantListings.jsx هو مكوّن عملاء يُشار إليه بالتوجيه "use client" في بداية الملف.

عبارات الاستيراد

قد تلاحظ عبارات استيراد مثل ما يلي:

import RatingPicker from "@/src/components/RatingPicker.jsx";

يستخدم التطبيق الرمز @ لتجنُّب المسارات النسبية المعقدة للاستيراد، ويُتاح ذلك من خلال الأسماء البديلة للمسارات.

واجهات برمجة التطبيقات الخاصة بمنصّة Firebase

يتم تضمين كل رمز Firebase API في الدليل src/lib/firebase. بعد ذلك، تستورد مكوّنات React الفردية الدوالّ المُغلفة من الدليل src/lib/firebase بدلاً من استيراد دوالّ Firebase مباشرةً.

بيانات وهمية

يتم تضمين بيانات المطاعم والمراجعات الزائفة في ملف src/lib/randomData.js. يتم تجميع البيانات من هذا الملف في الرمز البرمجي في ملف src/lib/fakeRestaurants.js.

5- إنشاء خلفية لاستضافة التطبيقات

في هذا القسم، ستُعدّ واجهة خلفية استضافة التطبيقات لمشاهدة فرع في مستودع git.

بحلول نهاية هذا القسم، سيكون لديك خلفية "استضافة التطبيقات" متصلة بمستودعك في GitHub، وستعيد هذه الخلفية تلقائيًا إنشاء إصدار جديد من تطبيقك وتطرحه كلما دفعت رمزًا جديدًا إلى فرع main.

إنشاء خلفية

  1. انتقِل إلى صفحة "استضافة التطبيقات" في وحدة تحكُّم Firebase:

الحالة الصفرية لمحطة تحكّم &quot;استضافة التطبيقات&quot;، مع زر &quot;البدء&quot;

  1. انقر على "البدء" لبدء عملية إنشاء الخلفية. اضبط الخلفية على النحو التالي:
  2. اختر منطقة. بالنسبة إلى تطبيق حقيقي، يمكنك اختيار المنطقة الأقرب إلى المستخدمين.
  3. اتّبِع التعليمات الواردة في خطوة "استيراد مستودع GitHub" لربط مستودع GitHub الذي أنشأته سابقًا.
  4. اضبط إعدادات النشر:
    1. الاحتفاظ بالدليل الجذر على النحو التالي: /
    2. اضبط الفرع المنشور على main.
    3. تفعيل عمليات الطرح التلقائية
  5. أدخِل اسمًا لنظامك الأساسي friendlyeats-codelab.
  6. في "ربط تطبيق ويب على Firebase"، انقر على "إنشاء تطبيق ويب جديد على Firebase".
  7. انقر على "إنهاء ونشر". بعد لحظات، سيتم نقلك إلى صفحة جديدة يمكنك من خلالها الاطّلاع على حالة الخلفية الجديدة لخدمة "استضافة التطبيقات".
  8. بعد اكتمال عملية الطرح، انقر على نطاقك المجاني ضمن "النطاقات". قد يستغرق الأمر بضع دقائق حتى يبدأ العمل بسبب نشر نظام أسماء النطاقات.
  9. عذرًا. عند تحميل الصفحة، ستظهر لك رسالة خطأ مفادها "خطأ في التطبيق: حدث استثناء من جهة الخادم (اطّلِع على سجلات الخادم للحصول على مزيد من المعلومات)".
  10. في وحدة تحكّم Firebase، اطّلِع على علامة التبويب "السجلّات" في الخلفية في "استضافة التطبيقات". سيظهر لك سجلّ "خطأ: لم يتم تنفيذ الإجراء". سنصلح ذلك في الخطوة التالية عند إضافة المصادقة.

لقد نجحت في نشر تطبيق الويب الأوّلي. في كل مرة تُرسِل فيها مشاركة جديدة إلى فرع main في مستودع GitHub، سترى بدء عملية إنشاء وطرح جديدة في وحدة تحكّم Firebase، وسيتم تعديل موقعك الإلكتروني تلقائيًا بعد اكتمال عملية الطرح.

6- إضافة مصادقة إلى تطبيق الويب

في هذا القسم، يمكنك إضافة مصادقة إلى تطبيق الويب حتى تتمكّن من تسجيل الدخول إليه.

إضافة نطاق مفوَّض

لن تقبل Firebase Authentication طلبات تسجيل الدخول إلا من النطاقات التي تسمح بها. سنضيف هنا نطاق الخلفية في "استضافة التطبيقات" إلى قائمة النطاقات الموافَق عليها في مشروعك.

  1. انسخ نطاق الخلفية لاستضافة التطبيقات من صفحة "نظرة عامة" في "استضافة التطبيقات".
  2. انتقِل إلى علامة التبويب إعدادات التوثيق واختَر النطاقات المعتمَدة.
  3. انقر على الزر إضافة نطاق.
  4. أدخِل نطاق الخلفية لاستضافة التطبيقات.
  5. انقر على إضافة.

تنفيذ وظيفتَي تسجيل الدخول وتسجيل الخروج

  1. في ملف src/lib/firebase/auth.js، استبدِل الدوالّ onAuthStateChanged وonIdTokenChanged وsignInWithGoogle وsignOut بالرمز التالي:
export function onAuthStateChanged(cb) {
  return _onAuthStateChanged(auth, cb);
}

export function onIdTokenChanged(cb) {
  return _onIdTokenChanged(auth, cb);
}

export async function signInWithGoogle() {
  const provider = new GoogleAuthProvider();

  try {
    await signInWithPopup(auth, provider);
  } catch (error) {
    console.error("Error signing in with Google", error);
  }
}

export async function signOut() {
  try {
    return auth.signOut();
  } catch (error) {
    console.error("Error signing out with Google", error);
  }
}

تستخدِم هذه الرمز البرمجي واجهات برمجة تطبيقات Firebase التالية:

Firebase API

الوصف

auth.onAuthStateChanged

تُضيف هذه السمة مراقبًا للتغييرات في حالة تسجيل دخول المستخدم.

auth.onIdTokenChanged

تُضيف مراقبًا للتغييرات في الرمز المميّز لتعريف المستخدم.

GoogleAuthProvider

لإنشاء مثيل لموفِّر مصادقة Google

signInWithPopup

يبدأ مسار مصادقة مستندًا إلى مربّع حوار.

auth.signOut

تسجيل خروج المستخدم

في ملف src/components/Header.jsx، تستدعي التعليمات البرمجية دالتَي signInWithGoogle وsignOut.

إرسال حالة المصادقة إلى الخادم

لنقل حالة المصادقة إلى الخادم، سنستخدم ملفات تعريف الارتباط. عندما تتغيّر حالة المصادقة في العميل، سنعدِّل ملف تعريف الارتباط __session.

في src/components/Header.jsx، استبدِل الدالة useUserSession بالرمز البرمجي التالي:

function useUserSession(initialUser) {
  useEffect(() => {
    return onIdTokenChanged(async (user) => {
      if (user) {
        const idToken = await user.getIdToken();
        await setCookie("__session", idToken);
      } else {
        await deleteCookie("__session");
      }
      if (initialUser?.uid === user?.uid) {
        return;
      }
      window.location.reload();
    });
  }, [initialUser]);

  return initialUser;
}

قراءة حالة المصادقة على الخادم

سنستخدم FirebaseServerApp لعكس حالة مصادقة العميل على الخادم.

افتح src/lib/firebase/serverApp.js واستبدِل دالة getAuthenticatedAppForUser:

export async function getAuthenticatedAppForUser() {
  const authIdToken = (await cookies()).get("__session")?.value;

  // Firebase Server App is a new feature in the JS SDK that allows you to
  // instantiate the SDK with credentials retrieved from the client & has
  // other affordances for use in server environments.
  const firebaseServerApp = initializeServerApp(
    // https://github.com/firebase/firebase-js-sdk/issues/8863#issuecomment-2751401913
    initializeApp(),
    {
      authIdToken,
    }
  );

  const auth = getAuth(firebaseServerApp);
  await auth.authStateReady();

  return { firebaseServerApp, currentUser: auth.currentUser };
}

التحقّق من التغييرات

يعرض تنسيق الجذر في ملف src/app/layout.js العنوان ويُدخل المستخدم، إذا كان متاحًا، كعنصر عرض.

<Header initialUser={currentUser?.toJSON()} />

وهذا يعني أنّ المكوّن <Header> يعرض بيانات المستخدم، إذا كانت متاحة، أثناء وقت تشغيل الخادم. إذا كانت هناك أي تعديلات على المصادقة أثناء دورة حياة الصفحة بعد تحميل الصفحة الأولي، يعالجها معالِج onAuthStateChanged.

حان الآن وقت طرح إصدار جديد والتحقّق من ما أنشأته.

  1. أنشئ عملية إرسال مع رسالة الإرسال "إضافة مصادقة" وأرسِلها إلى مستودع GitHub.
  2. افتح صفحة "استضافة التطبيقات" في وحدة تحكُّم Firebase وانتظِر اكتمال عملية الطرح الجديدة.
  3. تحقَّق من سلوك المصادقة الجديد:
    1. في المتصفّح، أعِد تحميل تطبيق الويب. سيظهر اسمك المعروض في العنوان.
    2. سجِّل الخروج ثم سجِّل الدخول مرة أخرى. يمكنك تكرار هذه الخطوة مع مستخدمين مختلفين.
    3. اختياري: انقر بزر الماوس الأيمن على تطبيق الويب، واختَر عرض مصدر الصفحة، وابحث عن الاسم المعروض. يظهر في مصدر HTML الأوّلي الذي يعرضه الخادم.

7. عرض معلومات المطعم

يتضمّن تطبيق الويب بيانات وهمية للمطاعم والمراجعات.

إضافة مطعم واحد أو أكثر

لإدراج بيانات وهمية للمطاعم في قاعدة بيانات Cloud Firestore على الجهاز، اتّبِع الخطوات التالية:

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

عرض قائمة المطاعم

تحتوي قاعدة بيانات Cloud Firestore الآن على المطاعم التي يمكن لتطبيق الويب Next.js عرضها.

لتحديد رمز استرجاع البيانات، اتّبِع الخطوات التالية:

  1. في ملف src/app/page.js، ابحث عن مكوّن خادم <Home />، وراجِع طلب الدالة getRestaurants التي تسترجع قائمة بالمطاعم في وقت تشغيل الخادم. يمكنك تنفيذ الدالة getRestaurants في الخطوات التالية.
  2. في ملف src/lib/firebase/firestore.js، استبدِل الدالتَين applyQueryFilters وgetRestaurants بالرمز البرمجي التالي:
function applyQueryFilters(q, { category, city, price, sort }) {
  if (category) {
    q = query(q, where("category", "==", category));
  }
  if (city) {
    q = query(q, where("city", "==", city));
  }
  if (price) {
    q = query(q, where("price", "==", price.length));
  }
  if (sort === "Rating" || !sort) {
    q = query(q, orderBy("avgRating", "desc"));
  } else if (sort === "Review") {
    q = query(q, orderBy("numRatings", "desc"));
  }
  return q;
}

export async function getRestaurants(db = db, filters = {}) {
  let q = query(collection(db, "restaurants"));

  q = applyQueryFilters(q, filters);
  const results = await getDocs(q);
  return results.docs.map((doc) => {
    return {
      id: doc.id,
      ...doc.data(),
      // Only plain objects can be passed to Client Components from Server Components
      timestamp: doc.data().timestamp.toDate(),
    };
  });
}
  1. أنشئ عملية إرسال مع رسالة الإرسال "قراءة قائمة المطاعم من Firestore" وادفعها إلى مستودع GitHub.
  2. افتح صفحة "استضافة التطبيقات" في وحدة تحكُّم Firebase وانتظِر اكتمال عملية الطرح الجديدة.
  3. في تطبيق الويب، أعِد تحميل الصفحة. تظهر صور المطاعم كمربّعات على الصفحة.

التأكّد من تحميل بيانات المطاعم في وقت تشغيل الخادم

باستخدام إطار عمل Next.js، قد لا يكون واضحًا متى يتم تحميل البيانات في وقت تشغيل الخادم أو وقت التشغيل من جهة العميل.

للتأكّد من تحميل بيانات المطاعم في وقت تشغيل الخادم، اتّبِع الخطوات التالية:

  1. في تطبيق الويب، افتح "أدوات مطوّري البرامج" وأوقِف JavaScript.

إيقاف JavaScript في &quot;أدوات مطوّري البرامج&quot;

  1. يُرجى إعادة تحميل تطبيق الويب، وسيستمر تحميل بيانات المطاعم. يتم عرض معلومات المطعم في ردّ الخادم. عند تفعيل JavaScript، تتم إعادة تحميل معلومات المطعم من خلال رمز JavaScript من جهة العميل.
  2. في "أدوات مطوّري البرامج"، أعِد تفعيل JavaScript.

الاستماع إلى آخر الأخبار حول المطاعم باستخدام مستمعي لقطات Cloud Firestore

في القسم السابق، اطّلعت على كيفية تحميل المجموعة الأولية من المطاعم من ملف src/app/page.js. ملف src/app/page.js هو مكوّن خادم ويتم عرضه على الخادم، بما في ذلك رمز جلب البيانات في Firebase.

ملف src/components/RestaurantListings.jsx هو مكوّن عميل ويمكن ضبطه لإعادة ترطيب الترميز الذي يعرضه الخادم.

لضبط ملف src/components/RestaurantListings.jsx لتنشيط الترميز الذي يعرضه الخادم، اتّبِع الخطوات التالية:

  1. في ملف src/components/RestaurantListings.jsx، راقِب الرمز البرمجي التالي الذي سبق أن كتبناه نيابةً عنك:
useEffect(() => {
    return getRestaurantsSnapshot((data) => {
      setRestaurants(data);
    }, filters);
  }, [filters]);

تستدعي هذه التعليمة البرمجية الدالة getRestaurantsSnapshot()، وهي مشابهة للدالة getRestaurants() التي نفّذتها في خطوة سابقة. ومع ذلك، توفّر دالة اللقطة هذه آلية ردّ اتصال حتى يتمّ استدعاء ردّ الاتصال في كلّ مرّة يتمّ فيها إجراء تغيير على مجموعة المطاعم.

  1. في ملف src/lib/firebase/firestore.js، استبدِل الدالة getRestaurantsSnapshot() بالرمز البرمجي التالي:
export function getRestaurantsSnapshot(cb, filters = {}) {
  if (typeof cb !== "function") {
    console.log("Error: The callback parameter is not a function");
    return;
  }

  let q = query(collection(db, "restaurants"));
  q = applyQueryFilters(q, filters);

  return onSnapshot(q, (querySnapshot) => {
    const results = querySnapshot.docs.map((doc) => {
      return {
        id: doc.id,
        ...doc.data(),
        // Only plain objects can be passed to Client Components from Server Components
        timestamp: doc.data().timestamp.toDate(),
      };
    });

    cb(results);
  });
}

تظهر التغييرات التي يتم إجراؤها من خلال صفحة قاعدة بيانات Firestore الآن في تطبيق الويب في الوقت الفعلي.

  1. أنشئ عملية إرسال مع رسالة الإرسال "الاستماع إلى آخر الأخبار حول المطاعم في الوقت الفعلي" وادفعها إلى مستودع GitHub.
  2. افتح صفحة "استضافة التطبيقات" في وحدة تحكُّم Firebase وانتظِر اكتمال عملية الطرح الجديدة.
  3. في تطبيق الويب، انقر على 27ca5d1e8ed8adfe.png > إضافة نماذج من المطاعم. في حال تنفيذ وظيفة اللقطة بشكل صحيح، ستظهر المطاعم في الوقت الفعلي بدون إعادة تحميل الصفحة.

8. حفظ المراجعات التي يرسلها المستخدمون من تطبيق الويب

  1. في ملف src/lib/firebase/firestore.js، استبدِل الدالة updateWithRating() بالرمز البرمجي التالي:
const updateWithRating = async (
  transaction,
  docRef,
  newRatingDocument,
  review
) => {
  const restaurant = await transaction.get(docRef);
  const data = restaurant.data();
  const newNumRatings = data?.numRatings ? data.numRatings + 1 : 1;
  const newSumRating = (data?.sumRating || 0) + Number(review.rating);
  const newAverage = newSumRating / newNumRatings;

  transaction.update(docRef, {
    numRatings: newNumRatings,
    sumRating: newSumRating,
    avgRating: newAverage,
  });

  transaction.set(newRatingDocument, {
    ...review,
    timestamp: Timestamp.fromDate(new Date()),
  });
};

تُدرج هذه القيمة مستندًا جديدًا في Firestore يمثّل المراجعة الجديدة. تعدّل التعليمة البرمجية أيضًا مستند Firestore الحالي الذي يمثّل المطعم بأرقام معدَّلة لعدد التقييمات ومتوسّط التقييم المحسوب.

  1. استبدِل الدالة addReviewToRestaurant() بالرمز البرمجي التالي:
export async function addReviewToRestaurant(db, restaurantId, review) {
	if (!restaurantId) {
		throw new Error("No restaurant ID has been provided.");
	}

	if (!review) {
		throw new Error("A valid review has not been provided.");
	}

	try {
		const docRef = doc(collection(db, "restaurants"), restaurantId);
		const newRatingDocument = doc(
			collection(db, `restaurants/${restaurantId}/ratings`)
		);

		// corrected line
		await runTransaction(db, transaction =>
			updateWithRating(transaction, docRef, newRatingDocument, review)
		);
	} catch (error) {
		console.error(
			"There was an error adding the rating to the restaurant",
			error
		);
		throw error;
	}
}

تنفيذ إجراء خادم Next.js

يوفّر إجراء Next.js Server واجهة برمجة تطبيقات ملائمة للوصول إلى بيانات النموذج، مثل data.get("text") للحصول على القيمة النصية من الحمولة المرسَلة لإرسال النموذج.

لاستخدام إجراء خادم Next.js لمعالجة عملية إرسال نموذج المراجعة، اتّبِع الخطوات التالية:

  1. في ملف src/components/ReviewDialog.jsx، ابحث عن السمة action في العنصر <form>.
<form action={handleReviewFormSubmission}>

تشير قيمة السمة action إلى دالة تنفّذها في الخطوة التالية.

  1. في ملف src/app/actions.js، استبدِل الدالة handleReviewFormSubmission() بالرمز البرمجي التالي:
// This is a next.js server action, which is an alpha feature, so
// use with caution.
// https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions
export async function handleReviewFormSubmission(data) {
        const { app } = await getAuthenticatedAppForUser();
        const db = getFirestore(app);

        await addReviewToRestaurant(db, data.get("restaurantId"), {
                text: data.get("text"),
                rating: data.get("rating"),

                // This came from a hidden form field.
                userId: data.get("userId"),
        });
}

إضافة مراجعات لمطعم

لقد نفّذت ميزة إرسال المراجعات، لذا يمكنك الآن التأكّد من إدراج مراجعاتك في Cloud Firestore بشكل صحيح.

لإضافة مراجعة والتأكّد من إدراجها في Cloud Firestore، اتّبِع الخطوات التالية:

  1. أنشئ عملية إرسال مع رسالة الإرسال "السماح للمستخدمين بإرسال مراجعات المطاعم" وادفعها إلى مستودع GitHub.
  2. افتح صفحة "استضافة التطبيقات" في وحدة تحكُّم Firebase وانتظِر اكتمال عملية الطرح الجديدة.
  3. أعِد تحميل تطبيق الويب، واختَر مطعمًا من الصفحة الرئيسية.
  4. في صفحة المطعم، انقر على 3e19beef78bb0d0e.png.
  5. اختَر تقييمًا بالنجوم.
  6. كتابة مراجعة
  7. انقر على إرسال. ستظهر مراجعتك في أعلى قائمة المراجعات.
  8. في Cloud Firestore، ابحث في لوحة إضافة مستند عن مستند المطعم الذي راجعته واختَره.
  9. في لوحة بدء عملية جمع البيانات، اختَر التقييمات.
  10. في لوحة إضافة مستند، ابحث عن المستند الذي تريد مراجعته للتأكّد من أنّه تم إدراجه على النحو المتوقّع.

المستندات في محاكي Firestore

9. حفظ الملفات التي حمّلها المستخدمون من تطبيق الويب

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

لحفظ الملفات التي حمّلها المستخدمون من تطبيق الويب، اتّبِع الخطوات التالية:

  1. في ملف src/components/Restaurant.jsx، راقِب الرمز البرمجي الذي يتم تنفيذه عندما يحمّل المستخدم ملفًا:
async function handleRestaurantImage(target) {
  const image = target.files ? target.files[0] : null;
  if (!image) {
    return;
  }

  const imageURL = await updateRestaurantImage(id, image);
  setRestaurantDetails({ ...restaurantDetails, photo: imageURL });
}

لا يلزم إجراء أي تغييرات على هذه الدالة، ولكن يمكنك تنفيذ سلوك الدالة updateRestaurantImage() في الخطوات التالية.

  1. في ملف src/lib/firebase/storage.js، استبدِل الدالتَين updateRestaurantImage() وuploadImage() بالرمز البرمجي التالي:
export async function updateRestaurantImage(restaurantId, image) {
  try {
    if (!restaurantId) {
      throw new Error("No restaurant ID has been provided.");
    }

    if (!image || !image.name) {
      throw new Error("A valid image has not been provided.");
    }

    const publicImageUrl = await uploadImage(restaurantId, image);
    await updateRestaurantImageReference(restaurantId, publicImageUrl);

    return publicImageUrl;
  } catch (error) {
    console.error("Error processing request:", error);
  }
}

async function uploadImage(restaurantId, image) {
  const filePath = `images/${restaurantId}/${image.name}`;
  const newImageRef = ref(storage, filePath);
  await uploadBytesResumable(newImageRef, image);

  return await getDownloadURL(newImageRef);
}

سبق أن تم تنفيذ الدالة updateRestaurantImageReference() نيابةً عنك. تعدِّل هذه الدالة مستند مطعم حاليًا في Cloud Firestore باستخدام عنوان URL معدَّل للصورة.

التحقّق من وظيفة تحميل الصور

للتأكّد من تحميل الصورة على النحو المتوقّع، اتّبِع الخطوات التالية:

  1. أنشئ عملية إرسال مع رسالة الإرسال "السماح للمستخدمين بتغيير صورة كل مطعم" وادفعها إلى مستودع GitHub.
  2. افتح صفحة "استضافة التطبيقات" في وحدة تحكُّم Firebase وانتظِر اكتمال عملية الطرح الجديدة.
  3. في تطبيق الويب، تأكَّد من تسجيل الدخول واختَر مطعمًا.
  4. انقر على 7067eb41fea41ff0.png وحمِّل صورة من نظام الملفات. تغادر صورتك بيئتك المحلية ويتم تحميلها إلى Cloud Storage. تظهر الصورة فور تحميلها.
  5. انتقِل إلى مساحة التخزين السحابي في Firebase.
  6. انتقِل إلى المجلد الذي يمثّل المطعم. تتوفّر الصورة التي حمّلتها في المجلد.

6cf3f9e2303c931c.png

10. تلخيص مراجعات المطاعم باستخدام الذكاء الاصطناعي التوليدي

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

تخزين مفتاح Gemini API في Cloud Secret Manager

  1. لاستخدام Gemini API، ستحتاج إلى مفتاح واجهة برمجة التطبيقات. انتقِل إلى Google AI Studio وانقر على "إنشاء مفتاح API".
  2. في حقل الإدخال "البحث في مشاريع Google Cloud"، اختَر مشروعك على Firebase. يستند كل مشروع على Firebase إلى مشروع على Google Cloud.
  3. يتم دمج ميزة "استضافة التطبيقات" مع Cloud Secret Manager للسماح لك بتخزين القيم الحساسة، مثل مفاتيح واجهة برمجة التطبيقات، بأمان:
    1. في وحدة طرفية، نفِّذ الأمر لإنشاء سر جديد:
    firebase apphosting:secrets:set GEMINI_API_KEY
    
    1. عندما يُطلب منك إدخال القيمة السرية، انسخ مفتاح Gemini API والصقه من Google AI Studio.
    2. عندما يُطلب منك تحديد ما إذا كان السر الجديد مخصّصًا للإصدار العلني أو الاختبار على الجهاز، اختَر "الإصدار العلني".
    3. عندما يُطلب منك منح إذن الوصول لكي يتمكّن حساب الخدمة في الخلفية من الوصول إلى السرّ، اختَر "نعم".
    4. عندما يُطلب منك تحديد ما إذا كان يجب إضافة الرمز السري الجديد إلى apphosting.yaml، أدخِل Y للموافقة.

يتم الآن تخزين مفتاح Gemini API بأمان في أداة "إدارة الأسرار" في السحابة الإلكترونية، ويمكن للخلفية في "استضافة التطبيقات" الوصول إليه.

تنفيذ مكوّن ملخّص المراجعات

  1. في src/components/Reviews/ReviewSummary.jsx، استبدِل الدالة GeminiSummary بالرمز البرمجي التالي:
    export async function GeminiSummary({ restaurantId }) {
      const { firebaseServerApp } = await getAuthenticatedAppForUser();
      const reviews = await getReviewsByRestaurantId(
        getFirestore(firebaseServerApp),
        restaurantId
      );
    
      const reviewSeparator = "@";
      const prompt = `
        Based on the following restaurant reviews, 
        where each review is separated by a '${reviewSeparator}' character, 
        create a one-sentence summary of what people think of the restaurant. 
    
        Here are the reviews: ${reviews.map((review) => review.text).join(reviewSeparator)}
      `;
    
      try {
        if (!process.env.GEMINI_API_KEY) {
          // Make sure GEMINI_API_KEY environment variable is set:
          // https://firebase.google.com/docs/genkit/get-started
          throw new Error(
            'GEMINI_API_KEY not set. Set it with "firebase apphosting:secrets:set GEMINI_API_KEY"'
          );
        }
    
        // Configure a Genkit instance.
        const ai = genkit({
          plugins: [googleAI()],
          model: gemini20Flash, // set default model
        });
        const { text } = await ai.generate(prompt);
    
        return (
          <div className="restaurant__review_summary">
            <p>{text}</p>
            <p> Summarized with Gemini</p>
          </div>
        );
      } catch (e) {
        console.error(e);
        return <p>Error summarizing reviews.</p>;
      }
    }
    
  2. أنشئ عملية إرسال مع رسالة الإرسال "استخدام الذكاء الاصطناعي لتلخيص المراجعات" وادفعها إلى مستودع GitHub.
  3. افتح صفحة "استضافة التطبيقات" في وحدة تحكُّم Firebase وانتظِر اكتمال عملية الطرح الجديدة.
  4. افتح صفحة مطعم. من المفترض أن يظهر في أعلى الصفحة ملخّص عبارة واحدة لجميع المراجعات الواردة فيها.
  5. أضِف مراجعة جديدة وأعِد تحميل الصفحة. من المفترض أن يظهر لك تغيير الملخّص.

11. الخاتمة

تهانينا! لقد تعرّفت على كيفية استخدام Firebase لإضافة ميزات ووظائف إلى تطبيق Next.js. ولقد استخدمت على وجه التحديد ما يلي:

مزيد من المعلومات