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.
إعداد بيئة التطوير
سيرشدك هذا القسم خلال عملية إعداد البيئة لبدء إنشاء تطبيق مراجعات الأفلام باستخدام Firebase Data Connect.
الخطوة 1: استنساخ مستودع المشروع
ابدأ من خلال استنساخ مستودع المشروع وتثبيت التبعيات المطلوبة:
git clone https://github.com/firebaseextended/codelab-dataconnect-web cd codelab-dataconnect-web cd ./app && npm i npm run dev
- بعد تنفيذ هذه الأوامر، افتح http://localhost:5173 في المتصفّح للاطّلاع على تطبيق الويب الذي يتم تشغيله على الجهاز. ويكون هذا القسم هو الواجهة الأمامية لإنشاء تطبيق مراجعة الأفلام والتفاعل مع ميزاته.
الخطوة 2: فتح المشروع في Visual Studio Code
افتح مجلد codelab-dataconnect-web
الذي تم نسخه باستخدام Visual Studio Code. في هذا القسم، يمكنك تحديد المخطّط وكتابة طلبات البحث واختبار وظائف التطبيق.
الخطوة 3: تثبيت إضافة Firebase Data Connect في Visual Studio
لاستخدام ميزات Data Connect، ثبِّت إضافة Visual Studio لـ Firebase Data Connect.أو ثبِّتها من Visual Studio Code Marketplace أو ابحث عنها في VS Code.
- أو: ثبِّت الإضافة من Visual Studio Code Marketplace أو ابحث عنها في VS Code.
الخطوة 4: إنشاء مشروع على Firebase
انتقِل إلى وحدة تحكّم Firebase لإنشاء مشروع جديد على Firebase إذا لم يكن لديك مشروع حالي. بعد ذلك، في إضافة Firebase Data Connect في VSCode:
- انقر على الزر تسجيل الدخول.
- انقر على ربط مشروع Firebase واختَر المشروع الذي أنشأته في "وحدة تحكُّم Firebase".
الخطوة 5: بدء محاكيات Firebase
في إضافة Firebase Data Connect في VSCode، انقر على "بدء المحاكيات" وتأكَّد من أنّ المحاكيات تعمل في المحطة الطرفية.
2- مراجعة قاعدة بيانات التطبيق الأساسي
في هذا القسم، ستستكشف الجوانب الرئيسية لقاعدة بيانات التطبيق الأساسية. على الرغم من أنّ التطبيق لا يتضمّن بعض الوظائف، إلا أنّه من المفيد فهم الهيكل العام.
بنية المجلدات والملفات
في ما يلي نظرة عامة سريعة على بنية مجلدات التطبيق وملفاته:
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 في وحدة التحكّمlib/dataconnect-sdk/
: يحتوي هذا المجلد على حزمة SDK التي تم إنشاؤها. يمكنك تعديل موقع إنشاء حِزم SDK في ملف connector/connector.yaml، وسيتم إنشاء حِزم SDK تلقائيًا في أي وقت تحدّد فيه طلب بحث أو تعديلًا.
3- تحديد مخطّط لمراجعة فيلم
في هذا القسم، ستحدِّد في مخطّط الهيكل والعلاقات بين الكيانات الرئيسية في تطبيق الأفلام. يتمّ ربط كيانات مثل Movie
وUser
وActor
وReview
بجداول قاعدة البيانات، مع إنشاء العلاقات باستخدام Firebase Data Connect وتوجيهات مخطّط GraphQL. بعد الانتهاء من ذلك، سيكون تطبيقك جاهزًا للتعامل مع كل شيء، بدءًا من البحث عن الأفلام الأعلى تقييمًا والفلترة حسب النوع، إلى السماح للمستخدمين بكتابة مراجعات أو وضع علامة على المحتوى المفضّل أو استكشاف الأفلام المشابهة أو العثور على أفلام مقترَحة استنادًا إلى النص الذي يُدخلونه من خلال البحث بالاستناد إلى المتجهات.
الكيانات والعلاقات الأساسية
يحتوي نوع Movie
على تفاصيل رئيسية، مثل العنوان والنوع والعلامات، التي يستخدمها التطبيق في عمليات البحث والملفات الشخصية للأفلام. يتتبّع النوع User
تفاعلات المستخدمين، مثل المراجعات والعناصر المفضّلة. Reviews
ربط المستخدمين بالأفلام، والسماح للتطبيق بعرض التقييمات والملاحظات التي ينشئها المستخدمون
تجعل العلاقات بين الأفلام والممثلين والمستخدمين التطبيق أكثر ديناميكية. يساعد جدول الربط MovieActor
في عرض تفاصيل الممثلين وأعمالهم السينمائية. يسمح نوع FavoriteMovie
للمستخدمين بإضافة الأفلام إلى قائمة المفضّلة، ما يتيح للتطبيق عرض قائمة مخصّصة بالمفضّلة وتسليط الضوء على الاختيارات الرائجة.
جدول الأفلام
يحدِّد نوع 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
، ما يؤدّي إلى إنشاء علاقة مفتاح خارجي.
جدول الجهات الفاعلة
انسخ مقتطف الرمز والصقه في ملف dataconnect/schema/schema.gql
:
type Actor @table {
id: UUID!
imageUrl: String!
name: String! @col(name: "name", dataType: "varchar(30)")
}
يمثّل نوع Actor
ممثلًا في قاعدة بيانات الأفلام، حيث يمكن أن يكون كل ممثل جزءًا من أفلام متعددة، ما يشكّل علاقة بين عناصر متعددة.
جدول "ممثلو الأفلام"
انسخ مقتطف الرمز والصقه في ملف 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
عنصر مستخدم يتفاعل مع الأفلام من خلال كتابة مراجعات أو إضافة الأفلام إلى المفضّلة.
انسخ مقتطف الرمز والصقه في ملف 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!.
جدول المراجعة
يمثّل نوع المراجعة عنصر المراجعة ويربط نوعَي "المستخدم" و"الفيلم" في علاقة بين أطراف متعددة (يمكن لمستخدم واحد نشر العديد من المراجعات، ويمكن أن يتضمّن كل فيلم العديد من المراجعات).
انسخ مقتطف الرمز والصقه في ملف 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 في نوعَي "الفيلم" و"المراجعة" تلقائيًا بمعرّف فريد عالمي (UUID) عند إنشاء سجلّ جديد.
بعد تحديد المخطّط، أصبح تطبيق الأفلام لديك يستند إلى قاعدة صلبة لهيكل البيانات والعلاقة بينها.
4. استرجاع أحدث الأفلام والأفلام الأكثر رواجًا
في هذا القسم، ستُدرج بيانات أفلام وهمية في المحاكيات المحلية، ثم ستنفِّذ الموصّلات (طلبات البحث) ورمز 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
}
}
يعرض طلب البحث هذا جميع الأفلام وتفاصيلها (مثل رقم التعريف والعنوان وسنة الإصدار). ومع ذلك، لا يتم ترتيب الأفلام.
- استبدِل طلب البحث
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 أفلام وأحدث الأفلام على الصفحة الرئيسية.
أمثلة واقعية
أعِد تحميل تطبيق الويب للاطّلاع على طلب البحث أثناء تنفيذه. تعرض الصفحة الرئيسية الآن قائمة الأفلام بشكل ديناميكي، وتسترجع البيانات مباشرةً من قاعدة البيانات المحلية. ستظهر لك أحدث الأفلام وتلك التي حصلت على أعلى التقييمات بسلاسة، استنادًا إلى البيانات التي ضبطتها للتو.
5- عرض تفاصيل الفيلم والممثل
في هذا القسم، ستنفّذ وظيفة استرداد معلومات تفصيلية عن فيلم أو ممثل باستخدام معرّفاتهما الفريدة. لا يقتصر ذلك على جلب البيانات من الجداول المعنية فحسب، بل يشمل أيضًا دمج الجداول ذات الصلة لعرض تفاصيل شاملة، مثل مراجعات الأفلام وفيلموجرافيات الممثلين.
تنفيذ الموصّلات
- افتح
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 لجلب فيلم أو ممثل واحد من جدول "الأفلام" أو "الممثلون" _on_
: يتيح هذا الوصول المباشر إلى الحقول من نوع مرتبط له علاقة مفتاح خارجي. على سبيل المثال، يُستخدَمreviews_on_movie
لاسترداد جميع المراجعات المرتبطة بفيلم معيّن._via_
: يُستخدَم للتنقّل في العلاقات بين عناصر متعددة من خلال جدول ربط. على سبيل المثال، يصل العمودactors_via_MovieActor
إلى نوع "الممثل" من خلال جدول الربط "الممثل في الفيلم"، وفلترَت الحالةwhere
الممثلين استنادًا إلى دورهم (مثل "main" أو "supporting").
في لوحة تنفيذ "ربط البيانات"، يمكنك اختبار طلب البحث عن طريق إدخال معرّفات وهمية، مثل:
{"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
: هذه هي أنواع النتائج المستخدَمة لعرض تفاصيل الأفلام والممثلين في التطبيق.
أمثلة واقعية
الآن، انتقِل إلى الصفحة الرئيسية لتطبيق الويب. انقر على فيلم، وستتمكّن من الاطّلاع على جميع تفاصيله، بما في ذلك الممثلون والمراجعات، وهي معلومات يتم استخراجها من الجداول ذات الصلة. وبالمثل، سيؤدي النقر على ممثل إلى عرض الأفلام التي شارك فيها.
6- التعامل مع مصادقة المستخدم
في هذا القسم، ستنفِّذ وظيفة تسجيل دخول المستخدم وتسجيل خروجه باستخدام 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"، ما يضمن الوصول الآمن إلى البيانات الخاصة بالمستخدم._on_
الحقول: تمثّل هذه الحقول الجداول التي يتمّ دمجها:-
reviews_on_user
: تُستخدَم هذه السمة لاسترداد جميع المراجعات المرتبطة بالمستخدم، بما في ذلك رقم تعريف الفيلم وعنوانه. -
favorite_movies_on_user
: استرداد جميع الأفلام التي وضع المستخدم علامة عليها باعتبارها مفضّلة، بما في ذلك معلومات تفصيلية مثل النوع وسنة الإصدار والتقييم والبيانات الوصفية
دمج طلبات البحث في تطبيق الويب
- في 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 Auth. بعد تسجيل الدخول، انقر على "ملفي الشخصي". سيكون هذا القسم فارغًا في الوقت الحالي، ولكنك أنشأت الأساس لمعالجة البيانات الخاصة بالمستخدمين في تطبيقك.
7- تنفيذ تفاعلات المستخدِمين
في هذا القسم، ستنفّذ تفاعلات المستخدمين في تطبيق مراجعة الأفلام، ما يسمح للمستخدمين بإدارة الأفلام المفضّلة لديهم وكتابة مراجعات أو حذفها.
تنفيذ الموصّلات
- افتح
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
لإزالة مراجعة لفيلم من قِبل المستخدم الذي تمّت مصادقة هويته.
أمثلة واقعية
يمكن للمستخدمين الآن كتابة مراجعات عن الأفلام في صفحة تفاصيل الفيلم. ويمكنهم أيضًا عرض مراجعاتهم وحذفها من صفحة ملفهم الشخصي، ما يمنحهم إمكانية التحكّم بشكل كامل في تفاعلاتهم مع التطبيق.
8- الفلاتر المتقدّمة ومطابقة النص الجزئي
في هذا القسم، ستنفّذ إمكانات بحث متقدّمة، ما يسمح للمستخدمين بالبحث عن الأفلام استنادًا إلى مجموعة من التقييمات وسنوات الإصدار، والفلترة حسب الأنواع والعلامات، وإجراء مطابقة نصية جزئية في العناوين أو الأوصاف، وحتى دمج فلاتر متعددة للحصول على نتائج أكثر دقة.
تنفيذ الموصّلات
- افتح
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
لإجراء بحث استنادًا إلى إدخال المستخدم، مع فلترة النتائج حسب مَعلمات مثل السنة والتقييم والنوع والمطابقات النصية الجزئية.
أمثلة واقعية
انتقِل إلى صفحة "البحث المتقدّم" من شريط التنقل في تطبيق الويب. يمكنك الآن البحث عن الأفلام والممثلين والمراجعات باستخدام فلاتر ومدخلات مختلفة، ما يؤدي إلى الحصول على نتائج بحث مفصّلة ومخصّصة.
9- اختياري: النشر على Cloud (يجب تفعيل الفوترة)
بعد الانتهاء من دورة التطوير على الجهاز، حان وقت نشر المخطّط والبيانات والاستعلامات على الخادم. ويمكن إجراء ذلك باستخدام إضافة Firebase Data Connect في VS Code أو واجهة Firebase CLI.
إضافة تطبيق ويب في وحدة تحكّم Firebase
- أنشئ تطبيق ويب في وحدة تحكّم Firebase وسجِّل رقم تعريف تطبيقك.
- إعداد تطبيق ويب في وحدة تحكّم Firebase من خلال النقر على "إضافة تطبيق" يمكنك تجاهل عملية إعداد حزمة SDK وضبطها في الوقت الحالي، ولكن عليك تدوين عنصر
firebaseConfig
الذي تم إنشاؤه. - استبدِل
firebaseConfig
فيapp/src/lib/firebase.tsx
:
const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.appspot.com", messagingSenderId: "SENDER_ID", appId: "APP_ID" };
- إنشاء تطبيق الويب: في مجلد
app
، استخدِم Vite لإنشاء تطبيق الويب من أجل استضافة عملية النشر:
cd app npm run build
إعداد Firebase Authentication في وحدة التحكّم
- إعداد Firebase Auth باستخدام ميزة "تسجيل الدخول باستخدام حساب Google"
- (اختياري) اسمح بالنطاقات لخدمة (Firebase Auth) [https://firebase.google.com/docs/auth/web/hosting] في وحدة تحكّم المشروع (مثل
http://127.0.0.1
):
- في إعدادات Auth (المصادقة)، اختَر مشروعك وانتقِل إلى (النطاقات المعتمَدة) [https://firebase.google.com/docs/auth/web/hosting]. انقر على "إضافة نطاق" وأدرِج نطاقك المحلي في القائمة.
النشر باستخدام 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، تمامًا كما فعلت مع المحاكيات المحلية، لإضافة بيانات إلى بيئة الإصدار العلني.
10- اختياري: البحث المرئي باستخدام Firebase Data Connect
في هذا القسم، ستفعّل ميزة البحث بالاستناد إلى المتجهات في تطبيق مراجعات الأفلام باستخدام Firebase Data Connect. تتيح هذه الميزة إجراء عمليات بحث مستندة إلى المحتوى، مثل العثور على أفلام تتضمّن أوصافًا مشابهة باستخدام عمليات إدراج المتجهات.
تعديل المخطّط لتضمين عمليات التضمين لحقل معيّن
- في
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 في VSCode.
ملء قاعدة البيانات بالعناصر المضمّنة
- افتح مجلد
dataconnect
في VSCode وانقر على Run(local) (تشغيل (محلي)) في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
، أزِل التعليقات من عمليات الاستيراد التالية:
تنفيذ وظيفة البحث عن المتجهات في التطبيق
بعد إعداد المخطّط وطلب البحث، يمكنك دمج البحث بالاستناد إلى المتجهات في طبقة الخدمات في تطبيقك. تتيح لك هذه الخطوة استدعاء طلب البحث من تطبيق الويب.
في 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
، مع تمرير نص الإدخال لإجراء بحث عن المحتوى بالاستناد إلى المتجهات.
أمثلة واقعية
انتقِل إلى قسم "بحث متجهات" في شريط التنقل واكتب عبارات مثل "رومانسي وحديث". ستظهر لك قائمة بالأفلام التي تتطابق مع المحتوى الذي تبحث عنه، أو يمكنك الانتقال إلى صفحة تفاصيل أي فيلم والاطّلاع على قسم الأفلام المشابهة في أسفل الصفحة.
11- الخاتمة
تهانينا، من المفترض أن يكون بإمكانك استخدام تطبيق الويب. إذا كنت تريد استخدام بيانات الأفلام الخاصة بك، لا داعي للقلق، يمكنك إدراج بياناتك باستخدام إضافة FDC عن طريق تقليد ملفات _insert.gql أو إضافتها من خلال لوحة تنفيذ "ربط البيانات".