Cloud Firestore Android Codelab

1. نظرة عامة

الأهداف

في مختبر الترميز هذا ، ستنشئ تطبيقًا لتوصية المطاعم على نظام Android مدعومًا من Cloud Firestore. سوف تتعلم كيفية:

  • اقرأ البيانات واكتبها إلى Firestore من تطبيق Android
  • استمع إلى التغييرات في بيانات Firestore في الوقت الفعلي
  • استخدم مصادقة Firebase وقواعد الأمان لتأمين بيانات Firestore
  • اكتب استفسارات Firestore المعقدة

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

قبل البدء في هذا الكود ، تأكد من أن لديك:

  • الروبوت ستوديو 4.0 أو أعلى
  • محاكي Android
  • نسخة نود.جي إس 10 أو أعلى
  • جافا الإصدار 8 أو أعلى

2. أنشئ مشروع Firebase

  1. تسجيل الدخول إلى وحدة التحكم Firebase مع حسابك في Google.
  2. في وحدة التحكم Firebase ، انقر فوق المشروع إضافة.
  3. كما هو مبين في لقطة الشاشة أدناه، أدخل اسما للمشروع Firebase الخاص بك (على سبيل المثال، "ودية يأكل")، وانقر فوق متابعة.

9d2f625aebcab6af.png

  1. قد يُطلب منك تمكين Google Analytics ، لأغراض مختبر الرموز هذا لا يهم اختيارك.
  2. بعد دقيقة أو نحو ذلك ، سيكون مشروع Firebase جاهزًا. انقر فوق متابعة.

3. إعداد مشروع العينة

قم بتنزيل الكود

قم بتشغيل الأمر التالي لنسخ نموذج التعليمات البرمجية لمعمل الرموز هذا. هذا سيخلق مجلد يسمى friendlyeats-android على جهازك:

$ git clone https://github.com/firebase/friendlyeats-android

إذا لم يكن لديك git على جهازك ، فيمكنك أيضًا تنزيل الكود مباشرةً من GitHub.

استيراد المشروع إلى الروبوت الاستوديو. هل من المحتمل أن نرى بعض أخطاء ترجمة أو ربما تحذيرا حول المفقودين google-services.json الملف. سنقوم بتصحيح هذا في القسم التالي.

أضف تهيئة Firebase

  1. في وحدة التحكم Firebase ، حدد محة عامة عن المشروع في التنقل اليمنى. انقر على زر الروبوت لتحديد المنصة. عند المطالبة للحصول على اسم الحزمة استخدام com.google.firebase.example.fireeats

73d151ed16016421.png

  1. انقر فوق تسجيل التطبيقات واتبع التعليمات لتحميل google-services.json الملف، ونقلها إلى app/ مجلد من نموذج التعليمات البرمجية. ثم انقر فوق التالي.

4. قم بإعداد محاكيات Firebase

في هذا codelab عليك استخدام المحاكي جناح Firebase لمحاكاة محليا سحابة Firestore والخدمات Firebase أخرى. يوفر هذا بيئة تطوير محلية آمنة وسريعة ومجانية لإنشاء تطبيقك.

قم بتثبيت Firebase CLI

اولا، ستحتاج إلى تثبيت Firebase CLI . أسهل طريقة للقيام بذلك هو استخدام npm :

npm install -g firebase-tools

إذا لم يكن لديك 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 المحاكيات. يجب أن تشاهد سجلات مثل هذا:

$ 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 الخاص بك إلى الاتصال بالمحاكيات.

ربط التطبيق بالمحاكيات

فتح ملف FirebaseUtil.java في الروبوت الاستوديو. يحتوي هذا الملف على المنطق لتوصيل Firebase SDKs بمحاكيات محلية تعمل على جهازك.

في الجزء العلوي من الملف ، افحص هذا السطر:

    /** Use emulators only in debug builds **/
    private static final boolean sUseEmulators = BuildConfig.DEBUG;

نحن نستخدم BuildConfig للتأكد من أننا توصيل فقط لمحاكاة عند تشغيل التطبيق لدينا في debug الوضع. عندما كنا تجميع التطبيق في release وضع هذا الشرط سوف تكون كاذبة.

الآن نلقي نظرة على getFirestore() الأسلوب:

    public static FirebaseFirestore getFirestore() {
        if (FIRESTORE == null) {
            FIRESTORE = FirebaseFirestore.getInstance();

            // Connect to the Cloud Firestore emulator when appropriate. The host '10.0.2.2' is a
            // special IP address to let the Android emulator connect to 'localhost'.
            if (sUseEmulators) {
                FIRESTORE.useEmulator("10.0.2.2", 8080);
            }
        }

        return FIRESTORE;
    }

يمكننا أن نرى أنه يستخدم useEmulator(host, port) طريقة لربط SDK Firebase إلى محاكي Firestore المحلي. في جميع أنحاء التطبيق سوف نستخدم FirebaseUtil.getFirestore() للوصول إلى هذه الحالة من FirebaseFirestore لذلك نحن واثقون من أننا يربط دائما إلى محاكي Firestore عند التشغيل في debug الوضع.

قم بتشغيل التطبيق

إذا قمت بإضافة google-services.json الملف بشكل صحيح، يجب أن المشروع الآن تجميع. في الروبوت ستوديو انقر فوق إنشاء> إعادة بناء المشروع وضمان عدم وجود أية أخطاء المتبقية.

في الروبوت ستوديو تشغيل التطبيق على محاكي الروبوت الخاص بك. في البداية ستظهر لك شاشة "تسجيل الدخول". يمكنك استخدام أي بريد إلكتروني وكلمة مرور لتسجيل الدخول إلى التطبيق. تتصل عملية تسجيل الدخول هذه بمحاكي Firebase Authentication ، لذلك لا يتم نقل أي بيانات اعتماد حقيقية.

الآن فتح UI المحاكيات قبل الانتقال إلى HTTP: // المضيف المحلي: 4000 في متصفح الويب الخاص بك. ثم انقر فوق علامة التبويب مصادقة وسترى الحساب الذي أنشأته للتو:

محاكي Firebase Auth

بمجرد الانتهاء من عملية تسجيل الدخول ، سترى الشاشة الرئيسية للتطبيق:

de06424023ffb4b9.png

سنضيف قريبًا بعض البيانات لملء الشاشة الرئيسية.

6. كتابة البيانات إلى Firestore

في هذا القسم ، سنكتب بعض البيانات إلى Firestore حتى نتمكن من ملء الشاشة الرئيسية الفارغة حاليًا.

الكائن النموذج الرئيسي في التطبيق لدينا هو مطعم (انظر model/Restaurant.java ). يتم تقسيم بيانات Firestore إلى مستندات ومجموعات ومجموعات فرعية. سنقوم بتخزين كل مطعم كوثيقة في مجموعة المستوى الأعلى يسمى "restaurants" . لمعرفة المزيد عن نموذج البيانات Firestore، قرأت عن وثائق ومجموعات في وثائق .

لأغراض العرض التوضيحي ، سنضيف وظائف في التطبيق لإنشاء عشرة مطاعم عشوائية عندما نضغط على زر "إضافة عناصر عشوائية" في القائمة الكاملة. فتح ملف MainActivity.java وملء في onAddItemsClicked() الأسلوب:

    private void onAddItemsClicked() {
        // Get a reference to the restaurants collection
        CollectionReference restaurants = mFirestore.collection("restaurants");

        for (int i = 0; i < 10; i++) {
            // Get a random Restaurant POJO
            Restaurant restaurant = RestaurantUtil.getRandom(this);

            // Add a new document to the restaurants collection
            restaurants.add(restaurant);
        }
    }

هناك بعض الأشياء المهمة التي يجب ملاحظتها حول الكود أعلاه:

  • بدأنا الحصول على إشارة إلى "restaurants" جمع. يتم إنشاء المجموعات بشكل ضمني عند إضافة المستندات ، لذلك لم تكن هناك حاجة لإنشاء المجموعة قبل كتابة البيانات.
  • يمكن إنشاء المستندات باستخدام POJO ، والتي نستخدمها لإنشاء كل مستند مطعم.
  • و add() يضيف طريقة مستند إلى جمع بمعرف الذي تم إنشاؤه تلقائيا، لذلك لم نكن بحاجة إلى تحديد هوية فريدة من نوعها لكل مطعم.

الآن قم بتشغيل التطبيق مرة أخرى وانقر فوق الزر "إضافة عناصر عشوائية" في القائمة الكاملة لاستدعاء الرمز الذي كتبته للتو:

95691e9b71ba55e3.png

الآن فتح UI المحاكيات قبل الانتقال إلى HTTP: // المضيف المحلي: 4000 في متصفح الويب الخاص بك. ثم انقر فوق علامة التبويب Firestore وسترى البيانات التي بإضافته:

محاكي Firebase Auth

هذه البيانات 100٪ محلية على جهازك. في الواقع ، لا يحتوي مشروعك الحقيقي حتى الآن على قاعدة بيانات Firestore! هذا يعني أنه من الآمن تجربة تعديل هذه البيانات وحذفها دون عواقب.

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

7. عرض البيانات من Firestore

في هذه الخطوة سوف نتعلم كيفية استرداد البيانات من Firestore وعرضها في تطبيقنا. الخطوة الأولى لقراءة البيانات من Firestore هي لإنشاء Query . تعديل onCreate() الأسلوب:

        mFirestore = FirebaseUtil.getFirestore();

        // Get the 50 highest rated restaurants
        mQuery = mFirestore.collection("restaurants")
                .orderBy("avgRating", Query.Direction.DESCENDING)
                .limit(LIMIT);

نريد الآن الاستماع إلى الاستعلام ، حتى نحصل على جميع المستندات المطابقة ويتم إخطارنا بالتحديثات المستقبلية في الوقت الفعلي. لأن هدفنا في نهاية المطاف هو ربط هذه البيانات إلى RecyclerView ، نحن بحاجة إلى إنشاء RecyclerView.Adapter الطبقة للاستماع إلى البيانات.

فتح FirestoreAdapter الطبقة، والتي تم تنفيذها جزئيا بالفعل. أولا، دعونا جعل محول تنفيذ EventListener وتحديد onEvent وظيفة حتى تتمكن من تلقي التحديثات على استفسار Firestore:

public abstract class FirestoreAdapter<VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<VH>
        implements EventListener<QuerySnapshot> { // Add this "implements"

    // ...

    // Add this method
    @Override
    public void onEvent(QuerySnapshot documentSnapshots,
                        FirebaseFirestoreException e) {

        // Handle errors
        if (e != null) {
            Log.w(TAG, "onEvent:error", e);
            return;
        }

        // Dispatch the event
        for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
            // Snapshot of the changed document
            DocumentSnapshot snapshot = change.getDocument();

            switch (change.getType()) {
                case ADDED:
                    // TODO: handle document added
                    break;
                case MODIFIED:
                    // TODO: handle document modified
                    break;
                case REMOVED:
                    // TODO: handle document removed
                    break;
            }
        }

        onDataChanged();
    }

  // ...
}

في الحمل الأولي المستمع سوف تتلقى إحدى ADDED الحدث لكل وثيقة جديدة. نظرًا لأن مجموعة نتائج الاستعلام تتغير بمرور الوقت ، سيتلقى المستمع المزيد من الأحداث التي تحتوي على التغييرات. لننتهي الآن من تطبيق المستمع. إضافة أول ثلاث طرق جديدة: onDocumentAdded ، onDocumentModified ، وعلى onDocumentRemoved :

    protected void onDocumentAdded(DocumentChange change) {
        mSnapshots.add(change.getNewIndex(), change.getDocument());
        notifyItemInserted(change.getNewIndex());
    }

    protected void onDocumentModified(DocumentChange change) {
        if (change.getOldIndex() == change.getNewIndex()) {
            // Item changed but remained in same position
            mSnapshots.set(change.getOldIndex(), change.getDocument());
            notifyItemChanged(change.getOldIndex());
        } else {
            // Item changed and changed position
            mSnapshots.remove(change.getOldIndex());
            mSnapshots.add(change.getNewIndex(), change.getDocument());
            notifyItemMoved(change.getOldIndex(), change.getNewIndex());
        }
    }

    protected void onDocumentRemoved(DocumentChange change) {
        mSnapshots.remove(change.getOldIndex());
        notifyItemRemoved(change.getOldIndex());
    }

ثم استدعاء هذه الأساليب الجديدة من onEvent :

    @Override
    public void onEvent(QuerySnapshot documentSnapshots,
                        FirebaseFirestoreException e) {

        // ...

        // Dispatch the event
        for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
            // Snapshot of the changed document
            DocumentSnapshot snapshot = change.getDocument();

            switch (change.getType()) {
                case ADDED:
                    onDocumentAdded(change); // Add this line
                    break;
                case MODIFIED:
                    onDocumentModified(change); // Add this line
                    break;
                case REMOVED:
                    onDocumentRemoved(change); // Add this line
                    break;
            }
        }

        onDataChanged();
    }

وأخيرا تنفيذ startListening() طريقة لإرفاق المستمع:

    public void startListening() {
        if (mQuery != null && mRegistration == null) {
            mRegistration = mQuery.addSnapshotListener(this);
        }
    }

الآن تم تكوين التطبيق بالكامل لقراءة البيانات من Firestore. تشغيل التطبيق مرة أخرى وسترى المطاعم قمت بإضافته في الخطوة السابقة:

9e45f40faefce5d0.png

عد الآن إلى Emulator UI في متصفحك وقم بتحرير أحد أسماء المطاعم. يجب أن ترى أنه يتغير في التطبيق على الفور تقريبًا!

8. فرز البيانات وتصفيتها

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

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

67898572a35672a5.png

تحرير دعونا في onFilter() طريقة MainActivity.java . هذا الأسلوب يقبل Filters الكائن الذي هو كائن مساعد أنشأنا لالتقاط إخراج الحوار المرشحات. سنقوم بتغيير هذه الطريقة لإنشاء استعلام من المرشحات:

    @Override
    public void onFilter(Filters filters) {
        // Construct query basic query
        Query query = mFirestore.collection("restaurants");

        // Category (equality filter)
        if (filters.hasCategory()) {
            query = query.whereEqualTo("category", filters.getCategory());
        }

        // City (equality filter)
        if (filters.hasCity()) {
            query = query.whereEqualTo("city", filters.getCity());
        }

        // Price (equality filter)
        if (filters.hasPrice()) {
            query = query.whereEqualTo("price", filters.getPrice());
        }

        // Sort by (orderBy with direction)
        if (filters.hasSortBy()) {
            query = query.orderBy(filters.getSortBy(), filters.getSortDirection());
        }

        // Limit items
        query = query.limit(LIMIT);

        // Update the query
        mQuery = query;
        mAdapter.setQuery(query);

        // Set header
        mCurrentSearchView.setText(Html.fromHtml(filters.getSearchDescription(this)));
        mCurrentSortByView.setText(filters.getOrderDescription(this));

        // Save filters
        mViewModel.setFilters(filters);
    }

في المقتطف أعلاه نبني Query الكائن عن طريق ربط where و orderBy بنود لتتناسب مع مرشحات معينة.

تشغيل التطبيق مرة أخرى وحدد التصفية التالية لإظهار معظم المطاعم الشعبية المنخفضة للسعر:

7a67a8a400c80c50.png

يجب أن تشاهد الآن قائمة تمت تصفيتها من المطاعم تحتوي على خيارات السعر المنخفض فقط:

a670188398c3c59.png

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

9. تنظيم البيانات في مجموعات فرعية

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

المجموعات والمجموعات الفرعية

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

الوصول إلى المجموعة الفرعية، دعوة .collection() على الوثيقة الأصلية:

CollectionReference subRef = mFirestore.collection("restaurants")
        .document("abc123")
        .collection("ratings");

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

كتابة البيانات في المعاملة

إضافة Rating إلى المجموعة الفرعية المناسبة يتطلب سوى الدعوة .add() ، لكننا بحاجة أيضا لتحديث Restaurant متوسط تقييم الكائن وعدد من الدرجات لتعكس بيانات جديدة. إذا استخدمنا عمليات منفصلة لإجراء هذين التغييرين ، فهناك عدد من ظروف السباق التي قد تؤدي إلى بيانات قديمة أو غير صحيحة.

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

  • اقرأ التصنيف الحالي للمطعم واحسب التصنيف الجديد
  • أضف التصنيف إلى المجموعة الفرعية
  • قم بتحديث متوسط ​​تصنيف المطعم وعدد التقييمات

مفتوحة RestaurantDetailActivity.java وتنفيذ addRating وظيفة:

    private Task<Void> addRating(final DocumentReference restaurantRef,
                                 final Rating rating) {
        // Create reference for new rating, for use inside the transaction
        final DocumentReference ratingRef = restaurantRef.collection("ratings")
                .document();

        // In a transaction, add the new rating and update the aggregate totals
        return mFirestore.runTransaction(new Transaction.Function<Void>() {
            @Override
            public Void apply(Transaction transaction)
                    throws FirebaseFirestoreException {

                Restaurant restaurant = transaction.get(restaurantRef)
                        .toObject(Restaurant.class);

                // Compute new number of ratings
                int newNumRatings = restaurant.getNumRatings() + 1;

                // Compute new average rating
                double oldRatingTotal = restaurant.getAvgRating() *
                        restaurant.getNumRatings();
                double newAvgRating = (oldRatingTotal + rating.getRating()) /
                        newNumRatings;

                // Set new restaurant info
                restaurant.setNumRatings(newNumRatings);
                restaurant.setAvgRating(newAvgRating);

                // Commit to Firestore
                transaction.set(restaurantRef, restaurant);
                transaction.set(ratingRef, rating);

                return null;
            }
        });
    }

و addRating() الدالة بإرجاع Task التي تمثل المعاملة بالكامل. في onRating() تضاف المستمعين وظيفة للمهمة للرد على نتيجة المعاملة.

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

78fa16cdf8ef435a.png

ضرب إرسال ستنطلق المعاملة. عند اكتمال المعاملة ، سترى تقييمك معروضًا أدناه وتحديثًا لعدد تعليقات المطعم:

f9e670f40bd615b0.png

تهاني! لديك الآن تطبيق مراجعة مطعم اجتماعي ومحلي ومحمول مبني على Cloud Firestore. سمعت أن هذه هي شعبية جدا هذه الأيام.

10. تأمين البيانات الخاصة بك

حتى الآن لم نفكر في أمان هذا التطبيق. كيف نعرف أن المستخدمين يمكنهم قراءة وكتابة البيانات الخاصة بهم فقط؟ يتم تأمينها Firestore datbases بواسطة ملف التكوين تسمى قواعد الأمن .

فتح 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;
    }
  }
}

تغيير دعونا هذه القواعد لمنع البيانات غير المرغوب فيها acesss أو التغييرات، فتح 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 consle انتقل إلى القسم مصادقة وانتقل إلى الدخول في التبويب مقدمي .

قم بتمكين طريقة تسجيل الدخول بالبريد الإلكتروني:

334ef7f6ff4da4ce.png

فايرستور

إنشاء قاعدة بيانات

انتقل إلى قسم Firestore من وحدة التحكم وانقر فوق إنشاء قاعدة بيانات:

  1. عند المطالبة حول اختيار قواعد الأمن أن تبدأ في وضع مغلق، سنقوم بتحديث هذه القواعد في وقت قريب.
  2. اختر موقع قاعدة البيانات الذي ترغب في استخدامه لتطبيقك. لاحظ أن اختيار موقع قاعدة البيانات هو قرار دائم ولتغييره سيكون لديك لإنشاء مشروع جديد. لمزيد من المعلومات حول اختيار موقع المشروع، راجع الوثائق .

نشر القواعد

لنشر قواعد الأمان التي كتبتها سابقًا ، قم بتشغيل الأمر التالي في دليل 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.

تكوين التطبيق

في FirebaseUtil الدرجة نحن تكوين SDK Firebase للاتصال محاكاة عندما تكون في وضع التصحيح:

public class FirebaseUtil {

    /** Use emulators only in debug builds **/
    private static final boolean sUseEmulators = BuildConfig.DEBUG;

    // ...
}

إذا كنت ترغب في اختبار تطبيقك باستخدام مشروع Firebase الحقيقي ، فيمكنك إما:

  1. أنشئ التطبيق في وضع الإصدار وقم بتشغيله على الجهاز.
  2. مؤقتا تغيير sUseEmulators ل false وتشغيل التطبيق مرة أخرى.

ملاحظة أنك قد تحتاج إلى تسجيل الخروج من التطبيق وتسجيل الدخول مرة أخرى من أجل الاتصال بشكل صحيح إلى الإنتاج.