Cloud Firestore Android Codelab

1. Genel Bakış

Gol sayısı

Bu codelab'de, Cloud Firestore tarafından desteklenen bir Android restoran önerisi uygulaması oluşturacaksınız. Bu eğitimde şunları öğreneceksiniz:

  • Android uygulamasından Firestore'a veri okuma ve yazma
  • Firestore verilerindeki değişiklikleri anlık olarak dinleme
  • Firestore verilerinin güvenliğini sağlamak için Firebase Authentication ve güvenlik kurallarını kullanma
  • Karmaşık Firestore sorguları yazma

Ön koşullar

Bu codelab'e başlamadan önce şunlara sahip olduğunuzdan emin olun:

  • Android Studio Flamingo veya daha yeni bir sürüm
  • API düzeyi 19 veya sonraki sürümlerin yüklü olduğu bir Android emülatör
  • Node.js'in 16 veya sonraki bir sürümü
  • Java sürümü 17 veya sonraki sürümler

2. Firebase projesi oluşturma

  1. Google Hesabınızı kullanarak Firebase konsolunda oturum açın.
  2. Yeni bir proje oluşturmak için düğmeyi tıklayın ve ardından bir proje adı girin (örneğin, FriendlyEats).
  3. Devam'ı tıklayın.
  4. İstenirse Firebase şartlarını inceleyip kabul edin ve Devam'ı tıklayın.
  5. (İsteğe bağlı) Firebase konsolunda yapay zeka yardımını etkinleştirin ("Firebase'de Gemini" olarak adlandırılır).
  6. Bu codelab için Google Analytics'e ihtiyacınız yoktur. Bu nedenle, Google Analytics seçeneğini devre dışı bırakın.
  7. Proje oluştur'u tıklayın, projenizin hazırlanmasını bekleyin ve ardından Devam'ı tıklayın.

3. Örnek projeyi oluşturma

Kodu indirme

Bu codelab'in örnek kodunu klonlamak için aşağıdaki komutu çalıştırın. Bu işlem, makinenizde friendlyeats-android adlı bir klasör oluşturur:

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

Bilgisayarınızda git yoksa kodu doğrudan GitHub'dan da indirebilirsiniz.

Firebase yapılandırması ekleme

  1. Firebase konsolunda soldaki gezinme menüsünden Proje Genel Bakışı'nı seçin. Platformu seçmek için Android düğmesini tıklayın. Paket adı istendiğinde com.google.firebase.example.fireeats kullanın.

73d151ed16016421.png

  1. Register App'i (Uygulamayı Kaydet) tıklayın ve google-services.json dosyasını indirip yeni indirdiğiniz kodun app/ klasörüne taşıma talimatlarını uygulayın. Ardından Sonraki'yi tıklayın.

Projeyi içe aktarma

Android Studio'yu açın. Dosya > Yeni > Projeyi İçe Aktar'ı tıklayın ve friendlyeats-android klasörünü seçin.

4. Firebase Emulators'ı ayarlama

Bu codelab'de, Cloud Firestore'u ve diğer Firebase hizmetlerini yerel olarak emüle etmek için Firebase Emulator Suite'i kullanacaksınız. Bu, uygulamanızı oluşturmak için güvenli, hızlı ve ücretsiz bir yerel geliştirme ortamı sağlar.

Firebase CLI'yı yükleme

Öncelikle Firebase CLI'yı yüklemeniz gerekir. macOS veya Linux kullanıyorsanız aşağıdaki cURL komutunu çalıştırabilirsiniz:

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

Windows kullanıyorsanız bağımsız bir ikili dosya almak veya npm üzerinden yüklemek için yükleme talimatlarını okuyun.

CLI'yı yükledikten sonra firebase --version komutunu çalıştırdığınızda 9.0.0 veya sonraki bir sürüm raporlanmalıdır:

$ firebase --version
9.0.0

Giriş Yap

CLI'yı Google Hesabınıza bağlamak için firebase login komutunu çalıştırın. Bu işlem, oturum açma sürecini tamamlamak için yeni bir tarayıcı penceresi açar. Daha önce Firebase projenizi oluştururken kullandığınız hesabı seçtiğinizden emin olun.

Yerel projenizi Firebase projenize bağlamak için friendlyeats-android klasöründe firebase use --add komutunu çalıştırın. Daha önce oluşturduğunuz projeyi seçmek için istemleri uygulayın ve bir takma ad seçmeniz istenirse default girin.

5. Uygulamayı çalıştırma

Şimdi Firebase Emulator Suite'i ve FriendlyEats Android uygulamasını ilk kez çalıştırma zamanı.

Emülatörleri çalıştırma

Firebase Emulators'ı başlatmak için friendlyeats-android dizinindeki terminalinizde firebase emulators:start komutunu çalıştırın. Aşağıdaki gibi günlükler görmeniz gerekir:

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

Artık makinenizde çalışan eksiksiz bir yerel geliştirme ortamınız var. Bu komutun codelab'in geri kalanı boyunca çalışmaya devam ettiğinden emin olun. Android uygulamanızın emülatörlere bağlanması gerekir.

Uygulamayı emülatörlere bağlama

Android Studio'da util/FirestoreInitializer.kt ve util/AuthInitializer.kt dosyalarını açın. Bu dosyalar, uygulama başlatıldığında Firebase SDK'larını makinenizde çalışan yerel emülatörlere bağlama mantığını içerir.

create() sınıfının FirestoreInitializer yönteminde şu kodu inceleyin:

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

Uygulamamız debug modunda çalışırken yalnızca emülatörlere bağlanmak için BuildConfig kullanıyoruz. Uygulamayı release modunda derlediğimizde bu koşul yanlış olur.

Firebase SDK'sını yerel Firestore emülatörüne bağlamak için useEmulator(host, port) yönteminin kullanıldığını görüyoruz. FirebaseUtil.getFirestore() uygulamasında FirebaseFirestore örneğine erişmek için FirebaseUtil.getFirestore() simgesini kullanacağız. Böylece debug modunda çalışırken her zaman Firestore emülatörüne bağlandığımızdan emin olacağız.

Uygulamayı çalıştırma

google-services.json dosyasını doğru şekilde eklediyseniz proje artık derlenmelidir. Android Studio'da Build (Derle) > Rebuild Project'i (Projeyi Yeniden Derle) tıklayın ve hata kalmadığından emin olun.

Android Studio'da uygulamayı Android emülatörünüzde çalıştırın. İlk olarak "Oturum açma" ekranı gösterilir. Uygulamada oturum açmak için herhangi bir e-posta adresi ve şifre kullanabilirsiniz. Bu oturum açma işlemi Firebase Authentication emülatörüne bağlanır. Bu nedenle, gerçek kimlik bilgileri iletilmez.

Şimdi web tarayıcınızda http://localhost:4000 adresine giderek Emulators kullanıcı arayüzünü açın. Ardından Authentication (Kimlik Doğrulama) sekmesini tıkladığınızda, yeni oluşturduğunuz hesabı görürsünüz:

Firebase Auth Emulator

Oturum açma işlemini tamamladığınızda uygulamanın ana ekranını görürsünüz:

de06424023ffb4b9.png

Yakında ana ekranı doldurmak için bazı veriler ekleyeceğiz.

6. Firestore'a veri yazma

Bu bölümde, şu anda boş olan ana ekranı doldurabilmek için Firestore'a bazı veriler yazacağız.

Uygulamamızdaki ana model nesnesi bir restorandır (model/Restaurant.kt bölümüne bakın). Firestore verileri dokümanlara, koleksiyonlara ve alt koleksiyonlara ayrılır. Her restoranı "restaurants" adlı üst düzey bir koleksiyonda belge olarak saklayacağız. Firestore veri modeli hakkında daha fazla bilgi edinmek için belgelerdeki dokümanlar ve koleksiyonlar hakkındaki makaleyi okuyun.

Gösterim amacıyla, taşma menüsündeki "Rastgele Öğeler Ekle" düğmesini tıkladığımızda uygulamaya on rastgele restoran oluşturma işlevi ekleyeceğiz. MainFragment.kt dosyasını açın ve onAddItemsClicked() yöntemindeki içeriği aşağıdakilerle değiştirin:

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

Yukarıdaki kodla ilgili dikkat edilmesi gereken birkaç önemli nokta vardır:

  • "restaurants" koleksiyonuna referans alarak başladık. Dokümanlar eklendiğinde koleksiyonlar örtülü olarak oluşturulur. Bu nedenle, veri yazmadan önce koleksiyon oluşturmaya gerek yoktur.
  • Her restoran dokümanını oluşturmak için kullandığımız Kotlin veri sınıfları kullanılarak dokümanlar oluşturulabilir.
  • add() yöntemi, otomatik olarak oluşturulan bir kimlikle koleksiyona belge eklediğinden her restoran için benzersiz bir kimlik belirtmemiz gerekmedi.

Şimdi uygulamayı tekrar çalıştırın ve yazdığınız kodu çağırmak için taşma menüsündeki (sağ üst köşede) "Add Random Items" (Rastgele Öğeler Ekle) düğmesini tıklayın:

95691e9b71ba55e3.png

Şimdi web tarayıcınızda http://localhost:4000 adresine giderek Emulators kullanıcı arayüzünü açın. Ardından Firestore sekmesini tıkladığınızda yeni eklediğiniz verileri görürsünüz:

Firebase Auth Emulator

Bu veriler, makinenizde tamamen yerel olarak saklanır. Hatta gerçek projenizde henüz bir Firestore veritabanı bile yok. Bu, bu verileri değiştirme ve silme konusunda herhangi bir sonuç olmadan deneme yapabileceğiniz anlamına gelir.

Tebrikler, Firestore'a veri yazdınız. Bir sonraki adımda bu verilerin uygulamada nasıl görüntüleneceğini öğreneceğiz.

7. Firestore'daki verileri görüntüleme

Bu adımda, Firestore'dan nasıl veri alınacağını ve bu verilerin uygulamamızda nasıl görüntüleneceğini öğreneceğiz. Firestore'dan veri okumanın ilk adımı Query oluşturmaktır. MainFragment.kt dosyasını açın ve aşağıdaki kodu onViewCreated() yönteminin başına ekleyin:

        // Firestore
        firestore = Firebase.firestore

        // Get the 50 highest rated restaurants
        query = firestore.collection("restaurants")
            .orderBy("avgRating", Query.Direction.DESCENDING)
            .limit(LIMIT.toLong())

Şimdi de sorguyu dinlemek istiyoruz. Böylece eşleşen tüm dokümanları alabilir ve gelecekteki güncellemelerden anında haberdar olabiliriz. Nihai hedefimiz bu verileri bir RecyclerView ile bağlamak olduğundan, verileri dinlemek için bir RecyclerView.Adapter sınıfı oluşturmamız gerekir.

Kısmen uygulanmış olan FirestoreAdapter sınıfını açın. İlk olarak, Firestore sorgusundaki güncellemeleri alabilmesi için bağdaştırıcının EventListener uygulamasını ve onEvent işlevini tanımlamasını sağlayalım:

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()
    }
    
    // ...
}

İlk yüklemede, dinleyici her yeni doküman için bir ADDED etkinliği alır. Sorgunun sonuç kümesi zaman içinde değiştiğinden dinleyici, değişiklikleri içeren daha fazla etkinlik alır. Şimdi de dinleyiciyi uygulamayı tamamlayalım. Öncelikle üç yeni yöntem ekleyin: onDocumentAdded, onDocumentModified ve 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)
    }

Ardından, onEvent'dan bu yeni yöntemleri çağırın:

    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()
    }

Son olarak, dinleyiciyi eklemek için startListening() yöntemini uygulayın:

    fun startListening() {
        if (registration == null) {
            registration = query.addSnapshotListener(this)
        }
    }

Uygulama artık Firestore'dan veri okuyacak şekilde tamamen yapılandırıldı. Uygulamayı tekrar çalıştırdığınızda önceki adımda eklediğiniz restoranları görmeniz gerekir:

9e45f40faefce5d0.png

Şimdi tarayıcınızda Emulator kullanıcı arayüzüne geri dönün ve restoran adlarından birini düzenleyin. Değişikliği uygulamada neredeyse anında görebilirsiniz.

8. Verileri sıralama ve filtreleme

Uygulama şu anda koleksiyonun tamamındaki en yüksek puanlı restoranları gösteriyor ancak gerçek bir restoran uygulamasında kullanıcı verileri sıralamak ve filtrelemek isteyecektir. Örneğin, uygulama "Philadelphia'daki en iyi deniz ürünleri restoranları" veya "En ucuz pizza" gibi sonuçlar gösterebilmelidir.

Uygulamanın üst kısmındaki beyaz çubuğu tıkladığınızda filtreler iletişim kutusu açılır. Bu bölümde, bu iletişim kutusunun çalışmasını sağlamak için Firestore sorgularını kullanacağız:

67898572a35672a5.png

onFilter() yöntemini MainFragment.kt olarak düzenleyelim. Bu yöntem, filtreler iletişim kutusunun çıkışını yakalamak için oluşturduğumuz yardımcı nesne olan bir Filters nesnesini kabul eder. Sorguyu filtrelerden oluşturma yöntemini şu şekilde değiştireceğiz:

    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
    }

Yukarıdaki snippet'te, verilen filtrelerle eşleşmesi için where ve orderBy ifadelerini ekleyerek bir Query nesnesi oluşturuyoruz.

Uygulamayı tekrar çalıştırın ve en popüler uygun fiyatlı restoranları göstermek için aşağıdaki filtreyi seçin:

7a67a8a400c80c50.png

Artık yalnızca uygun fiyatlı seçenekleri içeren filtrelenmiş bir restoran listesi görmelisiniz:

a670188398c3c59.png

Bu noktaya kadar geldiyseniz artık Firestore'da tam işlevli bir restoran önerisi görüntüleme uygulaması oluşturdunuz. Artık restoranları anlık olarak sıralayabilir ve filtreleyebilirsiniz. Sonraki birkaç bölümde restoranlara yorum ekleyecek ve uygulamaya güvenlik kuralları ekleyeceğiz.

9. Verileri alt koleksiyonlar halinde düzenleme

Bu bölümde, uygulamaya puanlar ekleyeceğiz. Böylece kullanıcılar en sevdikleri (veya en sevmedikleri) restoranları inceleyebilecek.

Koleksiyonlar ve alt koleksiyonlar

Şimdiye kadar tüm restoran verilerini "restoranlar" adlı üst düzey bir koleksiyonda sakladık. Bir kullanıcı bir restorana puan verdiğinde restoranlara yeni bir Rating nesnesi eklemek istiyoruz. Bu görevde alt koleksiyon kullanacağız. Alt koleksiyonu bir dokümana eklenmiş koleksiyon olarak düşünebilirsiniz. Bu nedenle, her restoran dokümanında derecelendirme dokümanlarıyla dolu bir derecelendirme alt koleksiyonu bulunur. Alt koleksiyonlar, belgelerimizi şişirmeden veya karmaşık sorgular gerektirmeden verileri düzenlememize yardımcı olur.

Bir alt koleksiyona erişmek için üst dokümanda .collection() işlevini çağırın:

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

Bir alt koleksiyona, üst düzey koleksiyonlarda olduğu gibi erişebilir ve sorgulayabilirsiniz. Boyut sınırlaması veya performans değişikliği yoktur. Firestore veri modeli hakkında daha fazla bilgiyi burada bulabilirsiniz.

İşlemde veri yazma

Doğru alt koleksiyona Rating eklemek için yalnızca .add() çağrısı yapılması gerekir ancak yeni verileri yansıtmak için Restaurant nesnesinin ortalama puanını ve puan sayısını da güncellememiz gerekir. Bu iki değişikliği yapmak için ayrı işlemler kullanırsak eski veya yanlış verilerle sonuçlanabilecek bir dizi yarış durumu oluşur.

Puanların düzgün şekilde eklenmesini sağlamak için restoranlara puan eklemek üzere bir işlem kullanırız. Bu işlem birkaç eylem gerçekleştirir:

  • Restoranın mevcut puanını okuyup yeni puanı hesaplama
  • Puanı alt koleksiyona ekleyin.
  • Restoranın ortalama puanını ve puan sayısını güncelleme

RestaurantDetailFragment.kt dosyasını açın ve addRating işlevini uygulayın:

    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() işlevi, işlemin tamamını temsil eden bir Task döndürür. onRating() işlevinde, işlemin sonucuna yanıt vermek için göreve işleyici işlevleri eklenir.

Şimdi uygulamayı tekrar çalıştırın ve restoran ayrıntıları ekranını açması gereken restoranlardan birini tıklayın. İnceleme eklemeye başlamak için + düğmesini tıklayın. Yıldız sayısını seçip metin girerek yorum ekleyin.

78fa16cdf8ef435a.png

Gönder'e bastığınızda işlem başlatılır. İşlem tamamlandığında incelemeniz aşağıda gösterilir ve restoranın inceleme sayısı güncellenir:

f9e670f40bd615b0.png

Tebrikler! Artık Cloud Firestore üzerinde oluşturulmuş sosyal, yerel ve mobil bir restoran yorumu uygulamanız var. Bu türlerin günümüzde çok popüler olduğunu duyuyorum.

10. Verilerinizi güvence altına alma

Şu ana kadar bu uygulamanın güvenliğini dikkate almadık. Kullanıcıların yalnızca doğru kendi verilerini okuyup yazabildiğini nasıl anlarız? Firestore veritabanları, Güvenlik Kuralları adlı bir yapılandırma dosyasıyla korunur.

firestore.rules dosyasını açın ve içeriği aşağıdakilerle değiştirin:

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

Bu kurallar, istemcilerin yalnızca güvenli değişiklikler yapmasını sağlamak için erişimi kısıtlar. Örneğin, bir restoran dokümanında yapılan güncellemeler yalnızca puanları değiştirebilir, adı veya diğer değişmez verileri değiştiremez. Yalnızca kullanıcı kimliği, oturum açmış kullanıcıyla eşleşiyorsa derecelendirme oluşturulabilir. Bu sayede kimlik sahteciliği önlenir.

Güvenlik kuralları hakkında daha fazla bilgi edinmek için dokümanları inceleyin.

11. Sonuç

Artık Firestore üzerinde tam özellikli bir uygulama oluşturdunuz. Aşağıdakiler de dahil olmak üzere en önemli Firestore özelliklerini öğrendiniz:

  • Dokümanlar ve koleksiyonlar
  • Veri okuma ve yazma
  • Sorgularla sıralama ve filtreleme
  • Alt koleksiyonlar
  • İşlemler

Daha Fazla Bilgi

Firestore hakkında daha fazla bilgi edinmek için başlayabileceğiniz bazı kaynaklar:

Bu codelab'deki restoran uygulaması, "Friendly Eats" örnek uygulamasına dayanmaktadır. İlgili uygulamanın kaynak koduna buradan göz atabilirsiniz.

İsteğe bağlı: Üretime dağıtma

Bu uygulama şu ana kadar yalnızca Firebase Emulator Suite'i kullandı. Bu uygulamayı gerçek bir Firebase projesine nasıl dağıtacağınızı öğrenmek istiyorsanız bir sonraki adıma geçin.

12. (İsteğe bağlı) Uygulamanızı dağıtın

Bu uygulama şu ana kadar tamamen yereldi ve tüm veriler Firebase Emulator Suite'te yer alıyordu. Bu bölümde, Firebase projenizi bu uygulamanın üretimde çalışacak şekilde nasıl yapılandıracağınızı öğreneceksiniz.

Firebase Authentication

Firebase konsolunda Kimlik Doğrulama bölümüne gidin ve Başlayın'ı tıklayın. Oturum açma yöntemi sekmesine gidin ve Yerel sağlayıcılar bölümünden E-posta/Şifre seçeneğini belirleyin.

E-posta/Şifre oturum açma yöntemini etkinleştirip Kaydet'i tıklayın.

sign-in-providers.png

Firestore

Veritabanı oluştur

Konsolun Firestore Veritabanı bölümüne gidin ve Veritabanı Oluştur'u tıklayın:

  1. Güvenlik kuralları hakkında sorulduğunda üretim modunda başlatmayı seçerseniz bu kurallar yakında güncellenir.
  2. Uygulamanız için kullanmak istediğiniz veritabanı konumunu seçin. Veritabanı konumunu seçmenin kalıcı bir karar olduğunu ve bunu değiştirmek için yeni bir proje oluşturmanız gerektiğini unutmayın. Proje konumu seçme hakkında daha fazla bilgi için belgelere bakın.

Kuralları dağıtma

Daha önce yazdığınız güvenlik kurallarını dağıtmak için codelab dizininde aşağıdaki komutu çalıştırın:

$ firebase deploy --only firestore:rules

Bu işlem, firestore.rules içeriğini projenize dağıtır. Konsoldaki Kurallar sekmesine giderek bunu onaylayabilirsiniz.

Dizinleri dağıtma

FriendlyEats uygulamasında, bir dizi özel bileşik dizin gerektiren karmaşık sıralama ve filtreleme özellikleri vardır. Bunlar Firebase konsolunda manuel olarak oluşturulabilir ancak tanımlarını firestore.indexes.json dosyasında yazıp Firebase CLI kullanarak dağıtmak daha kolaydır.

firestore.indexes.json dosyasını açtığınızda gerekli indekslerin zaten sağlandığını görürsünüz:

{
  "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": []
}

Bu dizinleri dağıtmak için aşağıdaki komutu çalıştırın:

$ firebase deploy --only firestore:indexes

Dizin oluşturma işleminin anında gerçekleşmediğini unutmayın. İlerlemeyi Firebase konsolunda izleyebilirsiniz.

Uygulamayı yapılandırma

util/FirestoreInitializer.kt ve util/AuthInitializer.kt dosyalarında, hata ayıklama modundayken Firebase SDK'sının emülatörlere bağlanmasını sağlayacak şekilde yapılandırma yaptık:

    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
    }

Uygulamanızı gerçek Firebase projenizle test etmek istiyorsanız şunlardan birini yapabilirsiniz:

  1. Uygulamayı yayın modunda oluşturun ve bir cihazda çalıştırın.
  2. BuildConfig.DEBUG öğesini geçici olarak false ile değiştirin ve uygulamayı tekrar çalıştırın.

Üretimle düzgün şekilde bağlantı kurmak için uygulamada Oturumu Kapatıp tekrar oturum açmanız gerekebileceğini unutmayın.