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

1- قبل البدء

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

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

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

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

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

  • حساب GitHub
  • الإلمام بـ Next.js وJavaScript

المعلومات التي ستطّلع عليها

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

المتطلبات

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

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

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

إنشاء مستودع جيت هب

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

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

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

firebase --version

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

npm install -g firebase-tools@latest

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

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

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

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

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

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

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

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

لاستخدام "استضافة التطبيقات"، يجب أن يكون مشروعك على Firebase ضِمن خطة أسعار Blaze، ما يعني أنّه مرتبط بحساب فوترة Cloud.

لترقية مشروعك إلى خطة 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. يمكنك استخدام الموقع الجغرافي التلقائي أو اختيار موقع جغرافي من اختيارك.
    بالنسبة إلى التطبيق الحقيقي، يجب اختيار موقع جغرافي قريب من المستخدمين. يُرجى العلم أنّه لا يمكن تغيير هذا الموقع لاحقًا، وسيكون أيضًا موقع الحزمة التلقائية في Cloud Storage (الخطوة التالية).
  4. انقر على تم.

إعداد Cloud Storage لمنصة Firebase

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

4- مراجعة قاعدة الرموز المخصّصة للمبتدئين

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

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

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

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

الوصف

src/components

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

src/lib

وظائف الأدوات المساعدة التي لا ترتبط بالضرورة بـ React أو Next.js

src/lib/firebase

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

public

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

src/app

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

src/app/restaurant

معالج مسار واجهة برمجة التطبيقات

package.json وpackage-lock.json

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

next.config.js

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

jsconfig.json

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

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

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

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

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

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

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

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

يتضمّن الدليل src/lib/firebase كل رموز واجهة برمجة التطبيقات لمنصة Firebase. بعد ذلك، تستورد مكوّنات React الفردية الدوال الملتفة من دليل src/lib/firebase، بدلاً من استيراد وظائف Firebase مباشرةً.

البيانات الوهمية

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

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

في هذا القسم، ستقوم بإعداد الواجهة الخلفية لـ "استضافة التطبيقات" لمشاهدة فرع في مستودع git الخاص بك.

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

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

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

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

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

  1. في وحدة تحكُّم Firebase، انتقِل إلى إعدادات المشروع.
  2. في جزء إعداد حزمة SDK وإعدادها، انقر على "إضافة تطبيق" وانقر على رمز أقواس الرمز لتسجيل تطبيق ويب جديد.
  3. في نهاية عملية إنشاء تطبيق الويب، انسخ المتغيّر firebaseConfig وانسخ خصائصه وقيمه.
  4. افتح ملف apphosting.yaml في أداة تعديل الرموز واملأ قيم متغيِّرات البيئة بقيم الضبط من "وحدة تحكُّم Firebase".
  5. في الملف، استبدِل السمات الحالية بالخصائص التي نسختها.
  6. احفظ الملف.

إنشاء خلفية

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

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

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

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

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

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

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

  1. في الملف src/lib/firebase/auth.js، استبدِل الدوال onAuthStateChanged وsignInWithGoogle وsignOut بالرمز التالي:
export function onAuthStateChanged(cb) {
	return _onAuthStateChanged(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

الوصف

GoogleAuthProvider

ينشئ مثيلاً لموفِّر مصادقة Google.

signInWithPopup

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

auth.signOut

يسجِّل خروج المستخدم.

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

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

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

من أجل تمرير حالة المصادقة إلى الخادم، سنستخدم عامل خدمة. استبدِل الدالتَين fetchWithFirebaseHeaders وgetAuthIdToken بالرمز التالي:

async function fetchWithFirebaseHeaders(request) {
  const app = initializeApp(firebaseConfig);
  const auth = getAuth(app);
  const installations = getInstallations(app);
  const headers = new Headers(request.headers);
  const [authIdToken, installationToken] = await Promise.all([
    getAuthIdToken(auth),
    getToken(installations),
  ]);
  headers.append("Firebase-Instance-ID-Token", installationToken);
  if (authIdToken) headers.append("Authorization", `Bearer ${authIdToken}`);
  const newRequest = new Request(request, { headers });
  return await fetch(newRequest);
}

async function getAuthIdToken(auth) {
  await auth.authStateReady();
  if (!auth.currentUser) return;
  return await getIdToken(auth.currentUser);
}

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

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

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

export async function getAuthenticatedAppForUser() {
  const idToken = headers().get("Authorization")?.split("Bearer ")[1];
  console.log('firebaseConfig', JSON.stringify(firebaseConfig));
  const firebaseServerApp = initializeServerApp(
    firebaseConfig,
    idToken
      ? {
          authIdToken: idToken,
        }
      : {}
  );

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

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

الاشتراك في خدمة تلقّي تغييرات المصادقة

للاشتراك في تغييرات المصادقة، اتبع الخطوات التالية:

  1. انتقِل إلى ملف src/components/Header.jsx.
  2. استبدل الدالة useUserSession بالرمز التالي:
function useUserSession(initialUser) {
	// The initialUser comes from the server via a server component
	const [user, setUser] = useState(initialUser);
	const router = useRouter();

	// Register the service worker that sends auth state back to server
	// The service worker is built with npm run build-service-worker
	useEffect(() => {
		if ("serviceWorker" in navigator) {
			const serializedFirebaseConfig = encodeURIComponent(JSON.stringify(firebaseConfig));
			const serviceWorkerUrl = `/auth-service-worker.js?firebaseConfig=${serializedFirebaseConfig}`
		
		  navigator.serviceWorker
			.register(serviceWorkerUrl)
			.then((registration) => console.log("scope is: ", registration.scope));
		}
	  }, []);

	useEffect(() => {
		const unsubscribe = onAuthStateChanged((authUser) => {
			setUser(authUser)
		})

		return () => unsubscribe()
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

	useEffect(() => {
		onAuthStateChanged((authUser) => {
			if (user === undefined) return

			// refresh when user changed to ease testing
			if (user?.email !== authUser?.email) {
				router.refresh()
			}
		})
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [user])

	return user;
}

يستخدِم هذا الرمز عنصر الجذب حالة التفاعل لتعديل المستخدم عندما تشير الدالة onAuthStateChanged إلى حدوث تغيير في حالة المصادقة.

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

يعرض تنسيق الجذر في ملف src/app/layout.js العنوان والتمريرات لدى المستخدم، في حال توفّره، كإجراء إرشادي.

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

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

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

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

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

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

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

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

  1. في تطبيق الويب، اختَر 2cf67d488d8e6332.png > إضافة نماذج مطاعم.
  2. في وحدة تحكُّم Firebase في صفحة قاعدة بيانات 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" وادفعها إلى مستودعك بجيت هب.
  2. افتح صفحة استضافة التطبيقات في "وحدة تحكُّم Firebase" وانتظِر إلى أن تكتمل عملية الطرح الجديدة.
  3. في تطبيق الويب، أعِد تحميل الصفحة. تظهر صور المطاعم كمربّعات على الصفحة.

التحقق من تحميل قوائم المطعم في وقت تشغيل الخادم

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

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

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

إيقاف JavaScipt في &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(() => {
        const unsubscribe = getRestaurantsSnapshot(data => {
                setRestaurants(data);
        }, filters);

        return () => {
                unsubscribe();
        };
}, [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);

	const unsubscribe = 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);
	});

	return unsubscribe;
}

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

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

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

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

في هذا القسم، يمكنك إضافة وظائف بحيث يمكنك استبدال الصورة المرتبطة بمطعم عند تسجيل الدخول. يمكنك تحميل الصورة إلى مساحة التخزين في Firebase، وتعديل عنوان 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);
        setRestaurant({ ...restaurant, 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. أنشئ التزامًا باستخدام رسالة الالتزام "السماح للمستخدمين بتغيير صورة كل مطعم"، ثم أرسِلها إلى مستودعك في جيت هب.
  2. افتح صفحة استضافة التطبيقات في "وحدة تحكُّم Firebase" وانتظِر إلى أن تكتمل عملية الطرح الجديدة.
  3. في تطبيق الويب، تحقَّق من تسجيل الدخول واختَر مطعمًا.
  4. انقر على 7067eb41fea41ff0.png وحمِّل صورة من نظام الملفات. تترك الصورة بيئتك المحلية ويتم تحميلها على Cloud Storage. تظهر الصورة مباشرةً بعد تحميلها.
  5. انتقِل إلى Cloud Storage لمنصّة Firebase.
  6. انتقل إلى المجلد الذي يمثل المطعم. تتوفّر الصورة التي حمّلتها في المجلد.

6cf3f9e2303c931c.png

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

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

تخزين مفتاح واجهة برمجة تطبيقات Gemini في Cloud Secret Manager

  1. لاستخدام Gemini API، يجب استخدام مفتاح واجهة برمجة التطبيقات. أنشِئ مفتاحًا في "استوديو Google AI Studio".
  2. تتكامل ميزة App Hosting مع Cloud Secret Manager للسماح لك بتخزين القيم الحساسة، مثل مفاتيح واجهة برمجة التطبيقات بشكل آمن:
    1. في الوحدة الطرفية، شغِّل الأمر لإنشاء مفتاح سرّي جديد:
    firebase apphosting:secrets:set gemini-api-key
    
    1. عندما يُطلب منك إدخال القيمة السرية، انسخ مفتاح واجهة برمجة تطبيقات Gemini وألصِقه من Google AI Studio.
    2. عند سؤالك عما إذا كان يجب إضافة المفتاح السرّي الجديد إلى apphosting.yaml، أدخِل Y لقبوله.

يتم الآن تخزين مفتاح واجهة برمجة تطبيقات Gemini بأمان في مدير Cloud Secret مع إمكانية الوصول إليه من خلال واجهة App Hosting الخلفية.

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

  1. في src/components/Reviews/ReviewSummary.jsx، استبدِل الدالة GeminiSummary بالرمز التالي:
    export async function GeminiSummary({ restaurantId }) {
        const { firebaseServerApp } = await getAuthenticatedAppForUser();
        const reviews = await getReviewsByRestaurantId(
            getFirestore(firebaseServerApp),
            restaurantId
        );
    
        const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
        const model = genAI.getGenerativeModel({ model: "gemini-pro"});
    
        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 {
            const result = await model.generateContent(prompt);
            const response = await result.response;
            const text = response.text();
    
            return (
                <div className="restaurant__review_summary">
                    <p>{text}</p>
                    <p>✨ Summarized with Gemini</p>
                </div>
            );
        } catch (e) {
            console.error(e);
            return <p>Error contacting Gemini</p>;
        }
    }
    
  2. أنشئ التزامًا من خلال رسالة "الالتزام" "استخدِم الذكاء الاصطناعي لتلخيص المراجعات" وانشرها في مستودعك على جيت هب.
  3. افتح صفحة استضافة التطبيقات في "وحدة تحكُّم Firebase" وانتظِر إلى أن تكتمل عملية الطرح الجديدة.
  4. افتح صفحة خاصة بالمطعم. في أعلى الصفحة، من المفترض أن يظهر لك ملخّص من جملة واحدة لكل المراجعات الواردة في الصفحة.
  5. أضِف مراجعة جديدة وأعِد تحميل الصفحة. من المفترض أن يظهر لك التغيير في الملخّص.

11- الخاتمة

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

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