1. نظرة عامة
الأهداف
في مختبر الترميز هذا ، ستنشئ تطبيقًا لتوصية المطاعم على نظام Android مدعومًا من Cloud Firestore. سوف تتعلم كيفية:
- اقرأ البيانات واكتبها إلى Firestore من تطبيق Android
- استمع إلى التغييرات في بيانات Firestore في الوقت الفعلي
- استخدم مصادقة Firebase وقواعد الأمان لتأمين بيانات Firestore
- اكتب استعلامات Firestore المعقدة
المتطلبات الأساسية
قبل البدء في هذا الكود ، تأكد من أن لديك:
- Android Studio 4.0 أو أعلى
- محاكي Android مع API 19 أو أعلى
- Node.js الإصدار 10 أو أعلى
- إصدار Java 8 أو أعلى
2. أنشئ مشروع Firebase
- سجّل الدخول إلى وحدة تحكم Firebase باستخدام حساب Google الخاص بك.
- في وحدة تحكم Firebase ، انقر على إضافة مشروع .
- كما هو موضح في لقطة الشاشة أدناه ، أدخل اسمًا لمشروع Firebase (على سبيل المثال ، "Friendly Eats") ، وانقر فوق "متابعة" .
- قد يُطلب منك تمكين Google Analytics ، لأغراض مختبر الرموز هذا لا يهم اختيارك.
- بعد دقيقة أو نحو ذلك ، سيكون مشروع Firebase جاهزًا. انقر فوق متابعة .
3. قم بإعداد مشروع العينة
قم بتنزيل الكود
قم بتشغيل الأمر التالي لنسخ نموذج التعليمات البرمجية لمعمل الرموز هذا. سيؤدي هذا إلى إنشاء مجلد يسمى friendlyeats-android
على جهازك:
$ git clone https://github.com/firebase/friendlyeats-android
إذا لم يكن لديك git على جهازك ، فيمكنك أيضًا تنزيل الكود مباشرةً من GitHub.
أضف تهيئة Firebase
- في وحدة تحكم Firebase ، حدد نظرة عامة على المشروع في التنقل الأيمن. انقر فوق الزر Android لتحديد النظام الأساسي. عندما يُطلب منك اسم الحزمة ، استخدم
com.google.firebase.example.fireeats
- انقر فوق تسجيل التطبيق واتبع التعليمات لتنزيل ملف
google-services.json
، وانقله إلىapp/
مجلد الكود الذي قمت بتنزيله للتو. ثم انقر فوق التالي .
استيراد المشروع
افتح Android Studio. انقر فوق ملف > جديد > استيراد مشروع وحدد مجلد androideats-friendly .
4. قم بإعداد محاكيات Firebase
في مختبر الرموز هذا ، ستستخدم Firebase Emulator Suite لمحاكاة Cloud Firestore محليًا وخدمات Firebase الأخرى. يوفر هذا بيئة تطوير محلية آمنة وسريعة وبدون تكلفة لإنشاء تطبيقك.
قم بتثبيت Firebase CLI
ستحتاج أولاً إلى تثبيت Firebase CLI . إذا كنت تستخدم macOS أو Linux ، فيمكنك تشغيل أمر cURL التالي:
curl -sL https://firebase.tools | bash
إذا كنت تستخدم Windows ، فاقرأ إرشادات التثبيت للحصول على ثنائي مستقل أو للتثبيت عبر npm
.
بمجرد تثبيت CLI ، يجب أن يقوم تشغيل firebase --version
بالإبلاغ عن إصدار 9.0.0
أو أعلى:
$ firebase --version 9.0.0
تسجيل الدخول
قم بتشغيل firebase login
لتوصيل CLI بحساب Google الخاص بك. سيؤدي هذا إلى فتح نافذة متصفح جديدة لإكمال عملية تسجيل الدخول. تأكد من اختيار الحساب نفسه الذي استخدمته عند إنشاء مشروع Firebase مسبقًا.
اربط مشروعك
من داخل مجلد friendlyeats-android
قم بتشغيل firebase use --add
لتوصيل مشروعك المحلي بمشروع Firebase. اتبع المطالبات لتحديد المشروع الذي قمت بإنشائه مسبقًا وإذا طُلب منك اختيار اسم مستعار ، فأدخل default
.
5. قم بتشغيل التطبيق
حان الوقت الآن لتشغيل Firebase Emulator Suite وتطبيق FriendlyEats Android لأول مرة.
قم بتشغيل المحاكيات
في جهازك الطرفي من داخل دليل friendlyeats-android
، قم بتشغيل firebase emulators:start
في بدء تشغيل Firebase Emulators. يجب أن تشاهد سجلات مثل هذا:
$ firebase emulators:start i emulators: Starting emulators: auth, firestore i firestore: Firestore Emulator logging to firestore-debug.log i ui: Emulator UI logging to ui-debug.log ┌─────────────────────────────────────────────────────────────┐ │ ✔ All emulators ready! It is now safe to connect your app. │ │ i View Emulator UI at http://localhost:4000 │ └─────────────────────────────────────────────────────────────┘ ┌────────────────┬────────────────┬─────────────────────────────────┐ │ Emulator │ Host:Port │ View in Emulator UI │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Authentication │ localhost:9099 │ http://localhost:4000/auth │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Firestore │ localhost:8080 │ http://localhost:4000/firestore │ └────────────────┴────────────────┴─────────────────────────────────┘ Emulator Hub running at localhost:4400 Other reserved ports: 4500 Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
لديك الآن بيئة تطوير محلية كاملة تعمل على جهازك! تأكد من ترك هذا الأمر قيد التشغيل لبقية مختبر الرموز ، سيحتاج تطبيق Android الخاص بك إلى الاتصال بالمحاكيات.
ربط التطبيق بالمحاكيات
افتح الملفات util/FirestoreInitializer.kt
util/AuthInitializer.kt
في Android Studio. تحتوي هذه الملفات على المنطق الذي يربط مجموعات Firebase SDK بالمحاكيات المحلية التي تعمل على جهازك عند بدء تشغيل التطبيق.
في طريقة create()
لفئة FirestoreInitializer
، افحص هذا الجزء من الكود:
// Use emulators only in debug builds
if (BuildConfig.DEBUG) {
firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
}
نحن نستخدم BuildConfig
للتأكد من أننا نتصل فقط بالمحاكيات عندما يعمل تطبيقنا في وضع debug
. عندما نقوم بتجميع التطبيق في وضع release
، سيكون هذا الشرط خاطئًا.
يمكننا أن نرى أنه يستخدم طريقة useEmulator(host, port)
لتوصيل Firebase SDK بمحاكي Firestore المحلي. في جميع أنحاء التطبيق ، سنستخدم FirebaseUtil.getFirestore()
للوصول إلى هذا المثيل من FirebaseFirestore
لذلك نحن على يقين من أننا نتصل دائمًا بمحاكي Firestore عند التشغيل في وضع debug
.
قم بتشغيل التطبيق
إذا كنت قد أضفت ملف google-services.json
بشكل صحيح ، فيجب أن يتم تجميع المشروع الآن. في Android Studio ، انقر فوق Build > Rebuild Project وتأكد من عدم وجود أخطاء متبقية.
في Android Studio ، قم بتشغيل التطبيق على محاكي Android. في البداية ستظهر لك شاشة "تسجيل الدخول". يمكنك استخدام أي بريد إلكتروني وكلمة مرور لتسجيل الدخول إلى التطبيق. تتصل عملية تسجيل الدخول هذه بمحاكي Firebase Authentication ، لذلك لا يتم نقل أي بيانات اعتماد حقيقية.
افتح الآن Emulators UI بالانتقال إلى http: // localhost: 4000 في متصفح الويب الخاص بك. ثم انقر فوق علامة التبويب المصادقة وسترى الحساب الذي أنشأته للتو:
بمجرد الانتهاء من عملية تسجيل الدخول ، سترى الشاشة الرئيسية للتطبيق:
سنضيف قريبًا بعض البيانات لملء الشاشة الرئيسية.
6. كتابة البيانات إلى Firestore
في هذا القسم ، سنكتب بعض البيانات إلى Firestore حتى نتمكن من ملء الشاشة الرئيسية الفارغة حاليًا.
كائن النموذج الرئيسي في تطبيقنا هو مطعم (انظر model/Restaurant.kt
). يتم تقسيم بيانات Firestore إلى مستندات ومجموعات ومجموعات فرعية. سنخزن كل مطعم كمستند في مجموعة عالية المستوى تسمى "restaurants"
. لمعرفة المزيد حول نموذج بيانات Firestore ، اقرأ عن المستندات والمجموعات في الوثائق .
لأغراض العرض التوضيحي ، سنضيف وظائف في التطبيق لإنشاء عشرة مطاعم عشوائية عندما نضغط على زر "إضافة عناصر عشوائية" في القائمة الكاملة. افتح الملف MainFragment.kt
واستبدل المحتوى في طريقة onAddItemsClicked()
بـ:
private fun onAddItemsClicked() {
val restaurantsRef = firestore.collection("restaurants")
for (i in 0..9) {
// Create random restaurant / ratings
val randomRestaurant = RestaurantUtil.getRandom(requireContext())
// Add restaurant
restaurantsRef.add(randomRestaurant)
}
}
هناك بعض الأشياء المهمة التي يجب ملاحظتها حول الكود أعلاه:
- بدأنا بالحصول على مرجع لمجموعة
"restaurants"
. يتم إنشاء المجموعات بشكل ضمني عند إضافة المستندات ، لذلك لم تكن هناك حاجة لإنشاء المجموعة قبل كتابة البيانات. - يمكن إنشاء المستندات باستخدام فئات بيانات Kotlin ، والتي نستخدمها لإنشاء كل مستند مطعم.
- تضيف طريقة
add()
مستندًا إلى مجموعة بمعرّف مُنشأ تلقائيًا ، لذلك لم نكن بحاجة إلى تحديد معرّف فريد لكل مطعم.
الآن قم بتشغيل التطبيق مرة أخرى وانقر فوق الزر "إضافة عناصر عشوائية" في القائمة الكاملة (في الزاوية اليمنى العليا) لاستدعاء الكود الذي كتبته للتو:
افتح الآن Emulators UI بالانتقال إلى http: // localhost: 4000 في متصفح الويب الخاص بك. ثم انقر فوق علامة التبويب Firestore وسترى البيانات التي أضفتها للتو:
هذه البيانات 100٪ محلية على جهازك. في الواقع ، لا يحتوي مشروعك الحقيقي حتى الآن على قاعدة بيانات Firestore! هذا يعني أنه من الآمن تجربة تعديل هذه البيانات وحذفها دون عواقب.
تهانينا ، لقد كتبت للتو بيانات إلى Firestore! في الخطوة التالية سوف نتعلم كيفية عرض هذه البيانات في التطبيق.
7. عرض البيانات من Firestore
في هذه الخطوة سوف نتعلم كيفية استرداد البيانات من Firestore وعرضها في تطبيقنا. تتمثل الخطوة الأولى لقراءة البيانات من Firestore في إنشاء Query
. افتح الملف MainFragment.kt
وأضف الكود التالي إلى بداية طريقة onViewCreated()
:
// Firestore
firestore = Firebase.firestore
// Get the 50 highest rated restaurants
query = firestore.collection("restaurants")
.orderBy("avgRating", Query.Direction.DESCENDING)
.limit(LIMIT.toLong())
نريد الآن الاستماع إلى الاستعلام ، حتى نحصل على جميع المستندات المطابقة ويتم إخطارنا بالتحديثات المستقبلية في الوقت الفعلي. نظرًا لأن هدفنا النهائي هو ربط هذه البيانات بـ RecyclerView
، نحتاج إلى إنشاء فئة RecyclerView.Adapter
للاستماع إلى البيانات.
افتح فئة FirestoreAdapter
، التي تم تنفيذها جزئيًا بالفعل. أولاً ، لنجعل المهايئ ينفذ EventListener
ويعرف وظيفة onEvent
بحيث يمكنه تلقي تحديثات لاستعلام Firestore:
abstract class FirestoreAdapter<VH : RecyclerView.ViewHolder>(private var query: Query?) :
RecyclerView.Adapter<VH>(),
EventListener<QuerySnapshot> { // Add this implements
// ...
// Add this method
override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {
// Handle errors
if (e != null) {
Log.w(TAG, "onEvent:error", e)
return
}
// Dispatch the event
if (documentSnapshots != null) {
for (change in documentSnapshots.documentChanges) {
// snapshot of the changed document
when (change.type) {
DocumentChange.Type.ADDED -> {
// TODO: handle document added
}
DocumentChange.Type.MODIFIED -> {
// TODO: handle document changed
}
DocumentChange.Type.REMOVED -> {
// TODO: handle document removed
}
}
}
}
onDataChanged()
}
// ...
}
عند التحميل الأولي ، سيتلقى المستمع حدث ADDED
واحد لكل مستند جديد. نظرًا لأن مجموعة نتائج الاستعلام تتغير بمرور الوقت ، سيتلقى المستمع المزيد من الأحداث التي تحتوي على التغييرات. لننتهي الآن من تطبيق المستمع. قم أولاً بإضافة ثلاث طرق جديدة: onDocumentAdded
و onDocumentModified
و onDocumentRemoved
:
private fun onDocumentAdded(change: DocumentChange) {
snapshots.add(change.newIndex, change.document)
notifyItemInserted(change.newIndex)
}
private fun onDocumentModified(change: DocumentChange) {
if (change.oldIndex == change.newIndex) {
// Item changed but remained in same position
snapshots[change.oldIndex] = change.document
notifyItemChanged(change.oldIndex)
} else {
// Item changed and changed position
snapshots.removeAt(change.oldIndex)
snapshots.add(change.newIndex, change.document)
notifyItemMoved(change.oldIndex, change.newIndex)
}
}
private fun onDocumentRemoved(change: DocumentChange) {
snapshots.removeAt(change.oldIndex)
notifyItemRemoved(change.oldIndex)
}
ثم استدع هذه الطرق الجديدة من onEvent
:
override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {
// Handle errors
if (e != null) {
Log.w(TAG, "onEvent:error", e)
return
}
// Dispatch the event
if (documentSnapshots != null) {
for (change in documentSnapshots.documentChanges) {
// snapshot of the changed document
when (change.type) {
DocumentChange.Type.ADDED -> {
onDocumentAdded(change) // Add this line
}
DocumentChange.Type.MODIFIED -> {
onDocumentModified(change) // Add this line
}
DocumentChange.Type.REMOVED -> {
onDocumentRemoved(change) // Add this line
}
}
}
}
onDataChanged()
}
أخيرًا ، قم بتنفيذ طريقة startListening()
لإرفاق المستمع:
fun startListening() {
if (registration == null) {
registration = query.addSnapshotListener(this)
}
}
الآن تم تكوين التطبيق بالكامل لقراءة البيانات من Firestore. قم بتشغيل التطبيق مرة أخرى وسترى المطاعم التي أضفتها في الخطوة السابقة:
عد الآن إلى Emulator UI في متصفحك وقم بتحرير أحد أسماء المطاعم. يجب أن ترى أنه يتغير في التطبيق على الفور تقريبًا!
8. فرز البيانات وتصفيتها
يعرض التطبيق حاليًا المطاعم ذات التصنيف الأعلى عبر المجموعة بأكملها ، ولكن في تطبيق مطعم حقيقي ، قد يرغب المستخدم في فرز البيانات وتصفيتها. على سبيل المثال ، يجب أن يكون التطبيق قادرًا على عرض "أفضل مطاعم المأكولات البحرية في فيلادلفيا" أو "البيتزا الأقل تكلفة".
يؤدي النقر فوق الشريط الأبيض في الجزء العلوي من التطبيق إلى إظهار مربع حوار عوامل التصفية. في هذا القسم ، سنستخدم استعلامات Firestore لجعل مربع الحوار هذا يعمل:
دعنا نعدل طريقة onFilter()
في MainFragment.kt
. تقبل هذه الطريقة كائن Filters
وهو كائن مساعد أنشأناه لالتقاط إخراج مربع حوار المرشحات. سنقوم بتغيير هذه الطريقة لإنشاء استعلام من المرشحات:
override fun onFilter(filters: Filters) {
// Construct query basic query
var query: Query = firestore.collection("restaurants")
// Category (equality filter)
if (filters.hasCategory()) {
query = query.whereEqualTo(Restaurant.FIELD_CATEGORY, filters.category)
}
// City (equality filter)
if (filters.hasCity()) {
query = query.whereEqualTo(Restaurant.FIELD_CITY, filters.city)
}
// Price (equality filter)
if (filters.hasPrice()) {
query = query.whereEqualTo(Restaurant.FIELD_PRICE, filters.price)
}
// Sort by (orderBy with direction)
if (filters.hasSortBy()) {
query = query.orderBy(filters.sortBy.toString(), filters.sortDirection)
}
// Limit items
query = query.limit(LIMIT.toLong())
// Update the query
adapter.setQuery(query)
// Set header
binding.textCurrentSearch.text = HtmlCompat.fromHtml(
filters.getSearchDescription(requireContext()),
HtmlCompat.FROM_HTML_MODE_LEGACY
)
binding.textCurrentSortBy.text = filters.getOrderDescription(requireContext())
// Save filters
viewModel.filters = filters
}
في المقتطف أعلاه ، قمنا ببناء كائن Query
عن طريق إرفاق جمل where
و orderBy
لمطابقة عوامل التصفية المحددة.
قم بتشغيل التطبيق مرة أخرى وحدد الفلتر التالي لإظهار أشهر المطاعم منخفضة السعر:
يجب أن تشاهد الآن قائمة تمت تصفيتها من المطاعم تحتوي على خيارات السعر المنخفض فقط:
إذا كنت قد وصلت إلى هذا الحد ، فأنت الآن قد أنشأت تطبيقًا لعرض توصيات المطاعم يعمل بكامل طاقته على Firestore! يمكنك الآن فرز المطاعم وتصفيتها في الوقت الفعلي. في الأقسام القليلة التالية سنضيف تعليقات إلى المطاعم ونضيف قواعد أمان إلى التطبيق.
9. تنظيم البيانات في مجموعات فرعية
في هذا القسم ، سنضيف التقييمات إلى التطبيق حتى يتمكن المستخدمون من مراجعة مطاعمهم المفضلة (أو المفضلة على الأقل).
المجموعات والمجموعات الفرعية
حتى الآن قمنا بتخزين جميع بيانات المطاعم في مجموعة عالية المستوى تسمى "المطاعم". عندما يقوم المستخدم بتقييم مطعم ما ، فإننا نريد إضافة كائن Rating
جديد إلى المطاعم. لهذه المهمة سوف نستخدم مجموعة فرعية. يمكنك التفكير في مجموعة فرعية على أنها مجموعة مرفقة بمستند. لذلك سيكون لكل وثيقة مطعم مجموعة فرعية من التصنيفات مليئة بوثائق التصنيف. تساعد المجموعات الفرعية في تنظيم البيانات دون تضخيم مستنداتنا أو طلب استعلامات معقدة.
للوصول إلى مجموعة فرعية ، قم باستدعاء .collection()
في المستند الأصلي:
val subRef = firestore.collection("restaurants")
.document("abc123")
.collection("ratings")
يمكنك الوصول إلى مجموعة فرعية والاستعلام عنها تمامًا كما هو الحال مع مجموعة المستوى الأعلى ، ولا توجد قيود على الحجم أو تغييرات في الأداء. يمكنك قراءة المزيد عن نموذج بيانات Firestore هنا .
كتابة البيانات في المعاملة
لا تتطلب إضافة Rating
إلى المجموعة الفرعية المناسبة سوى استدعاء .add()
، ولكننا نحتاج أيضًا إلى تحديث متوسط تصنيف كائن Restaurant
وعدد التقييمات لعكس البيانات الجديدة. إذا استخدمنا عمليات منفصلة لإجراء هذين التغييرين ، فهناك عدد من ظروف السباق التي قد تؤدي إلى بيانات قديمة أو غير صحيحة.
لضمان إضافة التقييمات بشكل صحيح ، سنستخدم معاملة لإضافة تقييمات إلى مطعم. ستؤدي هذه المعاملة بعض الإجراءات:
- اقرأ التصنيف الحالي للمطعم واحسب التصنيف الجديد
- أضف التصنيف إلى المجموعة الفرعية
- قم بتحديث متوسط تصنيف المطعم وعدد التقييمات
افتح RestaurantDetailFragment.kt
وقم بتنفيذ وظيفة addRating
:
private fun addRating(restaurantRef: DocumentReference, rating: Rating): Task<Void> {
// Create reference for new rating, for use inside the transaction
val ratingRef = restaurantRef.collection("ratings").document()
// In a transaction, add the new rating and update the aggregate totals
return firestore.runTransaction { transaction ->
val restaurant = transaction.get(restaurantRef).toObject<Restaurant>()
?: throw Exception("Restaurant not found at ${restaurantRef.path}")
// Compute new number of ratings
val newNumRatings = restaurant.numRatings + 1
// Compute new average rating
val oldRatingTotal = restaurant.avgRating * restaurant.numRatings
val newAvgRating = (oldRatingTotal + rating.rating) / newNumRatings
// Set new restaurant info
restaurant.numRatings = newNumRatings
restaurant.avgRating = newAvgRating
// Commit to Firestore
transaction.set(restaurantRef, restaurant)
transaction.set(ratingRef, rating)
null
}
}
ترجع الدالة addRating()
Task
تمثل المعاملة بأكملها. في وظيفة onRating()
، تتم إضافة المستمعين إلى المهمة للرد على نتيجة المعاملة.
الآن قم بتشغيل التطبيق مرة أخرى وانقر على أحد المطاعم ، والذي يجب أن يظهر شاشة تفاصيل المطعم. انقر فوق الزر + لبدء إضافة مراجعة. أضف مراجعة باختيار عدد من النجوم وإدخال بعض النصوص.
سيؤدي الضغط على إرسال إلى بدء المعاملة. عند اكتمال المعاملة ، سترى تقييمك معروضًا أدناه وتحديثًا لعدد تعليقات المطعم:
تهاني! لديك الآن تطبيق مراجعة مطعم اجتماعي ومحلي ومتحرك مبني على Cloud Firestore. سمعت أن هؤلاء يحظون بشعبية كبيرة هذه الأيام.
10. تأمين البيانات الخاصة بك
حتى الآن لم نفكر في أمان هذا التطبيق. كيف نعرف أن المستخدمين يمكنهم قراءة وكتابة البيانات الخاصة بهم فقط؟ يتم تأمين قواعد بيانات Firestore بواسطة ملف تكوين يسمى قواعد الأمان .
افتح ملف firestore.rules
، سترى ما يلي:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
//
// WARNING: These rules are insecure! We will replace them with
// more secure rules later in the codelab
//
allow read, write: if request.auth != null;
}
}
}
دعنا نغير هذه القواعد لمنع وصول البيانات أو التغييرات غير المرغوب فيها ، وافتح ملف firestore.rules
واستبدل المحتوى بما يلي:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Determine if the value of the field "key" is the same
// before and after the request.
function isUnchanged(key) {
return (key in resource.data)
&& (key in request.resource.data)
&& (resource.data[key] == request.resource.data[key]);
}
// Restaurants
match /restaurants/{restaurantId} {
// Any signed-in user can read
allow read: if request.auth != null;
// Any signed-in user can create
// WARNING: this rule is for demo purposes only!
allow create: if request.auth != null;
// Updates are allowed if no fields are added and name is unchanged
allow update: if request.auth != null
&& (request.resource.data.keys() == resource.data.keys())
&& isUnchanged("name");
// Deletes are not allowed.
// Note: this is the default, there is no need to explicitly state this.
allow delete: if false;
// Ratings
match /ratings/{ratingId} {
// Any signed-in user can read
allow read: if request.auth != null;
// Any signed-in user can create if their uid matches the document
allow create: if request.auth != null
&& request.resource.data.userId == request.auth.uid;
// Deletes and updates are not allowed (default)
allow update, delete: if false;
}
}
}
}
تقيد هذه القواعد الوصول لضمان قيام العملاء بإجراء تغييرات آمنة فقط. على سبيل المثال ، يمكن للتحديثات في مستند مطعم فقط تغيير التصنيفات ، وليس الاسم أو أي بيانات أخرى غير قابلة للتغيير. لا يمكن إنشاء التقييمات إلا إذا كان معرّف المستخدم يطابق المستخدم الذي سجّل الدخول ، مما يمنع الانتحال.
لقراءة المزيد حول قواعد الأمان ، قم بزيارة الوثائق .
11. الخلاصة
لقد قمت الآن بإنشاء تطبيق كامل الميزات أعلى Firestore. لقد تعرفت على أهم ميزات Firestore بما في ذلك:
- المستندات والمجموعات
- قراءة وكتابة البيانات
- الفرز والتصفية مع الاستفسارات
- المجموعات الفرعية
- المعاملات
يتعلم أكثر
لمواصلة التعرف على Firestore ، إليك بعض الأماكن الجيدة للبدء:
استند تطبيق المطعم في مختبر الرموز هذا إلى مثال تطبيق "Friendly Eats". يمكنك تصفح الكود المصدري لهذا التطبيق هنا .
اختياري: النشر في الإنتاج
حتى الآن ، استخدم هذا التطبيق فقط Firebase Emulator Suite. إذا كنت تريد معرفة كيفية نشر هذا التطبيق في مشروع Firebase حقيقي ، فتابع إلى الخطوة التالية.
12. (اختياري) انشر تطبيقك
حتى الآن ، كان هذا التطبيق محليًا بالكامل ، وجميع البيانات موجودة في Firebase Emulator Suite. ستتعلم في هذا القسم كيفية تكوين مشروع Firebase حتى يعمل هذا التطبيق في الإنتاج.
مصادقة Firebase
في وحدة تحكم Firebase ، انتقل إلى قسم المصادقة وانقر على البدء . انتقل إلى علامة تبويب طريقة تسجيل الدخول وحدد خيار البريد الإلكتروني / كلمة المرور من موفري الخدمة الأصليين .
قم بتمكين طريقة تسجيل الدخول إلى البريد الإلكتروني / كلمة المرور وانقر فوق حفظ .
فايرستور
إنشاء قاعدة بيانات
انتقل إلى قسم قاعدة بيانات Firestore في وحدة التحكم وانقر فوق إنشاء قاعدة بيانات :
- عند مطالبتك بشأن قواعد الأمان ، اختر البدء في وضع الإنتاج ، سنقوم بتحديث هذه القواعد قريبًا.
- اختر موقع قاعدة البيانات الذي ترغب في استخدامه لتطبيقك. لاحظ أن اختيار موقع قاعدة البيانات هو قرار دائم ولتغييره سيتعين عليك إنشاء مشروع جديد. لمزيد من المعلومات حول اختيار موقع المشروع ، راجع الوثائق .
نشر القواعد
لنشر قواعد الأمان التي كتبتها سابقًا ، قم بتشغيل الأمر التالي في دليل codelab:
$ firebase deploy --only firestore:rules
سيؤدي هذا إلى نشر محتويات firestore.rules
إلى مشروعك ، والتي يمكنك تأكيدها بالانتقال إلى علامة التبويب "القواعد" في وحدة التحكم.
نشر الفهارس
يحتوي تطبيق FriendlyEats على عمليات فرز وتصفية معقدة تتطلب عددًا من الفهارس المركبة المخصصة. يمكن إنشاء هذه يدويًا في وحدة تحكم Firebase ولكن من الأسهل كتابة تعريفاتها في ملف firestore.indexes.json
ونشرها باستخدام Firebase CLI.
إذا فتحت ملف firestore.indexes.json
، فسترى أن الفهارس المطلوبة قد تم توفيرها بالفعل:
{
"indexes": [
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "city", "mode": "ASCENDING" },
{ "fieldPath": "avgRating", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "category", "mode": "ASCENDING" },
{ "fieldPath": "avgRating", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "price", "mode": "ASCENDING" },
{ "fieldPath": "avgRating", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "city", "mode": "ASCENDING" },
{ "fieldPath": "numRatings", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "category", "mode": "ASCENDING" },
{ "fieldPath": "numRatings", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "price", "mode": "ASCENDING" },
{ "fieldPath": "numRatings", "mode": "DESCENDING" }
]
},
{
"collectionId": "restaurants",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "city", "mode": "ASCENDING" },
{ "fieldPath": "price", "mode": "ASCENDING" }
]
},
{
"collectionId": "restaurants",
"fields": [
{ "fieldPath": "category", "mode": "ASCENDING" },
{ "fieldPath": "price", "mode": "ASCENDING" }
]
}
],
"fieldOverrides": []
}
لنشر هذه الفهارس ، قم بتشغيل الأمر التالي:
$ firebase deploy --only firestore:indexes
لاحظ أن إنشاء الفهرس ليس فوريًا ، يمكنك مراقبة التقدم في وحدة تحكم Firebase.
تكوين التطبيق
في ملفات util/FirestoreInitializer.kt
و util/AuthInitializer.kt
قمنا بتهيئة Firebase SDK للاتصال بالمحاكيات عندما تكون في وضع التصحيح:
override fun create(context: Context): FirebaseFirestore {
val firestore = Firebase.firestore
// Use emulators only in debug builds
if (BuildConfig.DEBUG) {
firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
}
return firestore
}
إذا كنت ترغب في اختبار تطبيقك باستخدام مشروع Firebase الحقيقي ، فيمكنك إما:
- أنشئ التطبيق في وضع الإصدار وقم بتشغيله على الجهاز.
- استبدل
BuildConfig.DEBUG
مؤقتًا بـfalse
ثم أعد تشغيل التطبيق.
لاحظ أنك قد تحتاج إلى تسجيل الخروج من التطبيق وتسجيل الدخول مرة أخرى للاتصال بشكل صحيح بالإنتاج.