ক্লাউড ফায়ারস্টোর অ্যান্ড্রয়েড কোডল্যাব

1। সংক্ষিপ্ত বিবরণ

গোল

এই কোডল্যাবে আপনি ক্লাউড ফায়ারস্টোর দ্বারা সমর্থিত অ্যান্ড্রয়েডে একটি রেস্তোঁরা সুপারিশ অ্যাপ তৈরি করবেন। আপনি শিখবেন কিভাবে:

  • একটি Android অ্যাপ থেকে Firestore-এ ডেটা পড়ুন এবং লিখুন
  • রিয়েলটাইমে Firestore ডেটার পরিবর্তনগুলি শুনুন
  • Firestore ডেটা সুরক্ষিত করতে Firebase প্রমাণীকরণ এবং নিরাপত্তা নিয়ম ব্যবহার করুন
  • জটিল Firestore প্রশ্ন লিখুন

পূর্বশর্ত

এই কোডল্যাব শুরু করার আগে নিশ্চিত করুন যে আপনার আছে:

  • অ্যান্ড্রয়েড স্টুডিও ফ্ল্যামিঙ্গো বা নতুন
  • API 19 বা উচ্চতর সহ একটি Android এমুলেটর
  • Node.js সংস্করণ 16 বা উচ্চতর
  • জাভা সংস্করণ 17 বা উচ্চতর

2. একটি ফায়ারবেস প্রকল্প তৈরি করুন৷

  1. আপনার Google অ্যাকাউন্ট দিয়ে Firebase কনসোলে সাইন ইন করুন।
  2. Firebase কনসোলে , প্রজেক্ট যোগ করুন ক্লিক করুন।
  3. নীচের স্ক্রীন ক্যাপচারে যেমন দেখানো হয়েছে, আপনার ফায়ারবেস প্রকল্পের জন্য একটি নাম লিখুন (উদাহরণস্বরূপ, "ফ্রেন্ডলি ইটস"), এবং চালিয়ে যান ক্লিক করুন।

9d2f625aebcab6af.png

  1. আপনাকে Google Analytics সক্ষম করতে বলা হতে পারে, এই কোডল্যাবের উদ্দেশ্যে আপনার নির্বাচন কোন ব্যাপার না।
  2. এক মিনিট বা তার পরে, আপনার ফায়ারবেস প্রকল্প প্রস্তুত হয়ে যাবে। অবিরত ক্লিক করুন.

3. নমুনা প্রকল্প সেট আপ করুন

কোডটি ডাউনলোড করুন

এই কোডল্যাবের নমুনা কোড ক্লোন করতে নিম্নলিখিত কমান্ডটি চালান। এটি আপনার মেশিনে friendlyeats-android নামে একটি ফোল্ডার তৈরি করবে:

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

আপনার মেশিনে গিট না থাকলে, আপনি সরাসরি GitHub থেকে কোডটি ডাউনলোড করতে পারেন।

Firebase কনফিগারেশন যোগ করুন

  1. Firebase কনসোলে , বাম নেভিতে প্রজেক্ট ওভারভিউ নির্বাচন করুন। প্লাটফর্ম নির্বাচন করতে অ্যান্ড্রয়েড বোতামে ক্লিক করুন। প্যাকেজ নামের জন্য অনুরোধ করা হলে com.google.firebase.example.fireeats ব্যবহার করুন

73d151ed16016421.png

  1. রেজিস্টার অ্যাপ- এ ক্লিক করুন এবং google-services.json ফাইলটি ডাউনলোড করতে নির্দেশাবলী অনুসরণ করুন এবং আপনি যে কোডটি ডাউনলোড করেছেন তার app/ ফোল্ডারে নিয়ে যান। তারপর Next এ ক্লিক করুন।

প্রকল্পটি আমদানি করুন

অ্যান্ড্রয়েড স্টুডিও খুলুন। File > New > Import Project এ ক্লিক করুন এবং friendlyeats-android ফোল্ডার নির্বাচন করুন।

4. ফায়ারবেস এমুলেটর সেট আপ করুন

এই কোডল্যাবে আপনি স্থানীয়ভাবে ক্লাউড ফায়ারস্টোর এবং অন্যান্য ফায়ারবেস পরিষেবাগুলিকে অনুকরণ করতে Firebase এমুলেটর স্যুট ব্যবহার করবেন৷ এটি আপনার অ্যাপ তৈরি করার জন্য একটি নিরাপদ, দ্রুত এবং বিনা খরচে স্থানীয় উন্নয়ন পরিবেশ প্রদান করে।

Firebase CLI ইনস্টল করুন

প্রথমে আপনাকে Firebase CLI ইন্সটল করতে হবে। আপনি যদি ম্যাকোস বা লিনাক্স ব্যবহার করেন তবে আপনি নিম্নলিখিত সিআরএল কমান্ডটি চালাতে পারেন:

curl -sL https://firebase.tools | bash

আপনি যদি উইন্ডোজ ব্যবহার করেন তবে একটি স্বতন্ত্র বাইনারি পেতে বা npm এর মাধ্যমে ইনস্টল করতে ইনস্টলেশন নির্দেশাবলী পড়ুন।

একবার আপনি CLI ইন্সটল করে ফেললে, firebase --version 9.0.0 বা উচ্চতর সংস্করণের রিপোর্ট করা উচিত:

$ firebase --version
9.0.0

প্রবেশ করুন

আপনার Google অ্যাকাউন্টে CLI সংযোগ করতে firebase login চালান৷ এটি লগইন প্রক্রিয়া সম্পূর্ণ করতে একটি নতুন ব্রাউজার উইন্ডো খুলবে। আপনার ফায়ারবেস প্রকল্পটি আগে তৈরি করার সময় আপনি যে অ্যাকাউন্টটি ব্যবহার করেছিলেন সেটি বেছে নেওয়ার বিষয়টি নিশ্চিত করুন।

friendlyeats-android ফোল্ডারের মধ্যে থেকে firebase use --add স্থানীয় প্রজেক্টকে আপনার ফায়ারবেস প্রোজেক্টে সংযুক্ত করতে যোগ করুন। আপনি আগে তৈরি করা প্রকল্পটি নির্বাচন করতে প্রম্পটগুলি অনুসরণ করুন এবং যদি একটি উপনাম নির্বাচন করতে বলা হয় তবে default এন্টার করুন।

5. অ্যাপটি চালান

এখন প্রথমবার Firebase এমুলেটর স্যুট এবং FriendlyEats Android অ্যাপ চালানোর সময়।

এমুলেটর চালান

আপনার টার্মিনালে friendlyeats-android ডিরেক্টরির মধ্যে থেকে firebase emulators:start । আপনি এই মত লগ দেখতে হবে:

$ 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.

আপনি এখন আপনার মেশিনে চলমান একটি সম্পূর্ণ স্থানীয় উন্নয়ন পরিবেশ আছে! বাকি কোডল্যাবের জন্য এই কমান্ডটি চলমান থাকার বিষয়টি নিশ্চিত করুন, আপনার অ্যান্ড্রয়েড অ্যাপটিকে এমুলেটরগুলির সাথে সংযোগ করতে হবে।

এমুলেটরগুলির সাথে অ্যাপটি সংযুক্ত করুন

অ্যান্ড্রয়েড স্টুডিওতে util/FirestoreInitializer.kt এবং util/AuthInitializer.kt ফাইলগুলি খুলুন। এই ফাইলগুলিতে ফায়ারবেস SDKগুলিকে আপনার মেশিনে চলমান স্থানীয় এমুলেটরগুলির সাথে সংযুক্ত করার যুক্তি রয়েছে, অ্যাপ্লিকেশন শুরু হওয়ার পরে৷

FirestoreInitializer ক্লাসের create() পদ্ধতিতে, কোডের এই অংশটি পরীক্ষা করুন:

    // Use emulators only in debug builds
    if (BuildConfig.DEBUG) {
        firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
    }

আমরা BuildConfig ব্যবহার করছি তা নিশ্চিত করতে যে আমরা যখন আমাদের অ্যাপটি debug মোডে চলছে তখনই আমরা এমুলেটরগুলির সাথে সংযোগ করি৷ আমরা যখন release মোডে অ্যাপ কম্পাইল করি তখন এই শর্তটি মিথ্যা হবে।

আমরা দেখতে পাচ্ছি যে এটি স্থানীয় ফায়ারস্টোর এমুলেটরের সাথে Firebase SDK সংযোগ করতে useEmulator(host, port) পদ্ধতি ব্যবহার করছে। অ্যাপ জুড়ে আমরা FirebaseUtil.getFirestore() ব্যবহার করব FirebaseFirestore এর এই উদাহরণটি অ্যাক্সেস করার জন্য যাতে আমরা নিশ্চিত যে আমরা সবসময় Firestore এমুলেটরের সাথে সংযোগ রাখছি যখন debug মোডে চলছে।

অ্যাপটি চালান

আপনি যদি google-services.json ফাইলটি সঠিকভাবে যোগ করে থাকেন, তাহলে প্রকল্পটি এখন কম্পাইল করা উচিত। অ্যান্ড্রয়েড স্টুডিওতে বিল্ড > পুনর্নির্মাণ প্রকল্পে ক্লিক করুন এবং নিশ্চিত করুন যে কোনও অবশিষ্ট ত্রুটি নেই।

অ্যান্ড্রয়েড স্টুডিওতে আপনার অ্যান্ড্রয়েড এমুলেটরে অ্যাপটি চালান । প্রথমে আপনাকে একটি "সাইন ইন" স্ক্রীন দেওয়া হবে। অ্যাপে সাইন ইন করতে আপনি যেকোনো ইমেল এবং পাসওয়ার্ড ব্যবহার করতে পারেন। এই সাইন ইন প্রক্রিয়াটি Firebase প্রমাণীকরণ এমুলেটরের সাথে সংযুক্ত হচ্ছে, তাই কোনো প্রকৃত শংসাপত্র প্রেরণ করা হচ্ছে না।

এখন আপনার ওয়েব ব্রাউজারে http://localhost:4000 এ নেভিগেট করে Emulators UI খুলুন। তারপরে প্রমাণীকরণ ট্যাবে ক্লিক করুন এবং আপনি যে অ্যাকাউন্টটি তৈরি করেছেন তা দেখতে হবে:

ফায়ারবেস প্রমাণীকরণ এমুলেটর

একবার আপনি সাইন ইন প্রক্রিয়া সম্পন্ন করলে আপনাকে অ্যাপের হোম স্ক্রীন দেখতে হবে:

de06424023ffb4b9.png

শীঘ্রই আমরা হোম স্ক্রীনে কিছু তথ্য যোগ করব।

6. ফায়ারস্টোরে ডেটা লিখুন

এই বিভাগে আমরা ফায়ারস্টোরে কিছু ডেটা লিখব যাতে আমরা বর্তমানে খালি হোম স্ক্রীনটি পূরণ করতে পারি।

আমাদের অ্যাপের প্রধান মডেল অবজেক্ট হল একটি রেস্তোরাঁ (দেখুন model/Restaurant.kt )। ফায়ারস্টোর ডেটা নথি, সংগ্রহ এবং উপ-সংগ্রহগুলিতে বিভক্ত। আমরা প্রতিটি রেস্তোরাঁকে "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" সংগ্রহের একটি রেফারেন্স পেয়ে শুরু করেছি। নথিগুলি যোগ করার সময় সংগ্রহগুলি অন্তর্নিহিতভাবে তৈরি করা হয়, তাই ডেটা লেখার আগে সংগ্রহ তৈরি করার দরকার ছিল না।
  • কোটলিন ডেটা ক্লাস ব্যবহার করে নথি তৈরি করা যেতে পারে, যা আমরা প্রতিটি রেস্টুরেন্ট ডক তৈরি করতে ব্যবহার করি।
  • add() পদ্ধতিটি একটি স্বয়ংক্রিয়-উত্পন্ন আইডি সহ একটি সংগ্রহে একটি নথি যোগ করে, তাই আমাদের প্রতিটি রেস্টুরেন্টের জন্য একটি অনন্য আইডি নির্দিষ্ট করার প্রয়োজন নেই।

এখন আবার অ্যাপটি চালান এবং আপনার লেখা কোডটি চালু করতে ওভারফ্লো মেনুতে (উপরের ডানদিকের কোণায়) "র্যান্ডম আইটেম যোগ করুন" বোতামে ক্লিক করুন:

95691e9b71ba55e3.png

এখন আপনার ওয়েব ব্রাউজারে http://localhost:4000 এ নেভিগেট করে Emulators UI খুলুন। তারপর Firestore ট্যাবে ক্লিক করুন এবং আপনি এইমাত্র যোগ করা ডেটা দেখতে পাবেন:

ফায়ারবেস প্রমাণীকরণ এমুলেটর

এই ডেটা আপনার মেশিনে 100% স্থানীয়। আসলে, আপনার আসল প্রজেক্টে এখনও ফায়ারস্টোর ডাটাবেস নেই! এর মানে হল ফলাফল ছাড়াই এই ডেটা পরিবর্তন এবং মুছে ফেলার সাথে পরীক্ষা করা নিরাপদ।

অভিনন্দন, আপনি এইমাত্র Firestore-এ ডেটা লিখেছেন! পরবর্তী ধাপে আমরা শিখব কিভাবে অ্যাপটিতে এই ডেটা প্রদর্শন করতে হয়।

7. ফায়ারস্টোর থেকে ডেটা প্রদর্শন করুন

এই ধাপে আমরা শিখব কিভাবে 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)
        }
    }

এখন ফায়ারস্টোর থেকে ডেটা পড়ার জন্য অ্যাপটি সম্পূর্ণরূপে কনফিগার করা হয়েছে। অ্যাপটি আবার চালান এবং আগের ধাপে আপনি যে রেস্তোরাঁগুলি যোগ করেছেন তা দেখতে পাবেন:

9e45f40faefce5d0.png

এখন আপনার ব্রাউজারে এমুলেটর UI এ ফিরে যান এবং রেস্টুরেন্টের নামগুলির একটি সম্পাদনা করুন৷ আপনি প্রায় সঙ্গে সঙ্গে অ্যাপে এটি পরিবর্তন দেখতে হবে!

8. ডেটা সাজান এবং ফিল্টার করুন

অ্যাপটি বর্তমানে সমগ্র সংগ্রহ জুড়ে শীর্ষ-রেটেড রেস্তোরাঁগুলি প্রদর্শন করে, তবে একটি বাস্তব রেস্তোরাঁ অ্যাপে ব্যবহারকারী ডেটা সাজাতে এবং ফিল্টার করতে চান। উদাহরণস্বরূপ, অ্যাপটি "ফিলাডেলফিয়ার শীর্ষ সামুদ্রিক খাবারের রেস্তোরাঁ" বা "সর্বনিম্ন ব্যয়বহুল পিজা" দেখাতে সক্ষম হওয়া উচিত।

অ্যাপের শীর্ষে সাদা বারে ক্লিক করা একটি ফিল্টার ডায়ালগ নিয়ে আসে। এই বিভাগে আমরা এই ডায়ালগ কাজ করার জন্য Firestore প্রশ্নগুলি ব্যবহার করব:

67898572a35672a5.png

আসুন MainFragment.kt এর onFilter() পদ্ধতিটি সম্পাদনা করি। এই পদ্ধতিটি একটি 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
    }

উপরের স্নিপেটে আমরা প্রদত্ত ফিল্টারগুলির সাথে মিল করার জন্য where এবং orderBy ধারা সংযুক্ত করে একটি Query অবজেক্ট তৈরি করি।

অ্যাপটি আবার চালান এবং সবচেয়ে জনপ্রিয় কম দামের রেস্তোরাঁগুলি দেখাতে নিম্নলিখিত ফিল্টারটি নির্বাচন করুন:

7a67a8a400c80c50.png

আপনি এখন শুধুমাত্র কম দামের বিকল্প ধারণকারী রেস্টুরেন্টগুলির একটি ফিল্টার করা তালিকা দেখতে পাবেন:

a670188398c3c59.png

আপনি এখন পর্যন্ত এটি তৈরি করে থাকলে, আপনি এখন ফায়ারস্টোরে একটি সম্পূর্ণ কার্যকরী রেস্তোরাঁর সুপারিশ দেখার অ্যাপ তৈরি করেছেন! আপনি এখন রিয়েল টাইমে রেস্টুরেন্ট বাছাই এবং ফিল্টার করতে পারেন। পরবর্তী কয়েকটি বিভাগে আমরা রেস্তোরাঁগুলির পর্যালোচনা যোগ করব এবং অ্যাপে নিরাপত্তা নিয়মগুলি যোগ করব৷

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() ফাংশনে শ্রোতাদের লেনদেনের ফলাফলের প্রতিক্রিয়া জানাতে টাস্কে যুক্ত করা হয়।

এখন আবার অ্যাপটি চালান এবং রেস্তোঁরাগুলির একটিতে ক্লিক করুন, যা রেস্তোরাঁর বিশদ স্ক্রীন আনতে হবে। একটি পর্যালোচনা যোগ করা শুরু করতে + বোতামে ক্লিক করুন। বেশ কয়েকটি তারা বাছাই করে এবং কিছু পাঠ্য প্রবেশ করে একটি পর্যালোচনা যোগ করুন।

78fa16cdf8ef435a.png

সাবমিট টিপে লেনদেন শুরু হবে। লেনদেন সম্পূর্ণ হলে, আপনি নীচে প্রদর্শিত আপনার পর্যালোচনা এবং রেস্তোরাঁর পর্যালোচনা সংখ্যার একটি আপডেট দেখতে পাবেন:

f9e670f40bd615b0.png

অভিনন্দন! আপনার কাছে এখন ক্লাউড ফায়ারস্টোরে তৈরি একটি সামাজিক, স্থানীয়, মোবাইল রেস্তোঁরা পর্যালোচনা অ্যাপ রয়েছে। শুনছি যে আজকাল খুব জনপ্রিয়।

10. আপনার ডেটা সুরক্ষিত করুন

এখন পর্যন্ত আমরা এই অ্যাপ্লিকেশনটির নিরাপত্তা বিবেচনা করিনি। আমরা কিভাবে জানি যে ব্যবহারকারীরা শুধুমাত্র সঠিক নিজস্ব ডেটা পড়তে এবং লিখতে পারে? ফায়ারস্টোর ডাটাবেসগুলি সিকিউরিটি রুলস নামে একটি কনফিগারেশন ফাইল দ্বারা সুরক্ষিত।

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 সম্পর্কে শেখার জন্য, এখানে শুরু করার জন্য কিছু ভাল জায়গা রয়েছে:

এই কোডল্যাবের রেস্টুরেন্ট অ্যাপটি "ফ্রেন্ডলি ইটস" উদাহরণ অ্যাপ্লিকেশনের উপর ভিত্তি করে তৈরি করা হয়েছে। আপনি এখানে সেই অ্যাপের সোর্স কোড ব্রাউজ করতে পারেন।

ঐচ্ছিক: উৎপাদনে স্থাপন করুন

এখন পর্যন্ত এই অ্যাপটি শুধুমাত্র ফায়ারবেস এমুলেটর স্যুট ব্যবহার করেছে। আপনি যদি এই অ্যাপটিকে একটি বাস্তব ফায়ারবেস প্রকল্পে কীভাবে স্থাপন করতে হয় তা শিখতে চান তাহলে পরবর্তী ধাপে যান।

12. (ঐচ্ছিক) আপনার অ্যাপ স্থাপন করুন

এখন পর্যন্ত এই অ্যাপটি সম্পূর্ণ স্থানীয়, সমস্ত ডেটা Firebase এমুলেটর স্যুটে রয়েছে। এই বিভাগে আপনি শিখবেন কীভাবে আপনার ফায়ারবেস প্রকল্প কনফিগার করবেন যাতে এই অ্যাপটি উৎপাদনে কাজ করে।

ফায়ারবেস প্রমাণীকরণ

ফায়ারবেস কনসোলে প্রমাণীকরণ বিভাগে যান এবং শুরু করুন ক্লিক করুন। সাইন-ইন পদ্ধতি ট্যাবে নেভিগেট করুন এবং নেটিভ প্রদানকারীদের থেকে ইমেল/পাসওয়ার্ড বিকল্পটি নির্বাচন করুন।

ইমেল/পাসওয়ার্ড সাইন-ইন পদ্ধতি সক্ষম করুন এবং সংরক্ষণ করুন ক্লিক করুন।

sign-in-providers.png

ফায়ারস্টোর

ডাটাবেস তৈরি করুন

কনসোলের ফায়ারস্টোর ডেটাবেস বিভাগে নেভিগেট করুন এবং ডেটাবেস তৈরি করুন ক্লিক করুন:

  1. নিরাপত্তা বিধি সম্পর্কে অনুরোধ করা হলে প্রোডাকশন মোডে শুরু করতে বেছে নিন, আমরা শীঘ্রই সেই নিয়মগুলি আপডেট করব৷
  2. আপনি আপনার অ্যাপের জন্য যে ডাটাবেস অবস্থানটি ব্যবহার করতে চান তা চয়ন করুন৷ মনে রাখবেন যে একটি ডাটাবেস অবস্থান নির্বাচন একটি স্থায়ী সিদ্ধান্ত এবং এটি পরিবর্তন করতে আপনাকে একটি নতুন প্রকল্প তৈরি করতে হবে। একটি প্রকল্পের অবস্থান নির্বাচন করার বিষয়ে আরও তথ্যের জন্য, ডকুমেন্টেশন দেখুন।

বিধি স্থাপন করুন

আপনি আগে যে নিরাপত্তা বিধিগুলি লিখেছিলেন তা স্থাপন করতে, কোডল্যাব ডিরেক্টরিতে নিম্নলিখিত কমান্ডটি চালান:

$ 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
    }

আপনি যদি আপনার আসল ফায়ারবেস প্রকল্পের সাথে আপনার অ্যাপটি পরীক্ষা করতে চান তবে আপনি যেটি করতে পারেন:

  1. রিলিজ মোডে অ্যাপটি তৈরি করুন এবং এটি একটি ডিভাইসে চালান।
  2. অস্থায়ীভাবে BuildConfig.DEBUG false দিয়ে প্রতিস্থাপন করুন এবং অ্যাপটি আবার চালান।

মনে রাখবেন যে প্রোডাকশনের সাথে সঠিকভাবে সংযোগ করার জন্য আপনাকে অ্যাপ থেকে সাইন আউট করতে হবে এবং আবার সাইন ইন করতে হবে।