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

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

লক্ষ্য

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

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

পূর্বশর্ত

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

  • অ্যান্ড্রয়েড স্টুডিও ফ্লেমিঙ্গো বা নতুন
  • API 19 বা তার বেশি ভার্সন সহ একটি অ্যান্ড্রয়েড এমুলেটর
  • Node.js সংস্করণ ১৬ বা তার বেশি
  • জাভা সংস্করণ ১৭ বা তার বেশি

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

  1. আপনার গুগল অ্যাকাউন্ট ব্যবহার করে ফায়ারবেস কনসোলে সাইন ইন করুন।
  2. একটি নতুন প্রকল্প তৈরি করতে বোতামটি ক্লিক করুন, এবং তারপর একটি প্রকল্পের নাম লিখুন (উদাহরণস্বরূপ, FriendlyEats )।
  3. চালিয়ে যান ক্লিক করুন।
  4. যদি অনুরোধ করা হয়, তাহলে Firebase শর্তাবলী পর্যালোচনা করুন এবং গ্রহণ করুন, এবং তারপর Continue এ ক্লিক করুন।
  5. (ঐচ্ছিক) Firebase কনসোলে ("Gemini in Firebase" নামে পরিচিত) AI সহায়তা সক্ষম করুন।
  6. এই কোডল্যাবের জন্য, আপনার গুগল অ্যানালিটিক্সের প্রয়োজন নেই , তাই গুগল অ্যানালিটিক্স বিকল্পটি টগল করে বন্ধ করে দিন
  7. Create project এ ক্লিক করুন, আপনার province করার জন্য অপেক্ষা করুন, এবং তারপর Continue এ ক্লিক করুন।

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

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

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

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

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

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

  1. Firebase কনসোলে , বাম দিকের নেভিগেশনে Project Overview নির্বাচন করুন। প্ল্যাটফর্ম নির্বাচন করতে Android বোতামে ক্লিক করুন। প্যাকেজের নাম জিজ্ঞাসা করা হলে com.google.firebase.example.fireeats ব্যবহার করুন।

73d151ed16016421.png সম্পর্কে

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

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

অ্যান্ড্রয়েড স্টুডিও খুলুন। ফাইল > নতুন > আমদানি প্রকল্পে ক্লিক করুন এবং friendlyeats-android ফোল্ডারটি নির্বাচন করুন।

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

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

ফায়ারবেস সিএলআই ইনস্টল করুন

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

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

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

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

$ firebase --version
9.0.0

লগ ইন

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

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

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

এবার প্রথমবারের মতো 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.

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

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

অ্যান্ড্রয়েড স্টুডিওতে util/FirestoreInitializer.kt এবং util/AuthInitializer.kt ফাইলগুলি খুলুন। অ্যাপ্লিকেশন শুরু হওয়ার পরে, এই ফাইলগুলিতে আপনার মেশিনে চলমান স্থানীয় এমুলেটরগুলির সাথে Firebase 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 কে স্থানীয় Firestore এমুলেটরের সাথে সংযুক্ত করার জন্য useEmulator(host, port) পদ্ধতি ব্যবহার করছে। অ্যাপ জুড়ে আমরা FirebaseUtil.getFirestore() ব্যবহার করে FirebaseFirestore এর এই ইনস্ট্যান্সটি অ্যাক্সেস করব যাতে আমরা নিশ্চিত হতে পারি যে debug মোডে চলাকালীন আমরা সর্বদা Firestore এমুলেটরের সাথে সংযুক্ত থাকছি।

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

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

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

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

Firebase Auth Emulator সম্পর্কে

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

de06424023ffb4b9.png সম্পর্কে

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

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

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

আমাদের অ্যাপের মূল মডেল অবজেক্ট হল একটি রেস্তোরাঁ ( model/Restaurant.kt দেখুন)। ফায়ারস্টোর ডেটা ডকুমেন্ট, সংগ্রহ এবং উপ-সংগ্রহে বিভক্ত। আমরা প্রতিটি রেস্তোরাঁকে "restaurants" নামক একটি শীর্ষ-স্তরের সংগ্রহে একটি নথি হিসাবে সংরক্ষণ করব। ফায়ারস্টোর ডেটা মডেল সম্পর্কে আরও জানতে, ডকুমেন্টেশনে ডকুমেন্ট এবং সংগ্রহ সম্পর্কে পড়ুন।

প্রদর্শনের উদ্দেশ্যে, আমরা অ্যাপটিতে কার্যকারিতা যোগ করব যাতে ওভারফ্লো মেনুতে "Add Random Items" বোতামে ক্লিক করলে দশটি র‍্যান্ডম রেস্তোরাঁ তৈরি করা যায়। 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 ট্যাবে ক্লিক করুন এবং আপনি আপনার যোগ করা ডেটা দেখতে পাবেন:

Firebase Auth Emulator সম্পর্কে

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

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

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

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

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

৯. উপ-সংগ্রহে ডেটা সংগঠিত করুন

এই বিভাগে আমরা অ্যাপটিতে রেটিং যোগ করব যাতে ব্যবহারকারীরা তাদের প্রিয় (অথবা সবচেয়ে কম প্রিয়) রেস্তোরাঁগুলি পর্যালোচনা করতে পারেন।

সংগ্রহ এবং উপ-সংগ্রহ

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

একটি সাব-কালেকশন অ্যাক্সেস করতে, প্যারেন্ট ডকুমেন্টে .collection() কল করুন:

val subRef = firestore.collection("restaurants")
        .document("abc123")
        .collection("ratings")

আপনি একটি সাব-কালেকশন অ্যাক্সেস করতে এবং জিজ্ঞাসা করতে পারেন ঠিক যেমন একটি শীর্ষ-স্তরের সংগ্রহের ক্ষেত্রে হয়, কোনও আকার সীমাবদ্ধতা বা কর্মক্ষমতা পরিবর্তন নেই। আপনি এখানে ফায়ারস্টোর ডেটা মডেল সম্পর্কে আরও পড়তে পারেন।

লেনদেনে তথ্য লেখা

সঠিক উপ-সংগ্রহে একটি 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 সম্পর্কে

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

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

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

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

এই নিয়মগুলি অ্যাক্সেস সীমাবদ্ধ করে যাতে ক্লায়েন্টরা কেবল নিরাপদ পরিবর্তন করতে পারে। উদাহরণস্বরূপ, একটি রেস্তোরাঁর নথিতে আপডেট শুধুমাত্র রেটিং পরিবর্তন করতে পারে, নাম বা অন্য কোনও অপরিবর্তনীয় ডেটা নয়। ব্যবহারকারীর আইডি সাইন-ইন করা ব্যবহারকারীর সাথে মিলে গেলেই কেবল রেটিং তৈরি করা যেতে পারে, যা স্পুফিং প্রতিরোধ করে।

নিরাপত্তা নিয়ম সম্পর্কে আরও পড়তে, ডকুমেন্টেশন দেখুন।

১১. উপসংহার

আপনি এখন Firestore-এর উপরে একটি সম্পূর্ণ বৈশিষ্ট্যযুক্ত অ্যাপ তৈরি করেছেন। আপনি Firestore-এর সবচেয়ে গুরুত্বপূর্ণ বৈশিষ্ট্যগুলি সম্পর্কে শিখেছেন যার মধ্যে রয়েছে:

  • নথি এবং সংগ্রহ
  • তথ্য পড়া এবং লেখা
  • কোয়েরি অনুসারে বাছাই এবং ফিল্টারিং
  • উপ-সংগ্রহ
  • লেনদেন

আরও জানুন

ফায়ারস্টোর সম্পর্কে জানতে, শুরু করার জন্য এখানে কিছু ভালো জায়গা দেওয়া হল:

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

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

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

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

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

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

Firebase কনসোলে Authentication বিভাগে যান এবং Get started এ ক্লিক করুন। Sign-in method ট্যাবে যান এবং Native providers থেকে Email/Password বিকল্পটি নির্বাচন করুন।

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

সাইন-ইন-প্রোভাইডার.পিএনজি

ফায়ারস্টোর

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

কনসোলের Firestore Database বিভাগে যান এবং Create Database এ ক্লিক করুন:

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

নিয়মাবলী স্থাপন করুন

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

$ firebase deploy --only firestore:rules

এটি আপনার প্রকল্পে firestore.rules এর বিষয়বস্তু স্থাপন করবে, যা আপনি কনসোলের 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 দিয়ে প্রতিস্থাপন করুন এবং অ্যাপটি আবার চালান।

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