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
- كتابة طلبات بحث وعمليات تحويل مختلفة واختبارها لتطبيق مراجعات الأفلام
- تعرَّف على كيفية إنشاء Firebase Data Connect لحزمة تطوير البرامج (SDK) واستخدامها في التطبيق.
- يمكنك نشر المخطّط وإدارة قاعدة البيانات بكفاءة.
المتطلبات
- Git
- Visual Studio Code
- تثبيت Node.js باستخدام nvm-windows (لنظام التشغيل Windows) أو nvm (لنظام التشغيل macOS/Linux)
- أنشئ مشروعًا على Firebase في وحدة تحكّم Firebase، إذا لم يسبق لك إجراء ذلك.
- (اختياري) بالنسبة إلى البحث باستخدام الرسومات، عليك ترقية مشروعك إلى خطة أسعار Blaze المستندة إلى الدفع عند الاستخدام.
2- إعداد بيئة التطوير
سترشدك هذه المرحلة من ورشة رموز البرامج إلى كيفية إعداد البيئة لبدء إنشاء تطبيق مراجعات الأفلام باستخدام ميزة "ربط البيانات في Firebase".
- استنسِخ مستودع المشروع وثبِّت التبعيات المطلوبة:
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، ثبِّت إضافة Visual Studio لـ Firebase Data Connect.
وبدلاً من ذلك، يمكنك تثبيت الإضافة من 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
: لتحديد مخطّط GraphQLconnector/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: معرّف UUID فريد لكل فيلم، يتم إنشاؤه باستخدام
@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"
}
الأفكار الرئيسية المستخلصة:
- movie: يشير إلى نوع Movie، وينشئ بشكل ضمني مفتاحًا أجنبيًا movieId: UUID!.
- الفاعل: يشير إلى نوع الفاعل، وينشئ بشكل ضمني مفتاحًا خارجيًا actorId: UUID!.
- role: لتحديد دور الممثل في الفيلم (مثل "main" أو "supporting").
إعداد جدول User
يحدِّد نوع User
عنصر مستخدم يتفاعل مع الأفلام من خلال كتابة مراجعات أو إضافة الأفلام إلى المفضّلة.
انسخ مقتطف الرمز وألصقه في ملف dataconnect/schema/schema.gql
:
type User
@table {
id: String! @col(name: "auth_uid")
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!
}
الأفكار الرئيسية المستخلصة:
- 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")
}
الأفكار الرئيسية المستخلصة:
- user: يشير إلى المستخدم الذي كتب المراجعة.
- 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
. ويمكن استدعاء حِزم تطوير البرامج هذه مباشرةً في تطبيقك.
- في
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
الممثلين استنادًا إلى دورهم (على سبيل المثال، "رئيسي" أو "مساعد").
اختبار طلب البحث عن طريق إدخال بيانات وهمية
- في لوحة تنفيذ "ربط البيانات"، يمكنك اختبار طلب البحث عن طريق إدخال معرّفات وهمية، مثل:
{"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 Authentication. ستستخدم أيضًا بيانات Firebase Authentication لاسترداد بيانات المستخدمين أو تعديلها مباشرةً في 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 Authentication مباشرةً، وليس المستخدم أو التطبيق، ما يضيف طبقة إضافية من الأمان من خلال ضمان معالجة معرّف المستخدم بشكل آمن وتلقائي.
جلب المستخدم الحالي
- افتح
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 Authentication، ما يضمن الوصول الآمن إلى البيانات الخاصة بالمستخدم.- حقول
_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. اختياري: النشر على Cloud (يجب توفُّر الفوترة)
بعد الانتهاء من دورة التطوير على الجهاز، حان وقت نشر المخطّط والبيانات والاستعلامات على الخادم. ويمكن إجراء ذلك باستخدام إضافة Firebase Data Connect في VS Code أو واجهة Firebase CLI.
ترقية خطة أسعار Firebase
لدمج Firebase Data Connect مع Cloud SQL for PostgreSQL، يجب أن يكون مشروعك على Firebase مُدرَجًا في خطة الأسعار "الدفع حسب الاستخدام" (Blaze)، ما يعني أنّه مرتبط بحساب على Cloud Billing.
- يتطلب حساب "الفوترة في Google Cloud" طريقة دفع، مثل بطاقة الائتمان.
- إذا كنت حديث العهد باستخدام Firebase وGoogle Cloud، تحقّق ممّا إذا كنت مؤهلاً للحصول على رصيد بقيمة 300 دولار أمريكي وحساب "الفوترة في السحابة الإلكترونية" في الفترة التجريبية المجانية.
- إذا كنت تُجري هذا الإصدار التجريبي من رمز المصدر كجزء من حدث، اسأل المنظِّم ما إذا كانت هناك أي أرصدة متاحة في 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 Authentication في مشروعك على Firebase
- إعداد Firebase Authentication باستخدام ميزة "تسجيل الدخول باستخدام حساب 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 لنظام 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 من 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.