Veri Okuma ve Yazma

(İsteğe bağlı) Firebase Emulator Suite ile prototip yapın ve test edin

Uygulamanızın Realtime Database'den nasıl okuduğu ve ona nasıl yazdığı hakkında konuşmadan önce, Realtime Database işlevselliğini prototiplemek ve test etmek için kullanabileceğiniz bir dizi aracı tanıtalım: Firebase Emulator Suite. Farklı veri modelleri deniyorsanız, güvenlik kurallarınızı optimize ediyorsanız veya arka uçla etkileşim kurmanın en uygun maliyetli yolunu bulmaya çalışıyorsanız, canlı hizmetleri dağıtmadan yerel olarak çalışabilmek harika bir fikir olabilir.

Gerçek Zamanlı Veritabanı öykünücüsü, uygulamanızın öykünülmüş veritabanı içeriğiniz ve yapılandırmanızın yanı sıra isteğe bağlı olarak öykünülmüş proje kaynaklarınızla (işlevler, diğer veritabanları ve güvenlik kuralları) etkileşim kurmasını sağlayan Emulator Suite'in bir parçasıdır.emulator_suite_short

Gerçek Zamanlı Veritabanı öykünücüsünü kullanmak yalnızca birkaç adımı içerir:

  1. Öykünücüye bağlanmak için uygulamanızın test yapılandırmasına bir kod satırı ekleme.
  2. Yerel proje dizininizin kökünden, firebase emulators:start çalıştırılıyor.
  3. Her zamanki gibi bir Realtime Database platformu SDK'sını kullanarak veya Realtime Database REST API'yi kullanarak uygulamanızın prototip kodundan çağrı yapmak.

Gerçek Zamanlı Veritabanı ve Bulut İşlevlerini içeren ayrıntılı bir kılavuz mevcuttur. Ayrıca Emulator Suite tanıtımına da göz atmalısınız.

Veritabanı Referansı Alın

Veritabanından veri okumak veya yazmak için DatabaseReference örneğine ihtiyacınız vardır:

DatabaseReference ref = FirebaseDatabase.instance.ref();

Veri yaz

Bu belge Firebase verilerini okuma ve yazmanın temellerini kapsar.

Firebase verileri bir DatabaseReference yazılır ve referans tarafından yayılan olaylar beklenerek veya dinlenerek alınır. Olaylar, verinin başlangıç ​​durumu için bir kez ve veriler değiştiğinde tekrar yayınlanır.

Temel yazma işlemleri

Temel yazma işlemleri için, verileri belirli bir referansa kaydetmek ve o yoldaki mevcut verileri değiştirmek için set() işlevini kullanabilirsiniz. Şu türlere referans ayarlayabilirsiniz: String , boolean , int , double , Map , List .

Örneğin, set() ile bir kullanıcıyı aşağıdaki gibi ekleyebilirsiniz:

DatabaseReference ref = FirebaseDatabase.instance.ref("users/123");

await ref.set({
  "name": "John",
  "age": 18,
  "address": {
    "line1": "100 Mountain View"
  }
});

set() işlevinin bu şekilde kullanılması, alt düğümler de dahil olmak üzere belirtilen konumdaki verilerin üzerine yazar. Ancak nesnenin tamamını yeniden yazmadan bir alt öğeyi yine de güncelleyebilirsiniz. Kullanıcıların profillerini güncellemesine izin vermek istiyorsanız kullanıcı adını aşağıdaki şekilde güncelleyebilirsiniz:

DatabaseReference ref = FirebaseDatabase.instance.ref("users/123");

// Only update the name, leave the age and address!
await ref.update({
  "age": 19,
});

update() yöntemi, düğümlere giden bir alt yolu kabul ederek veritabanındaki birden fazla düğümü aynı anda güncellemenize olanak tanır:

DatabaseReference ref = FirebaseDatabase.instance.ref("users");

await ref.update({
  "123/age": 19,
  "123/address/line1": "1 Mountain View",
});

Verileri oku

Değer olaylarını dinleyerek verileri okuyun

Bir yoldaki verileri okumak ve değişiklikleri dinlemek için DatabaseEvent s'yi dinlemek üzere DatabaseReference onValue özelliğini kullanın.

Olay anında mevcut olan belirli bir yoldaki verileri okumak için DatabaseEvent kullanabilirsiniz. Bu olay, dinleyici eklendiğinde bir kez tetiklenir ve çocuklar da dahil olmak üzere veriler her değiştiğinde tekrar tetiklenir. Etkinlik, alt veriler de dahil olmak üzere o konumdaki tüm verileri içeren bir snapshot özelliğine sahiptir. Veri yoksa anlık görüntünün exists özelliği false , value özelliği ise null olacaktır.

Aşağıdaki örnek, veritabanından bir gönderinin ayrıntılarını alan bir sosyal blog uygulamasını göstermektedir:

DatabaseReference starCountRef =
        FirebaseDatabase.instance.ref('posts/$postId/starCount');
starCountRef.onValue.listen((DatabaseEvent event) {
    final data = event.snapshot.value;
    updateStarCount(data);
});

Dinleyici, value özelliğinde olay anında veritabanında belirtilen konumdaki verileri içeren bir DataSnapshot alır.

Verileri bir kez okuyun

Get() kullanarak bir kez okuyun

SDK, uygulamanızın çevrimiçi veya çevrimdışı olmasına bakılmaksızın veritabanı sunucularıyla olan etkileşimleri yönetmek için tasarlanmıştır.

Genel olarak, arka uçtan verilerdeki güncellemelerden haberdar olmak amacıyla verileri okumak için yukarıda açıklanan değer olayı tekniklerini kullanmalısınız. Bu teknikler, kullanımınızı ve faturalandırmanızı azaltır ve kullanıcılarınıza çevrimiçi ve çevrimdışıyken en iyi deneyimi sunmak için optimize edilmiştir.

Verilere yalnızca bir kez ihtiyacınız varsa, veritabanındaki verilerin anlık görüntüsünü almak için get() komutunu kullanabilirsiniz. Herhangi bir nedenle get() sunucu değerini döndüremezse, istemci yerel depolama önbelleğini araştıracak ve değer hala bulunamazsa bir hata döndürecektir.

Aşağıdaki örnek, bir kullanıcının herkese açık kullanıcı adının veritabanından tek bir kez alınmasını gösterir:

final ref = FirebaseDatabase.instance.ref();
final snapshot = await ref.child('users/$userId').get();
if (snapshot.exists) {
    print(snapshot.value);
} else {
    print('No data available.');
}

get() işlevinin gereksiz kullanımı bant genişliği kullanımını artırabilir ve performans kaybına yol açabilir; bu da yukarıda gösterildiği gibi gerçek zamanlı bir dinleyici kullanılarak önlenebilir.

Once() ile verileri bir kez okuyun

Bazı durumlarda, sunucuda güncelleştirilmiş bir değer olup olmadığını kontrol etmek yerine, yerel önbellekteki değerin hemen döndürülmesini isteyebilirsiniz. Bu durumlarda verileri yerel disk önbelleğinden hemen almak için once() kullanabilirsiniz.

Bu, yalnızca bir kez yüklenmesi gereken ve sık sık değişmesi beklenmeyen veya aktif dinleme gerektirmeyen veriler için kullanışlıdır. Örneğin, önceki örneklerdeki blog uygulaması, yeni bir gönderi yazmaya başladığında kullanıcının profilini yüklemek için bu yöntemi kullanır:

final event = await ref.once(DatabaseEventType.value);
final username = event.snapshot.value?.username ?? 'Anonymous';

Verileri güncelleme veya silme

Belirli alanları güncelle

Diğer alt düğümlerin üzerine yazmadan bir düğümün belirli alt öğelerine aynı anda yazmak için update() yöntemini kullanın.

update() öğesini çağırırken, anahtar için bir yol belirterek alt düzey alt değerleri güncelleyebilirsiniz. Veriler daha iyi ölçeklendirmek için birden fazla konumda depolanıyorsa, veri yayma özelliğini kullanarak bu verilerin tüm örneklerini güncelleyebilirsiniz. Örneğin, bir sosyal blog uygulaması bir gönderi oluşturmak ve aynı anda bunu en son etkinlik akışına ve gönderiyi gönderen kullanıcının etkinlik akışına güncellemek isteyebilir. Bunu yapmak için blog uygulaması şuna benzer bir kod kullanır:

void writeNewPost(String uid, String username, String picture, String title,
        String body) async {
    // A post entry.
    final postData = {
        'author': username,
        'uid': uid,
        'body': body,
        'title': title,
        'starCount': 0,
        'authorPic': picture,
    };

    // Get a key for a new Post.
    final newPostKey =
        FirebaseDatabase.instance.ref().child('posts').push().key;

    // Write the new post's data simultaneously in the posts list and the
    // user's post list.
    final Map<String, Map> updates = {};
    updates['/posts/$newPostKey'] = postData;
    updates['/user-posts/$uid/$newPostKey'] = postData;

    return FirebaseDatabase.instance.ref().update(updates);
}

Bu örnek, düğümde /posts/$postid adresindeki tüm kullanıcılar için gönderiler içeren bir gönderi oluşturmak ve aynı anda anahtarı key ile almak için push() işlevini kullanır. Anahtar daha sonra kullanıcının /user-posts/$userid/$postid adresindeki gönderilerinde ikinci bir giriş oluşturmak için kullanılabilir.

Bu yolları kullanarak, JSON ağacındaki birden çok konuma update() çağrısıyla eş zamanlı güncellemeler gerçekleştirebilirsiniz; örneğin bu örneğin her iki konumda da yeni gönderiyi nasıl oluşturduğu gibi. Bu şekilde yapılan eşzamanlı güncellemeler atomiktir: ya tüm güncellemeler başarılı olur ya da tüm güncellemeler başarısız olur.

Tamamlama geri araması ekleme

Verilerinizin ne zaman kaydedildiğini bilmek istiyorsanız tamamlama geri aramalarını kaydedebilirsiniz. Hem set() hem de update() , veritabanına yazma işlemi tamamlandığında ve çağrı başarısız olduğunda çağrılan başarı ve hata geri çağrılarını ekleyebileceğiniz Future s'yi döndürür.

FirebaseDatabase.instance
    .ref('users/$userId/email')
    .set(emailAddress)
    .then((_) {
        // Data saved successfully!
    })
    .catchError((error) {
        // The write failed...
    });

Verileri sil

Verileri silmenin en basit yolu, söz konusu verinin konumuna ilişkin bir referans üzerinden remove() işlevini çağırmaktır.

set() veya update() gibi başka bir yazma işleminin değeri olarak null belirterek de silebilirsiniz. Tek bir API çağrısında birden fazla alt öğeyi silmek için bu tekniği update() ile kullanabilirsiniz.

Verileri işlem olarak kaydet

Artımlı sayaçlar gibi eşzamanlı değişikliklerle bozulabilecek verilerle çalışırken, runTransaction() işlevine bir işlem işleyicisi ileterek bir işlemi kullanabilirsiniz. Bir işlem işleyicisi, verinin mevcut durumunu argüman olarak alır ve yazmak istediğiniz yeni durumu döndürür. Yeni değeriniz başarıyla yazılmadan önce başka bir istemci konuma yazarsa, güncelleme işleviniz yeni geçerli değerle yeniden çağrılır ve yazma işlemi yeniden denenir.

Örneğin, örnek sosyal blog uygulamasında, kullanıcıların gönderilere yıldız eklemesine ve yıldızları kaldırmasına ve bir gönderinin kaç yıldız aldığını aşağıdaki gibi takip etmesine izin verebilirsiniz:

void toggleStar(String uid) async {
  DatabaseReference postRef =
      FirebaseDatabase.instance.ref("posts/foo-bar-123");

  TransactionResult result = await postRef.runTransaction((Object? post) {
    // Ensure a post at the ref exists.
    if (post == null) {
      return Transaction.abort();
    }

    Map<String, dynamic> _post = Map<String, dynamic>.from(post as Map);
    if (_post["stars"] is Map && _post["stars"][uid] != null) {
      _post["starCount"] = (_post["starCount"] ?? 1) - 1;
      _post["stars"][uid] = null;
    } else {
      _post["starCount"] = (_post["starCount"] ?? 0) + 1;
      if (!_post.containsKey("stars")) {
        _post["stars"] = {};
      }
      _post["stars"][uid] = true;
    }

    // Return the new data.
    return Transaction.success(_post);
  });
}

Varsayılan olarak, işlem güncelleme işlevi her çalıştırıldığında olaylar tetiklenir, bu nedenle işlevi birden çok kez çalıştırırsanız, ara durumları görebilirsiniz. Bu ara durumları bastırmak için applyLocally değerini false olarak ayarlayabilir ve bunun yerine olaylar ortaya çıkmadan önce işlemin tamamlanmasını bekleyebilirsiniz:

await ref.runTransaction((Object? post) {
  // ...
}, applyLocally: false);

Bir işlemin sonucu, işlemin gerçekleştirilip gerçekleştirilmediği ve yeni anlık görüntü gibi bilgileri içeren TransactionResult :

DatabaseReference ref = FirebaseDatabase.instance.ref("posts/123");

TransactionResult result = await ref.runTransaction((Object? post) {
  // ...
});

print('Committed? ${result.committed}'); // true / false
print('Snapshot? ${result.snapshot}'); // DataSnapshot

Bir işlemin iptal edilmesi

Bir işlemi güvenli bir şekilde iptal etmek istiyorsanız, bir AbortTransactionException oluşturmak için Transaction.abort() öğesini çağırın:

TransactionResult result = await ref.runTransaction((Object? user) {
  if (user !== null) {
    return Transaction.abort();
  }

  // ...
});

print(result.committed); // false

Atomik sunucu tarafı artışları

Yukarıdaki kullanım durumunda veritabanına iki değer yazıyoruz: gönderiye yıldız veren/yıldızını kaldıran kullanıcının kimliği ve artan yıldız sayısı. Kullanıcının gönderiye başrol oynadığını zaten biliyorsak, işlem yerine atomik artış işlemini kullanabiliriz.

void addStar(uid, key) async {
  Map<String, Object?> updates = {};
  updates["posts/$key/stars/$uid"] = true;
  updates["posts/$key/starCount"] = ServerValue.increment(1);
  updates["user-posts/$key/stars/$uid"] = true;
  updates["user-posts/$key/starCount"] = ServerValue.increment(1);
  return FirebaseDatabase.instance.ref().update(updates);
}

Bu kod bir işlem işlemi kullanmadığından, çakışan bir güncelleme olması durumunda otomatik olarak yeniden çalıştırılmaz. Ancak artırma işlemi doğrudan veritabanı sunucusunda gerçekleştiği için çakışma ihtimali yoktur.

Bir kullanıcının daha önce yıldız eklediği bir gönderiye yıldız eklemesi gibi uygulamaya özel çakışmaları tespit etmek ve reddetmek istiyorsanız, o kullanım durumu için özel güvenlik kuralları yazmalısınız.

Verilerle çevrimdışı çalışma

Bir istemcinin ağ bağlantısını kaybederse uygulamanız düzgün çalışmaya devam edecektir.

Firebase veritabanına bağlı her istemci, tüm etkin verilerin kendi dahili sürümünü korur. Veriler yazıldığında öncelikle bu yerel versiyona yazılır. Firebase istemcisi daha sonra bu verileri uzak veritabanı sunucularıyla ve diğer istemcilerle "en iyi çabayı" esas alarak senkronize eder.

Sonuç olarak, veritabanına yapılan tüm yazma işlemleri, sunucuya herhangi bir veri yazılmadan önce yerel olayları anında tetikler. Bu, uygulamanızın ağ gecikmesine veya bağlantısına bakılmaksızın yanıt vermeye devam edeceği anlamına gelir.

Bağlantı yeniden kurulduğunda uygulamanız uygun olay kümesini alır, böylece istemci herhangi bir özel kod yazmak zorunda kalmadan mevcut sunucu durumuyla senkronize edilir.

Çevrimiçi ve çevrimdışı yetenekler hakkında daha fazla bilgi edinin bölümünde çevrimdışı davranış hakkında daha fazla konuşacağız.

Sonraki adımlar