1. সংক্ষিপ্ত বিবরণ
লক্ষ্য
এই কোডল্যাবে আপনি ক্লাউড ফায়ারস্টোর দ্বারা সমর্থিত অ্যান্ড্রয়েডে একটি রেস্তোরাঁ সুপারিশ অ্যাপ তৈরি করবেন। আপনি শিখবেন কিভাবে:
- একটি অ্যান্ড্রয়েড অ্যাপ থেকে ফায়ারস্টোরে ডেটা পড়ুন এবং লিখুন
- রিয়েলটাইমে ফায়ারস্টোর ডেটার পরিবর্তনগুলি শুনুন
- ফায়ারস্টোর ডেটা সুরক্ষিত করতে ফায়ারবেস প্রমাণীকরণ এবং সুরক্ষা নিয়ম ব্যবহার করুন
- জটিল ফায়ারস্টোর কোয়েরি লিখুন
পূর্বশর্ত
এই কোডল্যাবটি শুরু করার আগে নিশ্চিত করুন যে আপনার কাছে আছে:
- অ্যান্ড্রয়েড স্টুডিও ফ্লেমিঙ্গো বা নতুন
- API 19 বা তার বেশি ভার্সন সহ একটি অ্যান্ড্রয়েড এমুলেটর
- Node.js সংস্করণ ১৬ বা তার বেশি
- জাভা সংস্করণ ১৭ বা তার বেশি
২. একটি ফায়ারবেস প্রকল্প তৈরি করুন
- আপনার গুগল অ্যাকাউন্ট ব্যবহার করে ফায়ারবেস কনসোলে সাইন ইন করুন।
- একটি নতুন প্রকল্প তৈরি করতে বোতামটি ক্লিক করুন, এবং তারপর একটি প্রকল্পের নাম লিখুন (উদাহরণস্বরূপ,
FriendlyEats)। - চালিয়ে যান ক্লিক করুন।
- যদি অনুরোধ করা হয়, তাহলে Firebase শর্তাবলী পর্যালোচনা করুন এবং গ্রহণ করুন, এবং তারপর Continue এ ক্লিক করুন।
- (ঐচ্ছিক) Firebase কনসোলে ("Gemini in Firebase" নামে পরিচিত) AI সহায়তা সক্ষম করুন।
- এই কোডল্যাবের জন্য, আপনার গুগল অ্যানালিটিক্সের প্রয়োজন নেই , তাই গুগল অ্যানালিটিক্স বিকল্পটি টগল করে বন্ধ করে দিন ।
- Create project এ ক্লিক করুন, আপনার province করার জন্য অপেক্ষা করুন, এবং তারপর Continue এ ক্লিক করুন।
৩. নমুনা প্রকল্পটি সেট আপ করুন
কোডটি ডাউনলোড করুন
এই কোডল্যাবের জন্য নমুনা কোড ক্লোন করতে নিম্নলিখিত কমান্ডটি চালান। এটি আপনার মেশিনে friendlyeats-android নামে একটি ফোল্ডার তৈরি করবে:
$ git clone https://github.com/firebase/friendlyeats-android
যদি আপনার মেশিনে গিট না থাকে, তাহলে আপনি সরাসরি গিটহাব থেকে কোডটি ডাউনলোড করতে পারেন।
Firebase কনফিগারেশন যোগ করুন
- Firebase কনসোলে , বাম দিকের নেভিগেশনে Project Overview নির্বাচন করুন। প্ল্যাটফর্ম নির্বাচন করতে Android বোতামে ক্লিক করুন। প্যাকেজের নাম জিজ্ঞাসা করা হলে
com.google.firebase.example.fireeatsব্যবহার করুন।

- 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 ট্যাবে ক্লিক করুন এবং আপনি আপনার তৈরি করা অ্যাকাউন্টটি দেখতে পাবেন:

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

শীঘ্রই আমরা হোম স্ক্রিনে কিছু ডেটা যোগ করব।
৬. ফায়ারস্টোরে ডেটা লিখুন
এই বিভাগে আমরা ফায়ারস্টোরে কিছু তথ্য লিখব যাতে আমরা বর্তমানে খালি হোম স্ক্রিনটি পূরণ করতে পারি।
আমাদের অ্যাপের মূল মডেল অবজেক্ট হল একটি রেস্তোরাঁ ( 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()পদ্ধতিটি একটি সংগ্রহে একটি স্বয়ংক্রিয়ভাবে তৈরি আইডি সহ একটি ডকুমেন্ট যোগ করে, তাই আমাদের প্রতিটি রেস্তোরাঁর জন্য একটি অনন্য আইডি নির্দিষ্ট করার প্রয়োজন হয়নি।
এখন অ্যাপটি আবার চালান এবং আপনার লেখা কোডটি ব্যবহার করতে ওভারফ্লো মেনুতে (উপরের ডান কোণে) "অ্যাড র্যান্ডম আইটেম" বোতামে ক্লিক করুন:

এবার আপনার ওয়েব ব্রাউজারে http://localhost:4000 এ নেভিগেট করে Emulators UI খুলুন। তারপর 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)
}
}
এখন অ্যাপটি ফায়ারস্টোর থেকে ডেটা পড়ার জন্য সম্পূর্ণরূপে কনফিগার করা হয়েছে। অ্যাপটি আবার চালান এবং আপনি আগের ধাপে যোগ করা রেস্তোরাঁগুলি দেখতে পাবেন:

এখন আপনার ব্রাউজারে এমুলেটর UI-তে ফিরে যান এবং রেস্তোরাঁর নামগুলির একটি সম্পাদনা করুন। আপনি অ্যাপটিতে প্রায় তাৎক্ষণিকভাবে এটি পরিবর্তন দেখতে পাবেন!
8. ডেটা বাছাই এবং ফিল্টার করুন
অ্যাপটি বর্তমানে সমগ্র সংগ্রহের শীর্ষ-রেটেড রেস্তোরাঁগুলি প্রদর্শন করে, কিন্তু একটি প্রকৃত রেস্তোরাঁ অ্যাপে ব্যবহারকারী ডেটা বাছাই এবং ফিল্টার করতে চাইবেন। উদাহরণস্বরূপ, অ্যাপটি "ফিলাডেলফিয়ার শীর্ষ সামুদ্রিক খাবারের রেস্তোরাঁ" বা "সর্বনিম্ন ব্যয়বহুল পিৎজা" দেখাতে সক্ষম হওয়া উচিত।
অ্যাপের উপরের সাদা বারে ক্লিক করলে একটি ফিল্টার ডায়ালগ আসবে। এই বিভাগে আমরা এই ডায়ালগটি কার্যকর করার জন্য Firestore কোয়েরি ব্যবহার করব:

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 অবজেক্ট তৈরি করি যাতে প্রদত্ত ফিল্টারগুলির সাথে মেলে।
অ্যাপটি আবার চালান এবং সবচেয়ে জনপ্রিয় কম দামের রেস্তোরাঁগুলি দেখানোর জন্য নিম্নলিখিত ফিল্টারটি নির্বাচন করুন:

এখন আপনি এমন রেস্তোরাঁর একটি ফিল্টার করা তালিকা দেখতে পাবেন যেখানে শুধুমাত্র কম দামের বিকল্প রয়েছে:

যদি আপনি এতদূর এসে পৌঁছে থাকেন, তাহলে আপনি এখন 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() ফাংশনে লেনদেনের ফলাফলের প্রতিক্রিয়া জানাতে শ্রোতাদের টাস্কে যুক্ত করা হয়।
এখন অ্যাপটি আবার চালান এবং যেকোনো একটি রেস্তোরাঁয় ক্লিক করুন, যা রেস্তোরাঁর বিস্তারিত স্ক্রিনটি দেখাবে। একটি পর্যালোচনা যোগ করা শুরু করতে + বোতামটি ক্লিক করুন। কয়েকটি তারকা বেছে নিয়ে এবং কিছু টেক্সট লিখে একটি পর্যালোচনা যোগ করুন।

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

অভিনন্দন! এখন আপনার কাছে ক্লাউড ফায়ারস্টোরে তৈরি একটি সামাজিক, স্থানীয়, মোবাইল রেস্তোরাঁ পর্যালোচনা অ্যাপ রয়েছে। আমি শুনেছি এগুলো আজকাল খুব জনপ্রিয়।
১০. আপনার ডেটা সুরক্ষিত করুন
এখন পর্যন্ত আমরা এই অ্যাপ্লিকেশনটির নিরাপত্তা বিবেচনা করিনি। আমরা কীভাবে জানব যে ব্যবহারকারীরা কেবল সঠিক তথ্যই পড়তে এবং লিখতে পারেন? ফায়ারস্টোর ডাটাবেসগুলি সিকিউরিটি রুলস নামক একটি কনফিগারেশন ফাইল দ্বারা সুরক্ষিত।
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 এ ক্লিক করুন:
- যখন নিরাপত্তা নিয়ম সম্পর্কে জিজ্ঞাসা করা হবে তখন প্রোডাকশন মোডে শুরু করার জন্য নির্বাচন করুন, আমরা শীঘ্রই সেই নিয়মগুলি আপডেট করব।
- আপনার অ্যাপের জন্য আপনি যে ডাটাবেস লোকেশনটি ব্যবহার করতে চান তা বেছে নিন। মনে রাখবেন যে ডাটাবেস লোকেশন নির্বাচন করা একটি স্থায়ী সিদ্ধান্ত এবং এটি পরিবর্তন করতে আপনাকে একটি নতুন প্রোজেক্ট তৈরি করতে হবে। প্রোজেক্ট লোকেশন নির্বাচন করার বিষয়ে আরও তথ্যের জন্য, ডকুমেন্টেশন দেখুন।
নিয়মাবলী স্থাপন করুন
আপনার আগে লেখা নিরাপত্তা নিয়মগুলি স্থাপন করতে, কোডল্যাব ডিরেক্টরিতে নিম্নলিখিত কমান্ডটি চালান:
$ 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
}
আপনি যদি আপনার আসল ফায়ারবেস প্রকল্পের সাথে আপনার অ্যাপটি পরীক্ষা করতে চান তবে আপনি নিম্নলিখিতগুলির মধ্যে একটি করতে পারেন:
- অ্যাপটি রিলিজ মোডে তৈরি করুন এবং এটি একটি ডিভাইসে চালান।
- অস্থায়ীভাবে
BuildConfig.DEBUGfalseদিয়ে প্রতিস্থাপন করুন এবং অ্যাপটি আবার চালান।
মনে রাখবেন যে প্রোডাকশনের সাথে সঠিকভাবে সংযোগ স্থাপনের জন্য আপনাকে অ্যাপ থেকে সাইন আউট করে আবার সাইন ইন করতে হতে পারে।