Cloud Firestore Android Codelab

1. Genel Bakış

Gol sayısı

Bu codelab'de, Android üzerinde Cloud Firestore tarafından desteklenen bir restoran öneri uygulaması geliştireceksiniz. Bu derslere katılarak:

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

Ön koşullar

Bu codelab'i başlatmadan önce aşağıdakileri yaptığınızdan emin olun:

  • Android Studio Flamingo veya daha yeni sürümler
  • API 19 veya sonraki bir sürüme sahip Android emülatörü
  • Node.js sürüm 16 veya sonraki sürümler
  • Java sürüm 17 veya sonraki sürümler

2. Firebase projesi oluşturma

  1. Google Hesabınızla Firebase konsolunda oturum açın.
  2. Firebase konsolunda Proje ekle'yi tıklayın.
  3. Aşağıdaki ekran görüntüsünde gösterildiği gibi Firebase projeniz için bir ad girin (ör. "Friendly Eats") ve Devam'ı tıklayın.

9d2f625aebcab6af.png

  1. Google Analytics'i etkinleştirmeniz istenebilir. Bu codelab'in amaçları doğrultusunda, seçiminiz önemli değildir.
  2. Yaklaşık bir dakika sonra Firebase projeniz hazır olacaktır. Devam'ı tıklayın.

3. Örnek projeyi ayarlama

Kodu indirme

Bu codelab için örnek kodu klonlamak amacıyla aşağıdaki komutu çalıştırın. Bu işlem, makinenizde friendlyeats-android adında bir klasör oluşturur:

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

Makinenizde Git yoksa kodu doğrudan GitHub'dan da indirebilirsiniz.

Firebase yapılandırması ekleyin

  1. Firebase konsolunda, sol gezinme bölmesinden Projeye Genel Bakış'ı 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. Uygulamayı Kaydet'i tıklayın ve google-services.json dosyasını indirmek için talimatları uygulayın ve dosyayı az önce indirdiğiniz kodun app/ klasörüne taşıyın. Ardından İleri'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 ve diğer Firebase hizmetlerinin yerel olarak emülasyonu için Firebase Emulator Suite'i kullanacaksınız. Böylece, uygulamanızı geliştirmek için güvenli, hızlı ve ücretsiz bir yerel geliştirme ortamı elde edebilirsiniz.

Firebase CLI'ı yükleme

Öncelikle Firebase CLI'ı 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 program edinmek veya npm üzerinden yüklemek için yükleme talimatlarını okuyun.

KSA yüklendikten sonra firebase --version çalıştırıldığında 9.0.0 veya sonraki bir sürüm bildirilmelidir:

$ firebase --version
9.0.0

Giriş yap

KSA'yı Google Hesabınıza bağlamak için firebase login komutunu çalıştırın. Bu, giriş işlemini 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 talimatları uygulayın ve takma ad seçmeniz istenirse default komutunu girin.

5. Uygulamayı çalıştırın

Firebase Emulator Suite ve FriendlyEats Android uygulamasını ilk kez çalıştırmanın zamanı geldi.

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

Firebase Emülatörleri'ni başlatmak için friendlyeats-android dizinindeki terminalinizde firebase emulators:start komutunu çalıştırın. Şuna benzer günlükler göreceksiniz:

$ 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. Codelab'in geri kalanında bu komutu çalışır durumda bıraktığınızdan emin olun. Android uygulamanızın emülatörlere bağlanması gerekir.

Uygulamayı emülatörlere bağlayın

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

FirestoreInitializer sınıfının create() yönteminde şu kod parçasını inceleyin:

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

Yalnızca uygulamamız debug modunda çalışırken emülatörlere bağlandığımızdan emin olmak için BuildConfig kullanıyoruz. Uygulamayı release modunda derlediğimizde bu koşul yanlış olacaktır.

Firebase SDK'sını yerel Firestore emülatörüne bağlamak için useEmulator(host, port) yöntemini kullandığını görüyoruz. Uygulama boyunca bu FirebaseFirestore örneğine erişmek için FirebaseUtil.getFirestore() kullanacağız. Bu nedenle, debug modunda çalışırken her zaman Firestore emülatörüne bağlandığımızdan eminiz.

Uygulamayı çalıştırın

google-services.json dosyasını düzgün şekilde eklediyseniz proje şimdi derlenir. Android Studio'da Build (Derleme) > Projeyi yeniden derleyin ve başka hata kalmadığından emin olun.

Android Studio'da uygulamayı Android emülatörünüzde çalıştırın. İlk başta bir "Oturum aç" seçeneği sunulur tıklayın. Uygulamada oturum açmak için herhangi bir e-posta adresini ve şifreyi kullanabilirsiniz. Bu oturum açma işlemi Firebase Authentication emülatörüne bağlandığı için 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ıklayın. Yeni oluşturduğunuz hesabı göreceksiniz:

Firebase Auth Emülatörü

Oturum açma işlemini tamamladıktan sonra uygulama 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 bazı verileri Firestore'a yazacağız.

Uygulamamızdaki ana model nesnesi bir restoran (bkz. model/Restaurant.kt). Firestore verileri belgelere, koleksiyonlara ve alt koleksiyonlara ayrılır. Her bir restoranı, "restaurants" adlı üst düzey bir koleksiyonda belge olarak saklayacağız. Firestore veri modeli hakkında daha fazla bilgi edinmek için belgelerdeki belge ve koleksiyonlar hakkındaki bilgileri okuyun.

Gösterim amacıyla, "Rastgele Öğeler Ekle"yi tıkladığımızda uygulamaya rastgele on restoran oluşturma işlevi ekleyeceğiz. düğmesini kullanabilirsiniz. MainFragment.kt dosyasını açın ve onAddItemsClicked() yöntemindeki içeriği şununla 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 göz önünde bulundurulması gereken birkaç önemli nokta vardır:

  • İşe "restaurants" koleksiyonu için referans alarak başladık. Koleksiyonlar, dokümanlar eklendiğinde dolaylı olarak oluşturulur. Bu nedenle, verileri yazmadan önce koleksiyon oluşturmaya gerek kalmaz.
  • Dokümanlar, her Restaurant dokümanını oluşturmak için kullandığımız Kotlin veri sınıfları kullanılarak oluşturulabilir.
  • add() yöntemi, otomatik olarak oluşturulmuş kimliğe sahip bir koleksiyona doküman eklediğinden her Restoran için benzersiz bir kimlik belirtmemiz gerekmedi.

Şimdi uygulamayı tekrar çalıştırın ve "Rastgele Öğe Ekle"yi tıklayın 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ıklayın, az önce eklediğiniz verileri göreceksiniz:

Firebase Auth Emülatörü

Bu veriler, makineniz için% 100 yereldir. Hatta gerçek projenizde henüz bir Firestore veritabanı bile yok. Bu nedenle, hiçbir sonuç olmadan bu verileri değiştirip silebilirsiniz.

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

7. Firestore verilerini göster

Bu adımda, Firestore'dan veri almayı ve uygulamamızda görüntülemeyi öğreneceğiz. Firestore verilerini okumanın ilk adımı bir Query oluşturmaktır. MainFragment.kt dosyasını açın ve onViewCreated() yönteminin başına aşağıdaki kodu ekleyin:

        // Firestore
        firestore = Firebase.firestore

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

Şimdi sorguyu dinlemek, böylece eşleşen tüm dokümanları almak ve gelecekteki güncellemelerle ilgili gerçek zamanlı bildirimler almak istiyoruz. Nihai hedefimiz bu verileri bir RecyclerView öğesine 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 bağdaştırıcının EventListener uygulamasını uygulamasını sağlayalım ve Firestore sorgusuna ilişkin güncellemeleri alabilmesi için onEvent işlevini tanımlayalı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 işleyici, her yeni doküman için bir ADDED etkinliği alır. Sorgunun sonuç grubu zaman içinde değiştikçe işleyici, değişiklikleri içeren daha fazla etkinlik alır. Şimdi işleyiciyi uygulamayı bitirelim. Ö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 uygulamasındaki şu 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, işleyiciyi eklemek için startListening() yöntemini uygulayın:

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

Uygulama artık Firestore'daki verileri okuyacak şekilde tamamen yapılandırılmıştır. Uygulamayı tekrar çalıştırırsanız, bir önceki adımda eklediğiniz restoranları görürsünüz:

9e45f40faefce5d0.png

Şimdi tarayıcınızdaki Emülatör kullanıcı arayüzüne geri dönün ve restoran adlarından birini düzenleyin. Neredeyse anında değiştiğini göreceksiniz.

8. Verileri sıralama ve filtreleme

Uygulama şu anda koleksiyonun tamamında en yüksek puanlı restoranları gösteriyor ancak gerçek bir restoran uygulamasında verileri sıralamak ve filtrelemek isteyecektir. Örneğin, uygulama "Philadelphia'daki en iyi deniz ürünleri restoranları"nı göstermelidir. veya "En ucuz pizza" gibi seçenekler arasından tercih yapabilirsiniz.

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

Şimdi MainFragment.kt öğesinin onFilter() yöntemini düzenleyelim. Bu yöntem, filtreler iletişim kutusunun çıkışını yakalamak için oluşturduğumuz yardımcı nesne olan Filters nesnesini kabul eder. Filtrelerden bir sorgu oluşturmak için bu yöntemi 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şecek where ve orderBy ifadelerini ekleyerek bir Query nesnesi oluştururuz.

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

7a67a8a400c80c50.png

Şimdi yalnızca düşük fiyat seçeneklerini içeren filtrelenmiş bir restoran listesi görmeniz gerekir:

a670188398c3c59.png

Bu noktaya kadar ulaştığınız takdirde Firestore'da tam işlevli bir restoran önerisi görüntüleme uygulaması oluşturmuşsunuz demektir. Artık restoranları gerçek zamanlı olarak sıralayabilir ve filtreleyebilirsiniz. Önümüzdeki birkaç bölümde restoranlara yorum ekleyecek ve uygulamaya güvenlik kuralları ekleyeceğiz.

9. Verileri alt koleksiyonlarda düzenleme

Bu bölümde, kullanıcıların favori (veya en beğendikleri) restoranları hakkında yorum yapabilmeleri için uygulamaya puan ekleyeceğiz.

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örev için bir alt koleksiyon kullanacağız. Alt koleksiyonu bir dokümana ekli bir koleksiyon olarak düşünebilirsiniz. Dolayısıyla her restoran belgesinde, derecelendirme belgeleriyle dolu bir derecelendirme alt koleksiyonu bulunur. Alt koleksiyonlar, dokümanlarımızı şişirmeden veya karmaşık sorgular gerektirmeden verilerin düzenlenmesine yardımcı olur.

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

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

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

Bir işlemde veri yazma

Doğru alt koleksiyona bir Rating eklemek için yalnızca .add() yönteminin çağrı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ış verilere yol açabilecek bir dizi yarış koşulu söz konusudur.

Puanların doğru şekilde eklendiğinden emin olmak için bir restorana puan eklemek üzere bir işlem kullanırız. Bu işlemde birkaç işlem gerçekleştirilecek:

  • Restoranın mevcut puanını okuyup yenisini hesaplayın
  • Puanı alt koleksiyona ekleyin
  • Restoranın ortalama puanını ve puan sayısını güncellemek

RestaurantDetailFragment.kt öğesini 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şleyiciler eklenir.

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

78fa16cdf8ef435a.png

Gönder'i tıkladığınızda işlem başlar. İşlem tamamlandığında yorumunuzun aşağıda görüntülendiğini ve restoranın yorum sayısındaki güncellemeyi görürsünüz:

f9e670f40bd615b0.png

Tebrikler! Artık Cloud Firestore'da yerleşik, sosyal, yerel ve mobil bir restoran değerlendirme uygulamanız var. Günümüzde bu videoların çok popüler olduğunu duydum.

10. Verilerinizin güvenliğini sağlayın

Şu ana kadar bu uygulamanın güvenliğini dikkate almadık. Kullanıcıların yalnızca kendi doğru verileri okuyup yazabildiğini nasıl bilebiliriz? Firestore veritabanlarının güvenliği, Security Rules (Güvenlik Kuralları) adlı bir yapılandırma dosyasıyla sağlanır.

firestore.rules dosyasını açtığınızda aşağıdakileri göreceksiniz:

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

İstenmeyen veri erişimlerini veya değişiklikleri önlemek için bu kuralları değiştirelim. firestore.rules dosyasını açıp içeriği aşağıdakiyle 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, müşterilerin 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 derecelendirmeleri değiştirebilir, adı veya diğer sabit verileri değiştiremez. Derecelendirmeler yalnızca kullanıcı kimliği oturum açmış kullanıcıyla eşleşirse oluşturulabilir. Bu da adres sahteciliğini önler.

Güvenlik Kuralları hakkında daha fazla bilgi edinmek için dokümanları ziyaret edin.

11. Sonuç

Firestore üzerinde eksiksiz özellikler sunan 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 bilgi edinmeye devam etmek için işe koyulabileceğiniz bazı tavsiyeler:

Bu codelab'deki restoran uygulaması "Friendly Eats"i temel alıyordu örnek bir uygulamadır. Bu uygulamanın kaynak koduna buradan göz atabilirsiniz.

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

Bu uygulama şimdiye 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ğıtma

Şu ana kadar bu uygulama tamamen yerelti ve tüm veriler Firebase Emulator Suite'te yer alıyor. Bu bölümde, bu uygulamanın üretimde çalışması için Firebase projenizi 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'dan E-posta/Şifre'yi seçin.

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

oturum açma-sağlayıcılar.png

Firestore

Veritabanı oluştur

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

  1. Üretim Modu'nda başlatmayı seçtiğiniz güvenlik kuralları hakkında sorulduğunda bu kuralları yakında güncelleyeceğiz.
  2. Uygulamanız için kullanmak istediğiniz veritabanı konumunu seçin. Veritabanı konumu seçmenin kalıcı bir karar olduğunu ve bunu değiştirmek için yeni bir proje oluşturmanız gerekeceğini unutmayın. Proje konumu seçmek hakkında daha fazla bilgi için belgeleri inceleyin.

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. Bunu doğrulamak için konsolda Kurallar sekmesine gidin.

Dizinleri Dağıt

FriendlyEats uygulaması karmaşık sıralama ve filtreleme özelliklerine sahiptir. Bu nedenle, bir dizi özel bileşik dizin gerekir. Bunlar Firebase konsolunda elle oluşturulabilir ancak bunların tanımlarını firestore.indexes.json dosyasına yazmak ve bunları Firebase CLI kullanarak dağıtmak daha kolaydır.

firestore.indexes.json dosyasını açarsanız gerekli dizinlerin zaten sağlanmış olduğunu 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şlemi anlık değildir. İlerleme durumunu Firebase konsolundan izleyebilirsiniz.

Uygulamayı yapılandırma

util/FirestoreInitializer.kt ve util/AuthInitializer.kt dosyalarında Firebase SDK'sını, hata ayıklama modundayken emülatörlere bağlanacak şekilde yapılandırdı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ınlama modunda derleyin ve bir cihazda çalıştırın.
  2. BuildConfig.DEBUG yerine geçici olarak false koyun ve uygulamayı tekrar çalıştırın.

Üretime düzgün şekilde bağlanmak için uygulamada oturumu kapatıp tekrar açmanız gerekebileceğini unutmayın.