1. قبل البدء

في هذا الدرس التطبيقي حول الترميز، ستدمج Firebase Data Connect مع قاعدة بيانات Cloud SQL لإنشاء تطبيق ويب لمراجعة الأفلام. يوضّح التطبيق المكتمل كيف يسهّل Firebase Data Connect عملية إنشاء تطبيقات تستند إلى SQL. يتضمّن هذا التطبيق الميزات التالية:
- المصادقة: نفِّذ مصادقة مخصّصة لطلبات البحث والتعديلات في تطبيقك، ما يضمن تفاعل المستخدمين المصرَّح لهم فقط مع بياناتك.
- مخطط GraphQL: يمكنك إنشاء بنى البيانات وإدارتها باستخدام مخطط GraphQL مرن ومصمّم خصيصًا لتلبية احتياجات تطبيق الويب الخاص بمراجعات الأفلام.
- استعلامات SQL وعمليات التعديل: يمكنك استرداد البيانات وتعديلها وإدارتها في Cloud SQL باستخدام الاستعلامات وعمليات التعديل المستندة إلى GraphQL.
- البحث المتقدّم مع مطابقة السلسلة الجزئية: استخدِم الفلاتر وخيارات البحث للعثور على الأفلام استنادًا إلى حقول مثل العنوان أو الوصف أو العلامات.
- (اختياري) دمج البحث المتّجه: أضِف وظيفة البحث عن المحتوى باستخدام البحث المتّجه في Firebase Data Connect لتقديم تجربة مستخدم غنية استنادًا إلى المدخلات والإعدادات المفضّلة.
المتطلبات الأساسية
ستحتاج إلى فهم أساسي للغة JavaScript.
ما ستتعلمه
- إعداد Firebase Data Connect باستخدام المحاكيات المحلية
- تصميم مخطط بيانات باستخدام Data Connect وGraphQL
- كتابة واختبار طلبات بحث وعمليات تغيير مختلفة لتطبيق مراجعات الأفلام
- تعرَّف على كيفية إنشاء حزمة تطوير البرامج (SDK) واستخدامها في التطبيق من خلال ميزة "ربط البيانات في Firebase".
- تفعيل المخطط وإدارة قاعدة البيانات بكفاءة
المتطلبات
- Git
- Visual Studio Code
- ثبِّت Node.js باستخدام nvm-windows (في نظام التشغيل Windows) أو nvm (في نظام التشغيل macOS أو Linux).
- إذا لم يسبق لك إنشاء مشروع على Firebase، أنشئ مشروعًا في وحدة تحكّم Firebase.
- (اختياري) لاستخدام "البحث المتّجه"، عليك ترقية مشروعك إلى خطة التسعير بنظام الدفع حسب الاستخدام Blaze
2. إعداد بيئة التطوير
ستساعدك هذه المرحلة من الدرس التطبيقي حول الترميز في إعداد البيئة لبدء إنشاء تطبيق مراجعة الأفلام باستخدام Firebase Data Connect.
- أنشئ نسخة طبق الأصل من مستودع المشروع وثبِّت التبعيات المطلوبة:
git clone https://github.com/firebaseextended/codelab-dataconnect-web cd codelab-dataconnect-web cd ./app && npm i npm run dev
- بعد تنفيذ هذه الأوامر، افتح http://localhost:5173 في المتصفّح للاطّلاع على تطبيق الويب الذي يتم تشغيله محليًا. سيكون هذا بمثابة الواجهة الأمامية لإنشاء تطبيق مراجعة الأفلام والتفاعل مع ميزاته.

- افتح مجلد
codelab-dataconnect-webالمستنسخ باستخدام Visual Studio Code. هذا هو المكان الذي ستحدّد فيه المخطط وتكتب طلبات البحث وتختبر وظائف التطبيق. - لاستخدام ميزات Data Connect، ثبِّت إضافة Firebase Data Connect في Visual Studio.
بدلاً من ذلك، يمكنك تثبيت الإضافة من Visual Studio Code Marketplace أو البحث عنها في VS Code.
- افتح مشروع Firebase جديدًا أو أنشئه في وحدة تحكّم Firebase.
- ربط مشروع Firebase بإضافة Firebase Data Connect VSCode في الإضافة، اتّبِع الخطوات التالية:
- انقر على الزر تسجيل الدخول.
- انقر على ربط مشروع Firebase واختَر مشروعك على Firebase.

- ابدأ محاكيات Firebase باستخدام إضافة Firebase Data Connect في VS Code:
انقر على بدء المحاكيات، ثم تأكَّد من أنّ المحاكيات تعمل في الوحدة الطرفية.
3- مراجعة قاعدة الرموز البرمجية الأولية
في هذا القسم، ستستكشف المجالات الرئيسية لقاعدة الرموز الأولية للتطبيق. على الرغم من أنّ التطبيق لا يتضمّن بعض الوظائف، إلا أنّه مفيد لفهم البنية العامة.
بنية المجلدات والملفات
تقدّم الأقسام الفرعية التالية نظرة عامة على بنية المجلدات والملفات في التطبيق.
دليل dataconnect/
يحتوي على إعدادات Firebase Data Connect والموصلات (التي تحدّد طلبات البحث والتعديلات) وملفات المخطط.
schema/schema.gql: تحدّد مخطط GraphQL-
connector/queries.gql: طلبات البحث المطلوبة في تطبيقك connector/mutations.gql: التغييرات المطلوبة في تطبيقك-
connector/connector.yaml: ملف الإعداد لإنشاء حزمة SDK
دليل app/src/
يحتوي على منطق التطبيق والتفاعل مع Firebase Data Connect.
firebase.ts: إعدادات الربط بتطبيق Firebase في مشروع Firebase.-
lib/dataconnect-sdk/: يحتوي على حزمة SDK التي تم إنشاؤها. يمكنك تعديل موقع إنشاء حزمة SDK في ملفconnector/connector.yamlوسيتم إنشاء حِزم SDK تلقائيًا في أي وقت تحدّد فيه طلب بحث أو تعديل.
4. تحديد مخطط لمراجعات الأفلام
في هذا القسم، ستحدّد بنية العلاقات بين الكيانات الرئيسية في تطبيق الأفلام في مخطط. يتم ربط الكيانات، مثل Movie وUser وActor وReview، بجداول قواعد البيانات، مع إنشاء العلاقات باستخدام Firebase Data Connect وتوجيهات مخطط GraphQL. بعد إعداد هذه الميزة، سيصبح تطبيقك جاهزًا للتعامل مع كل شيء، بدءًا من البحث عن الأفلام الأعلى تقييمًا والفلترة حسب النوع، وصولاً إلى السماح للمستخدمين بترك مراجعات أو وضع علامة على الأفلام المفضّلة أو استكشاف أفلام مشابهة أو العثور على أفلام مقترَحة استنادًا إلى إدخال نصي من خلال البحث المتّجه.
الكيانات والعلاقات الأساسية
يحتوي النوع Movie على تفاصيل أساسية، مثل العنوان والنوع والعلامات، التي يستخدمها التطبيق في عمليات البحث وملفات الأفلام. يتتبّع النوع User تفاعلات المستخدمين، مثل المراجعات وقوائم المفضّلة. Reviews ربط المستخدمين بالأفلام، ما يتيح للتطبيق عرض التقييمات والملاحظات التي ينشئها المستخدمون
تساهم العلاقات بين الأفلام والممثلين والمستخدمين في جعل التطبيق أكثر ديناميكية. يساعد جدول الربط MovieActor في عرض تفاصيل فريق التمثيل وأعمال الممثلين السينمائية. يتيح النوع FavoriteMovie للمستخدمين إضافة الأفلام إلى قائمة المفضّلة، ما يسمح للتطبيق بعرض قائمة مفضّلة مخصّصة وإبراز الأفلام الرائجة.
إعداد جدول Movie
يحدّد النوع Movie البنية الرئيسية لعنصر الفيلم، بما في ذلك الحقول مثل title وgenre وreleaseYear وrating.
انسخ مقتطف الرمز وألصِقه في ملف dataconnect/schema/schema.gql:
type Movie
@table {
id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
releaseYear: Int
genre: String
rating: Float
description: String
tags: [String]
}
الأفكار الرئيسية:
- id: معرّف فريد لكل فيلم، يتم إنشاؤه باستخدام
@default(expr: "uuidV4()").
إعداد جدول MovieMetadata
يُنشئ النوع MovieMetadata علاقة فردية مع النوع Movie. ويتضمّن بيانات إضافية، مثل مخرج الفيلم.
انسخ مقتطف الرمز وألصِقه في ملف dataconnect/schema/schema.gql:
type MovieMetadata
@table {
# @ref creates a field in the current table (MovieMetadata)
# It is a reference that holds the primary key of the referenced type
# In this case, @ref(fields: "movieId", references: "id") is implied
movie: Movie! @ref
# movieId: UUID <- this is created by the above @ref
director: String
}
الأفكار الرئيسية:
- فيلم! @ref: يشير إلى النوع
Movie، ما يؤدي إلى إنشاء علاقة مفتاح خارجي.
إعداد جدول Actor
انسخ مقتطف الرمز وألصِقه في ملف dataconnect/schema/schema.gql:
type Actor @table {
id: UUID!
imageUrl: String!
name: String! @col(name: "name", dataType: "varchar(30)")
}
يمثّل النوع Actor ممثلاً في قاعدة بيانات الأفلام، حيث يمكن أن يكون كل ممثل جزءًا من عدة أفلام، ما يشكّل علاقة متعددة إلى متعددة.
إعداد جدول MovieActor
انسخ مقتطف الرمز وألصِقه في ملف dataconnect/schema/schema.gql:
type MovieActor @table(key: ["movie", "actor"]) {
# @ref creates a field in the current table (MovieActor) that holds the primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie!
# movieId: UUID! <- this is created by the implied @ref, see: implicit.gql
actor: Actor!
# actorId: UUID! <- this is created by the implied @ref, see: implicit.gql
role: String! # "main" or "supporting"
}
الأفكار الرئيسية:
- الفيلم: يشير إلى نوع الفيلم، ويُنشئ ضمنيًا مفتاحًا خارجيًا movieId: UUID!.
- يشير actor: إلى نوع Actor، ويُنشئ ضمنيًا مفتاحًا خارجيًا actorId: UUID!.
- role: تحدّد هذه السمة دور الممثل في الفيلم (مثلاً، "دور رئيسي" أو "دور ثانوي").
إعداد جدول User
يحدّد النوع User كيان مستخدم يتفاعل مع الأفلام من خلال ترك مراجعات أو إضافة الأفلام إلى قائمة المفضّلة.
انسخ مقتطف الرمز وألصِقه في ملف dataconnect/schema/schema.gql:
type User
@table {
id: String! @col
username: String! @col(dataType: "varchar(50)")
# The following are generated from the @ref in the Review table
# reviews_on_user
# movies_via_Review
}
إعداد جدول FavoriteMovie
النوع FavoriteMovie هو جدول ربط يتعامل مع علاقات متعددة إلى متعددة بين المستخدمين وأفلامهم المفضّلة. يربط كل جدول User بـ Movie.
انسخ مقتطف الرمز وألصِقه في ملف dataconnect/schema/schema.gql:
type FavoriteMovie
@table(name: "FavoriteMovies", singular: "favorite_movie", plural: "favorite_movies", key: ["user", "movie"]) {
# @ref is implicit
user: User!
movie: Movie!
}
الأفكار الرئيسية:
- فيلم: يشير إلى نوع الفيلم، وينشئ ضمنيًا مفتاحًا خارجيًا
movieId: UUID!. - user: يشير إلى نوع المستخدم، وينشئ ضمنيًا مفتاحًا خارجيًا
userId: UUID!.
إعداد جدول Review
يمثّل النوع Review كيان المراجعة ويربط النوعَين User وMovie في علاقة متعددة إلى متعددة (يمكن للمستخدم الواحد ترك العديد من المراجعات، ويمكن أن يتضمّن كل فيلم العديد من المراجعات).
انسخ مقتطف الرمز وألصِقه في ملف dataconnect/schema/schema.gql:
type Review @table(name: "Reviews", key: ["movie", "user"]) {
id: UUID! @default(expr: "uuidV4()")
user: User!
movie: Movie!
rating: Int
reviewText: String
reviewDate: Date! @default(expr: "request.time")
}
الأفكار الرئيسية:
- المستخدم: يشير إلى المستخدم الذي أضاف المراجعة.
- movie: تشير إلى الفيلم الذي تتم مراجعته.
- reviewDate: يتم ضبطها تلقائيًا على الوقت الذي تم فيه إنشاء المراجعة باستخدام
@default(expr: "request.time").
الحقول والقيم التلقائية التي يتم إنشاؤها تلقائيًا
يستخدم المخطط تعابير مثل @default(expr: "uuidV4()") لإنشاء معرّفات فريدة وطوابع زمنية تلقائيًا. على سبيل المثال، يتم تلقائيًا ملء الحقل id في النوعَين Movie وReview بمعرّف فريد عالمي (UUID) عند إنشاء سجلّ جديد.
بعد تحديد المخطط، أصبح تطبيق الأفلام يملك أساسًا متينًا لبنية البيانات والعلاقات.
5- استرداد أهم وأحدث الأفلام

في هذا القسم، ستُدرج بيانات وهمية للأفلام في المحاكيات المحلية، ثم ستنفّذ أدوات الربط (طلبات البحث) ورمز TypeScript لاستدعاء أدوات الربط هذه في تطبيق الويب. وبنهاية هذا البرنامج التعليمي، سيتمكّن تطبيقك من استرداد وعرض الأفلام الأعلى تقييمًا والأحدث مباشرةً من قاعدة البيانات.
إدراج بيانات وهمية للأفلام والممثلين والمراجعات
- في VSCode، افتح
dataconnect/moviedata_insert.gql. تأكَّد من أنّ المحاكيات في إضافة Firebase Data Connect تعمل. - من المفترض أن يظهر لك الزر تشغيل (محلي) في أعلى الملف. انقر على هذا الزر لإدراج بيانات الأفلام التجريبية في قاعدة البيانات.

- راجِع نافذة تنفيذ ربط البيانات للتأكّد من أنّه تمت إضافة البيانات بنجاح.

تنفيذ الموصّل
- فتح
dataconnect/movie-connector/queries.gqlستجد استعلامًا أساسيًا فيListMoviesفي التعليقات: يجلب طلب البحث هذا جميع الأفلام وتفاصيلها (مثلquery ListMovies @auth(level: PUBLIC) { movies { id title imageUrl releaseYear genre rating tags description } }idوtitleوreleaseYear)، ولكنّه لا يرتب الأفلام. - استبدِل طلب البحث
ListMoviesالحالي بطلب البحث التالي لإضافة خيارات الترتيب والحدّ:# List subset of fields for movies query ListMovies($orderByRating: OrderDirection, $orderByReleaseYear: OrderDirection, $limit: Int) @auth(level: PUBLIC) { movies( orderBy: [ { rating: $orderByRating }, { releaseYear: $orderByReleaseYear } ] limit: $limit ) { id title imageUrl releaseYear genre rating tags description } } - انقر على الزر تشغيل (محلي) لتنفيذ طلب البحث في قاعدة البيانات المحلية. يمكنك أيضًا إدخال متغيّرات طلب البحث في لوحة الإعداد قبل التشغيل.

الأفكار الرئيسية:
-
movies(): حقل طلب بحث GraphQL لاسترداد بيانات الأفلام من قاعدة البيانات orderByRating: معلَمة لترتيب الأفلام حسب التقييم (تصاعديًا أو تنازليًا)- استبدِل
orderByReleaseYearبالمعلمة التي تتيح ترتيب الأفلام حسب سنة الإصدار (تصاعديًا أو تنازليًا). limit: تقيّد عدد الأفلام التي يتم عرضها.
دمج طلبات البحث في تطبيق الويب
في هذا الجزء من الدرس التطبيقي حول الترميز، ستستخدم طلبات البحث المحدّدة في القسم السابق في تطبيق الويب. وتنشئ محاكيات Firebase Data Connect حِزم تطوير البرامج (SDK) استنادًا إلى المعلومات الواردة في ملفات .gql (تحديدًا schema.gql وqueries.gql وmutations.gql) وملف connector.yaml. ويمكن استدعاء حِزم SDK هذه مباشرةً في تطبيقك.
- في
MovieService(app/src/lib/MovieService.tsx)، أزِل التعليق من عبارة الاستيراد في أعلى الصفحة: الدالةimport { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";listMoviesونوع الاستجابةListMoviesDataوالقيمة الثابتةOrderDirectionهي جميعها حِزم تطوير برامج (SDK) أنشأتها محاكيات Firebase Data Connect استنادًا إلى المخطط والاستعلامات التي حدّدتها سابقًا . - استبدِل الدالتَين
handleGetTopMoviesوhandleGetLatestMoviesبالرمز التالي:// Fetch top-rated movies export const handleGetTopMovies = async ( limit: number ): Promise<ListMoviesData["movies"] | null> => { try { const response = await listMovies({ orderByRating: OrderDirection.DESC, limit, }); return response.data.movies; } catch (error) { console.error("Error fetching top movies:", error); return null; } }; // Fetch latest movies export const handleGetLatestMovies = async ( limit: number ): Promise<ListMoviesData["movies"] | null> => { try { const response = await listMovies({ orderByReleaseYear: OrderDirection.DESC, limit, }); return response.data.movies; } catch (error) { console.error("Error fetching latest movies:", error); return null; } };
الأفكار الرئيسية:
-
listMovies: دالة يتم إنشاؤها تلقائيًا وتستدعي طلب البحثlistMoviesلاسترداد قائمة بالأفلام. ويتضمّن خيارات للترتيب حسب التقييم أو سنة الإصدار والحدّ من عدد النتائج. ListMoviesData: هو نوع النتيجة المستخدَم لعرض أفضل 10 أفلام وأحدثها على الصفحة الرئيسية للتطبيق.
مثال عملي
أعِد تحميل تطبيق الويب للاطّلاع على طلب البحث أثناء تنفيذه. تعرض الصفحة الرئيسية الآن قائمة الأفلام بشكل ديناميكي، حيث يتم جلب البيانات مباشرةً من قاعدة البيانات المحلية. ستظهر لك الأفلام الأعلى تقييمًا والأحدث بسلاسة، ما يعكس البيانات التي أعددتها للتو.
6. عرض تفاصيل الأفلام والممثلين
في هذا القسم، ستنفّذ وظيفة استرداد معلومات تفصيلية عن فيلم أو ممثل باستخدام المعرّفات الفريدة الخاصة بهما. ولا يقتصر ذلك على استرداد البيانات من الجداول الخاصة بها، بل يشمل أيضًا ربط الجداول ذات الصلة لعرض تفاصيل شاملة، مثل مراجعات الأفلام وأعمال الممثلين السينمائية.

تنفيذ الموصلات
- افتح
dataconnect/movie-connector/queries.gqlفي مشروعك. - أضِف طلبات البحث التالية لاسترداد تفاصيل الأفلام والممثلين:
# Get movie by id query GetMovieById($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { id title imageUrl releaseYear genre rating description tags metadata: movieMetadatas_on_movie { director } mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) { id name imageUrl } supportingActors: actors_via_MovieActor( where: { role: { eq: "supporting" } } ) { id name imageUrl } reviews: reviews_on_movie { id reviewText reviewDate rating user { id username } } } } # Get actor by id query GetActorById($id: UUID!) @auth(level: PUBLIC) { actor(id: $id) { id name imageUrl mainActors: movies_via_MovieActor(where: { role: { eq: "main" } }) { id title genre tags imageUrl } supportingActors: movies_via_MovieActor( where: { role: { eq: "supporting" } } ) { id title genre tags imageUrl } } } - احفظ التغييرات وراجِع طلبات البحث.
الأفكار الرئيسية:
-
movie()/actor(): حقول طلب بحث GraphQL لجلب فيلم أو ممثل واحد من الجدولMoviesأوActors -
_on_: يتيح ذلك الوصول المباشر إلى الحقول من نوع مرتبط يتضمّن علاقة مفتاح خارجي. على سبيل المثال،reviews_on_movieيجلب كل المراجعات المرتبطة بفيلم معيّن. _via_: تُستخدَم للتنقّل بين علاقات متعددة إلى متعددة من خلال جدول ربط. على سبيل المثال، يصلactors_via_MovieActorإلى النوعActorمن خلال جدول الربطMovieActor، ويتم فلترة الممثلين باستخدام الشرطwhereاستنادًا إلى دورهم (على سبيل المثال، "رئيسي" أو "ثانوي").
اختبار طلب البحث عن طريق إدخال بيانات وهمية
- في لوحة تنفيذ Data Connect، يمكنك اختبار طلب البحث عن طريق إدخال معرّفات وهمية، مثل:
{"id": "550e8400-e29b-41d4-a716-446655440000"} - انقر على تشغيل (محلي) لـ
GetMovieByIdلاسترداد التفاصيل حول "Quantum Paradox" (الفيلم التجريبي الذي يرتبط به المعرّف أعلاه).

دمج طلبات البحث في تطبيق الويب
- في
MovieService(app/src/lib/MovieService.tsx)، أزِل التعليق عن عمليات الاستيراد التالية:import { getMovieById, GetMovieByIdData } from "@movie/dataconnect"; import { GetActorByIdData, getActorById } from "@movie/dataconnect"; - استبدِل الدالتَين
handleGetMovieByIdوhandleGetActorByIdبالرمز البرمجي التالي:// Fetch movie details by ID export const handleGetMovieById = async ( movieId: string ) => { try { const response = await getMovieById({ id: movieId }); if (response.data.movie) { return response.data.movie; } return null; } catch (error) { console.error("Error fetching movie:", error); return null; } }; // Calling generated SDK for GetActorById export const handleGetActorById = async ( actorId: string ): Promise<GetActorByIdData["actor"] | null> => { try { const response = await getActorById({ id: actorId }); if (response.data.actor) { return response.data.actor; } return null; } catch (error) { console.error("Error fetching actor:", error); return null; } };
الأفكار الرئيسية:
-
getMovieById/getActorById: هذه دوال يتم إنشاؤها تلقائيًا وتستدعي طلبات البحث التي حدّدتها، ما يؤدي إلى استرداد معلومات تفصيلية عن فيلم أو ممثل معيّن. -
GetMovieByIdData/GetActorByIdData: هذان النوعان من النتائج يُستخدمان لعرض تفاصيل الأفلام والممثلين في التطبيق.
مثال عملي
الآن، انتقِل إلى الصفحة الرئيسية لتطبيق الويب. انقر على فيلم، وستتمكّن من الاطّلاع على جميع تفاصيله، بما في ذلك الممثلون والمراجعات، وهي معلومات يتم استخلاصها من جداول ذات صلة. وبالمثل، سيؤدي النقر على ممثل إلى عرض الأفلام التي شارك فيها.
7. التعامل مع مصادقة المستخدم
في هذا القسم، ستنفّذ وظائف تسجيل الدخول والخروج للمستخدمين باستخدام خدمة "مصادقة Firebase". ستستخدم أيضًا بيانات مصادقة Firebase لاسترداد بيانات المستخدمين أو إدراجها أو تعديلها مباشرةً في Firebase DataConnect، ما يضمن إدارة المستخدمين بشكل آمن داخل تطبيقك.

تنفيذ الموصلات
- افتح
mutations.gqlفيdataconnect/movie-connector/. - أضِف عملية التعديل التالية لإنشاء المستخدم الحالي الذي تمّت مصادقته أو تعديله:
# Create or update the current authenticated user mutation UpsertUser($username: String!) @auth(level: USER) { user_upsert( data: { id_expr: "auth.uid" username: $username } ) }
الأفكار الرئيسية:
id_expr: "auth.uid": تستخدِم هذه الطريقةauth.uid، التي توفّرها خدمة مصادقة Firebase مباشرةً، وليس المستخدم أو التطبيق، ما يضيف طبقة أمان إضافية من خلال ضمان التعامل مع رقم تعريف المستخدم بشكل آمن وتلقائي.
استرداد المستخدم الحالي
- افتح
queries.gqlفيdataconnect/movie-connector/. - أضِف طلب البحث التالي لجلب المستخدم الحالي:
# Get user by ID query GetCurrentUser @auth(level: USER) { user(key: { id_expr: "auth.uid" }) { id username reviews: reviews_on_user { id rating reviewDate reviewText movie { id title } } favoriteMovies: favorite_movies_on_user { movie { id title genre imageUrl releaseYear rating description tags metadata: movieMetadatas_on_movie { director } } } } }
الأفكار الرئيسية:
auth.uid: يتم استرداد هذا المعرّف مباشرةً من خدمة "مصادقة Firebase"، ما يضمن الوصول الآمن إلى البيانات الخاصة بالمستخدم.- حقول
_on_: تمثّل هذه الحقول جداول الربط:reviews_on_user: تجلب هذه السمة جميع المراجعات المرتبطة بالمستخدم، بما في ذلكidوtitleالخاصَين بالفيلم.-
favorite_movies_on_user: يستردّ جميع الأفلام التي وضع المستخدم علامة عليها كمفضّلة، بما في ذلك معلومات تفصيلية مثلgenreوreleaseYearوratingوmetadata.
دمج طلبات البحث في تطبيق الويب
- في
MovieService(app/src/lib/MovieService.tsx)، أزِل التعليق من عمليات الاستيراد التالية:import { upsertUser } from "@movie/dataconnect"; import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect"; - استبدِل الدالتَين
handleAuthStateChangeوhandleGetCurrentUserبالرمز التالي:// Handle user authentication state changes and upsert user export const handleAuthStateChange = ( auth: any, setUser: (user: User | null) => void ) => { return onAuthStateChanged(auth, async (user) => { if (user) { setUser(user); const username = user.email?.split("@")[0] || "anon"; await upsertUser({ username }); } else { setUser(null); } }); }; // Fetch current user profile export const handleGetCurrentUser = async (): Promise< GetCurrentUserData["user"] | null > => { try { const response = await getCurrentUser(); return response.data.user; } catch (error) { console.error("Error fetching user profile:", error); return null; } };
الأفكار الرئيسية:
handleAuthStateChange: تستمع هذه الدالة إلى تغييرات حالة المصادقة. عندما يسجّل المستخدم الدخول، يتم ضبط بيانات المستخدم واستدعاء عملية التغييرupsertUserلإنشاء معلومات المستخدم أو تعديلها في قاعدة البيانات.handleGetCurrentUser: يستردّ الملف الشخصي للمستخدم الحالي باستخدام طلب البحثgetCurrentUserالذي يستردّ مراجعات المستخدم وأفلامه المفضّلة.
مثال عملي
الآن، انقر على زر "تسجيل الدخول باستخدام حساب Google" في شريط التنقّل. يمكنك تسجيل الدخول باستخدام محاكي مصادقة Firebase. بعد تسجيل الدخول، انقر على "ملفي الشخصي". سيكون هذا الحقل فارغًا في الوقت الحالي، ولكنّك أعددت الأساس لمعالجة البيانات الخاصة بالمستخدمين في تطبيقك.
8. تنفيذ تفاعلات المستخدمين
في هذا القسم من الدرس العملي، ستنفّذ تفاعلات المستخدمين في تطبيق مراجعات الأفلام، وتحديدًا ستتيح للمستخدمين إدارة أفلامهم المفضّلة وكتابة المراجعات أو حذفها.

السماح للمستخدم بإضافة فيلم إلى قائمة الأفلام المفضّلة
في هذا القسم، ستُعدّ قاعدة البيانات للسماح للمستخدمين بإضافة فيلم إلى قائمة الأفلام المفضّلة.
تنفيذ الموصلات
- افتح
mutations.gqlفيdataconnect/movie-connector/. - أضِف عمليات التغيير التالية للتعامل مع إضافة الأفلام إلى قائمة المفضّلة:
# Add a movie to the user's favorites list mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId }) } # Remove a movie from the user's favorites list mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId }) }
الأفكار الرئيسية:
userId_expr: "auth.uid": تستخدمauth.uid، التي توفّرها خدمة "مصادقة Firebase" مباشرةً، ما يضمن عدم الوصول إلى بيانات المستخدم الذي تمّت مصادقته أو تعديلها إلا من خلاله.
التحقّق ممّا إذا كان الفيلم قد تمّت إضافته إلى قائمة الأفلام المفضّلة
- افتح
queries.gqlفيdataconnect/movie-connector/. - أضِف طلب البحث التالي للتحقّق مما إذا كان فيلم قد تم وضعه في قائمة الأفلام المفضّلة:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) { movieId } }
الأفكار الرئيسية:
auth.uid: يضمن الوصول الآمن إلى البيانات الخاصة بالمستخدمين باستخدام "مصادقة Firebase".-
favorite_movie: يتحقّق من جدول الربطfavorite_moviesلمعرفة ما إذا كان المستخدم الحالي قد وضع علامة على فيلم معيّن كفيلم مفضّل.
دمج طلبات البحث في تطبيق الويب
- في
MovieService(app/src/lib/MovieService.tsx)، أزِل التعليق عن عمليات الاستيراد التالية:import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect"; - استبدِل الدوال
handleAddFavoritedMovieوhandleDeleteFavoritedMovieوhandleGetIfFavoritedMovieبالرمز التالي:// Add a movie to user's favorites export const handleAddFavoritedMovie = async ( movieId: string ): Promise<void> => { try { await addFavoritedMovie({ movieId }); } catch (error) { console.error("Error adding movie to favorites:", error); throw error; } }; // Remove a movie from user's favorites export const handleDeleteFavoritedMovie = async ( movieId: string ): Promise<void> => { try { await deleteFavoritedMovie({ movieId }); } catch (error) { console.error("Error removing movie from favorites:", error); throw error; } }; // Check if the movie is favorited by the user export const handleGetIfFavoritedMovie = async ( movieId: string ): Promise<boolean> => { try { const response = await getIfFavoritedMovie({ movieId }); return !!response.data.favorite_movie; } catch (error) { console.error("Error checking if movie is favorited:", error); return false; } };
الأفكار الرئيسية:
handleAddFavoritedMovieوhandleDeleteFavoritedMovie: استخدِم عمليات التغيير لإضافة فيلم إلى قائمة الأفلام المفضّلة للمستخدم أو إزالته منها بشكل آمن.-
handleGetIfFavoritedMovie: تستخدم طلب البحثgetIfFavoritedMovieلمعرفة ما إذا كان المستخدم قد وضع علامة "مفضّل" على فيلم.
مثال عملي
يمكنك الآن إضافة أفلام إلى قائمة الأفلام المفضّلة أو إزالتها منها من خلال النقر على رمز القلب في بطاقات الأفلام وصفحة تفاصيل الفيلم. بالإضافة إلى ذلك، يمكنك الاطّلاع على أفلامك المفضّلة في صفحة ملفك الشخصي.
السماح للمستخدمين بإضافة مراجعات أو حذفها
بعد ذلك، عليك تنفيذ القسم الخاص بإدارة مراجعات المستخدمين في التطبيق.
تنفيذ الموصلات
في mutations.gql (dataconnect/movie-connector/mutations.gql): أضِف التغييرات التالية:
# Add a review for a movie
mutation AddReview($movieId: UUID!, $rating: Int!, $reviewText: String!)
@auth(level: USER) {
review_insert(
data: {
userId_expr: "auth.uid"
movieId: $movieId
rating: $rating
reviewText: $reviewText
reviewDate_date: { today: true }
}
)
}
# Delete a user's review for a movie
mutation DeleteReview($movieId: UUID!) @auth(level: USER) {
review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}
الأفكار الرئيسية:
-
userId_expr: "auth.uid": يضمن ربط المراجعات بالمستخدم الذي تمّت مصادقته. -
reviewDate_date: { today: true }: يتم إنشاء التاريخ الحالي للمراجعة تلقائيًا باستخدام DataConnect، ما يلغي الحاجة إلى إدخاله يدويًا.
دمج طلبات البحث في تطبيق الويب
- في
MovieService(app/src/lib/MovieService.tsx)، أزِل التعليق عن عمليات الاستيراد التالية:import { addReview, deleteReview } from "@movie/dataconnect"; - استبدِل الدالتَين
handleAddReviewوhandleDeleteReviewبالرمز البرمجي التالي:// Add a review to a movie export const handleAddReview = async ( movieId: string, rating: number, reviewText: string ): Promise<void> => { try { await addReview({ movieId, rating, reviewText }); } catch (error) { console.error("Error adding review:", error); throw error; } }; // Delete a review from a movie export const handleDeleteReview = async (movieId: string): Promise<void> => { try { await deleteReview({ movieId }); } catch (error) { console.error("Error deleting review:", error); throw error; } };
الأفكار الرئيسية:
-
handleAddReview: يتم استدعاء عملية التغييرaddReviewلإضافة مراجعة للفيلم المحدّد، مع ربطها بشكل آمن بالمستخدم الذي تمّت مصادقته. -
handleDeleteReview: تستخدم عملية التغييرdeleteReviewلإزالة مراجعة لفيلم كتبها المستخدم الذي تم التحقّق من هويته.
مثال عملي
يمكن للمستخدمين الآن كتابة مراجعات للأفلام على صفحة تفاصيل الفيلم. ويمكنهم أيضًا الاطّلاع على مراجعاتهم وحذفها من صفحة ملفهم الشخصي، ما يمنحهم تحكّمًا كاملاً في تفاعلاتهم مع التطبيق.
9. الفلاتر المتقدّمة ومطابقة النص الجزئي
في هذا القسم، ستنفّذ إمكانات بحث متقدّمة، ما يتيح للمستخدمين البحث عن الأفلام استنادًا إلى مجموعة من التقييمات وسنوات الإصدار، والفلترة حسب الأنواع والعلامات، وإجراء مطابقة جزئية للنص في العناوين أو الأوصاف، وحتى الجمع بين فلاتر متعددة للحصول على نتائج أكثر دقة.

تنفيذ الموصلات
- افتح
queries.gqlفيdataconnect/movie-connector/. - أضِف طلب البحث التالي لإتاحة إمكانات بحث متنوعة:
# Search for movies, actors, and reviews query SearchAll( $input: String $minYear: Int! $maxYear: Int! $minRating: Float! $maxRating: Float! $genre: String! ) @auth(level: PUBLIC) { moviesMatchingTitle: movies( where: { _and: [ { releaseYear: { ge: $minYear } } { releaseYear: { le: $maxYear } } { rating: { ge: $minRating } } { rating: { le: $maxRating } } { genre: { contains: $genre } } { title: { contains: $input } } ] } ) { id title genre rating imageUrl } moviesMatchingDescription: movies( where: { _and: [ { releaseYear: { ge: $minYear } } { releaseYear: { le: $maxYear } } { rating: { ge: $minRating } } { rating: { le: $maxRating } } { genre: { contains: $genre } } { description: { contains: $input } } ] } ) { id title genre rating imageUrl } actorsMatchingName: actors(where: { name: { contains: $input } }) { id name imageUrl } reviewsMatchingText: reviews(where: { reviewText: { contains: $input } }) { id rating reviewText reviewDate movie { id title } user { id username } } }
الأفكار الرئيسية:
- عامل التشغيل
_and: يجمع بين شروط متعددة في طلب بحث واحد، ما يسمح بفلترة البحث حسب عدة حقول، مثلreleaseYearوratingوgenre. - عامل التشغيل
contains: يبحث عن تطابقات جزئية للنص داخل الحقول. في طلب البحث هذا، يتم البحث عن تطابقات ضمنtitleأوdescriptionأوnameأوreviewText. where: تحدّد هذه العبارة شروط فلترة البيانات. يستخدم كل قسم (الأفلام والممثلون والمراجعات) عبارةwhereلتحديد المعايير المحدّدة للبحث.
دمج طلبات البحث في تطبيق الويب
- في
MovieService(app/src/lib/MovieService.tsx)، أزِل التعليق عن عمليات الاستيراد التالية:import { searchAll, SearchAllData } from "@movie/dataconnect"; - استبدِل الدالة
handleSearchAllبالرمز التالي:// Function to perform the search using the query and filters export const handleSearchAll = async ( searchQuery: string, minYear: number, maxYear: number, minRating: number, maxRating: number, genre: string ): Promise<SearchAllData | null> => { try { const response = await searchAll({ input: searchQuery, minYear, maxYear, minRating, maxRating, genre, }); return response.data; } catch (error) { console.error("Error performing search:", error); return null; } };
الأفكار الرئيسية:
handleSearchAll: تستخدم هذه الدالة طلب البحثsearchAllلإجراء بحث استنادًا إلى إدخال المستخدم، مع فلترة النتائج حسب مَعلمات مثل السنة والتقييم والنوع والمطابقات الجزئية للنص.
مثال عملي
انتقِل إلى صفحة "البحث المتقدّم" من شريط التنقّل في تطبيق الويب. يمكنك الآن البحث عن الأفلام والممثلين والمراجعات باستخدام فلاتر ومدخلات مختلفة، والحصول على نتائج بحث مفصّلة ومخصّصة.
10. اختياري: النشر على السحابة الإلكترونية (يجب توفّر الفوترة)
بعد الانتهاء من عملية التطوير المحلية، حان الوقت لنشر المخطط والبيانات وطلبات البحث على الخادم. يمكن إجراء ذلك باستخدام إضافة Firebase Data Connect VS Code أو Firebase CLI.
ترقية خطة أسعار Firebase
لدمج Firebase Data Connect مع Cloud SQL for PostgreSQL، يجب أن يكون مشروع Firebase الخاص بك على خطة الأسعار "الدفع حسب الاستخدام" (Blaze)، ما يعني أنّه مرتبط بحساب فوترة على السحابة الإلكترونية.
- يتطلّب حساب الفوترة في Cloud طريقة دفع، مثل بطاقة الائتمان.
- إذا كنت حديث العهد باستخدام Firebase وGoogle Cloud، تحقّق ممّا إذا كنت مؤهَّلاً للحصول على رصيد بقيمة 300 دولار أمريكي وحساب فوترة في Cloud ضمن "الفترة التجريبية المجانية".
- إذا كنت تجري هذا الدرس التطبيقي حول الترميز كجزء من حدث، اسأل المنظّم عمّا إذا كانت هناك أي أرصدة Cloud متاحة.
لترقية مشروعك إلى خطة Blaze، اتّبِع الخطوات التالية:
- في "وحدة تحكّم Firebase"، اختَر ترقية خطتك.
- اختَر خطة Blaze. اتّبِع التعليمات الظاهرة على الشاشة لربط حساب فوترة على Cloud بمشروعك.
إذا كان عليك إنشاء حساب فوترة على Cloud كجزء من عملية الترقية هذه، قد تحتاج إلى الرجوع إلى مسار الترقية في وحدة تحكّم Firebase لإكمال عملية الترقية.
ربط تطبيق الويب بمشروع Firebase
- سجِّل تطبيق الويب في مشروع Firebase باستخدام وحدة تحكّم Firebase:
- افتح مشروعك، ثم انقر على إضافة تطبيق.
- تجاهَل إعداد حزمة تطوير البرامج (SDK) وإعدادات الضبط في الوقت الحالي، ولكن احرص على نسخ العنصر
firebaseConfigالذي تم إنشاؤه.

- استبدِل
firebaseConfigالحالي فيapp/src/lib/firebase.tsxبالإعداد الذي نسخته للتو من وحدة تحكّم Firebase.const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.firebasestorage.app", messagingSenderId: "SENDER_ID", appId: "APP_ID" }; - إنشاء تطبيق الويب: في VS Code، في المجلد
app، استخدِم Vite لإنشاء تطبيق الويب من أجل نشر الاستضافة:cd app npm run build
إعداد مصادقة Firebase في مشروع Firebase
- إعداد مصادقة Firebase باستخدام ميزة "تسجيل الدخول باستخدام حساب Google"

- (اختياري) يمكنك السماح بنطاقات مصادقة Firebase باستخدام وحدة تحكّم Firebase (على سبيل المثال،
http://127.0.0.1).- في إعدادات المصادقة، انتقِل إلى النطاقات المعتمَدة.
- انقر على "إضافة نطاق" وأدرِج نطاقك المحلي في القائمة.

النشر باستخدام Firebase CLI
- في
dataconnect/dataconnect.yaml، تأكَّد من أنّ معرّف مثيلك وقاعدة البيانات ومعرّف الخدمة يتطابقون مع مشروعك:specVersion: "v1alpha" serviceId: "your-service-id" location: "us-central1" schema: source: "./schema" datasource: postgresql: database: "your-database-id" cloudSql: instanceId: "your-instance-id" connectorDirs: ["./movie-connector"] - تأكَّد من إعداد Firebase CLI مع مشروعك:
npm i -g firebase-tools firebase login --reauth firebase use --add
- في الوحدة الطرفية، شغِّل الأمر التالي لنشر التطبيق:
firebase deploy --only dataconnect,hosting
- نفِّذ الأمر التالي لمقارنة تغييرات المخطط:
firebase dataconnect:sql:diff
- إذا كانت التغييرات مقبولة، طبِّقها باستخدام:
firebase dataconnect:sql:migrate
سيتم تعديل آلة Cloud SQL for PostgreSQL الافتراضية باستخدام المخطط والبيانات النهائية التي تم نشرها. يمكنك تتبُّع الحالة في وحدة تحكُّم Firebase.
من المفترض أن تتمكّن الآن من مشاهدة تطبيقك مباشرةً على your-project.web.app/. بالإضافة إلى ذلك، يمكنك النقر على تشغيل (الإنتاج) في لوحة Firebase Data Connect، تمامًا كما فعلت مع المحاكيات المحلية، لإضافة بيانات إلى بيئة الإنتاج.
11. اختياري: البحث المتّجه باستخدام Firebase Data Connect (يجب توفّر معلومات الفوترة)
في هذا القسم، ستفعِّل البحث المتّجه في تطبيق مراجعات الأفلام باستخدام Firebase Data Connect. تتيح هذه الميزة عمليات بحث مستندة إلى المحتوى، مثل العثور على أفلام بأوصاف مشابهة باستخدام التضمينات المتجهة.
تتطلّب هذه الخطوة إكمال الخطوة الأخيرة من هذا الدرس التطبيقي حول الترميز لنشر التطبيق على Google Cloud.

تعديل المخطّط ليشمل عمليات التضمين لحقل معيّن
في dataconnect/schema/schema.gql، أضِف الحقل descriptionEmbedding إلى الجدول Movie:
type Movie
# The below parameter values are generated by default with @table, and can be edited manually.
@table {
# implicitly calls @col to generates a column name. ex: @col(name: "movie_id")
id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
releaseYear: Int
genre: String
rating: Float
description: String
tags: [String]
descriptionEmbedding: Vector @col(size:768) # Enables vector search
}
الأفكار الرئيسية:
descriptionEmbedding: Vector @col(size:768): يخزّن هذا الحقل عمليات التضمين الدلالية لأوصاف الأفلام، ما يتيح البحث عن المحتوى المستند إلى المتجهات في تطبيقك.
تفعيل Vertex AI
- اتّبِع دليل المتطلبات الأساسية لإعداد واجهات Vertex AI API من Google Cloud. هذه الخطوة ضرورية لإتاحة وظيفتَي إنشاء عمليات التضمين والبحث المتّجه.
- أعِد نشر المخطط لتفعيل
pgvectorوالبحث المتّجه من خلال النقر على "النشر في مرحلة الإنتاج" باستخدام إضافة Firebase Data Connect VS Code.
ملء قاعدة البيانات بالتضمينات
- افتح المجلد
dataconnectفي VS Code. - انقر على تشغيل(محلي) في
optional_vector_embed.gqlلملء قاعدة البيانات بعمليات التضمين الخاصة بالأفلام.

إضافة طلب بحث مستند إلى البحث المتّجهي
في dataconnect/movie-connector/queries.gql، أضِف طلب البحث التالي لإجراء عمليات بحث متّجهة:
# Search movie descriptions using L2 similarity with Vertex AI
query SearchMovieDescriptionUsingL2Similarity($query: String!)
@auth(level: PUBLIC) {
movies_descriptionEmbedding_similarity(
compare_embed: { model: "textembedding-gecko@003", text: $query }
method: L2
within: 2
limit: 5
) {
id
title
description
tags
rating
imageUrl
}
}
الأفكار الرئيسية:
compare_embed: تحدّد نموذج التضمين (textembedding-gecko@003) والنص الإدخال ($query) للمقارنة.-
method: تحدّد طريقة التشابه (L2)، التي تمثّل المسافة الإقليدية. -
within: يقتصر البحث على الأفلام التي تبلغ مسافة L2 فيها 2 أو أقل، مع التركيز على المطابقات القريبة للمحتوى. limit: تقصر عدد النتائج المعروضة على 5.
تنفيذ وظيفة البحث الدلالي في تطبيقك
بعد إعداد المخطط وطلب البحث، يمكنك دمج البحث المتّجه في طبقة الخدمة في تطبيقك. تتيح لك هذه الخطوة استدعاء طلب البحث من تطبيق الويب.
- في
app/src/lib/MovieService.ts، أزِل التعليق عن عمليات الاستيراد التالية من حِزم SDK، وسيعمل ذلك مثل أي طلب بحث آخر.import { searchMovieDescriptionUsingL2similarity, SearchMovieDescriptionUsingL2similarityData, } from "@movie/dataconnect"; - أضِف الدالة التالية لدمج البحث المستند إلى المتجهات في التطبيق:
// Perform vector-based search for movies based on description export const searchMoviesByDescription = async ( query: string ): Promise< | SearchMovieDescriptionUsingL2similarityData["movies_descriptionEmbedding_similarity"] | null > => { try { const response = await searchMovieDescriptionUsingL2similarity({ query }); return response.data.movies_descriptionEmbedding_similarity; } catch (error) { console.error("Error fetching movie descriptions:", error); return null; } };
الأفكار الرئيسية:
-
searchMoviesByDescription: تستدعي هذه الدالة طلب البحثsearchMovieDescriptionUsingL2similarity، وتمرّر النص المُدخَل لإجراء بحث عن المحتوى استنادًا إلى المتجهات.
مثال عملي
انتقِل إلى قسم "البحث المتّجه" في شريط التنقّل واكتب عبارات مثل "رومانسي وعصري". ستظهر لك قائمة بالأفلام التي تتطابق مع المحتوى الذي تبحث عنه، أو يمكنك الانتقال إلى صفحة تفاصيل أي فيلم والاطّلاع على قسم "أفلام مشابهة" في أسفل الصفحة.

12. الخاتمة
تهانينا، من المفترض أن تتمكّن من استخدام تطبيق الويب. إذا كنت تريد تجربة بيانات الأفلام الخاصة بك، لا تقلق، يمكنك إدراج بياناتك باستخدام إضافة Firebase Data Connect عن طريق محاكاة ملفات _insert.gql، أو إضافتها من خلال لوحة تنفيذ Data Connect في VS Code.