(İsteğe bağlı) Firebase Emulator Suite ile prototip oluşturma ve test etme
Uygulamanızın Realtime Database'den nasıl okuma ve Realtime Database'e nasıl yazma işlemi yaptığından bahsetmeden önce, Realtime Database işlevlerinin prototipini oluşturmak ve test etmek için kullanabileceğiniz bir araç setini (Firebase Emulator Suite) tanıtalım. Farklı veri modellerini 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.
Realtime Database Emulator, Emulator Suite'in bir parçasıdır. Bu araç, uygulamanızın emüle edilmiş veritabanı içeriği ve yapılandırmasıyla etkileşime geçmesini sağlar. Ayrıca, isteğe bağlı olarak emüle edilmiş proje kaynaklarınızla (işlevler, diğer veritabanları ve güvenlik kuralları) etkileşime geçmesini de sağlar.emulator_suite_short
Realtime Database emülatörünü kullanmak için yalnızca birkaç adım gerekir:
- Emülatöre bağlanmak için uygulamanızın test yapılandırmasına bir kod satırı ekleyin.
- Yerel proje dizininizin kökünden
firebase emulators:startkomutunu çalıştırın. - Uygulamanızın prototip kodundan, her zamanki gibi bir Realtime Database platformu SDK'sı veya Realtime Database REST API'si kullanarak çağrı yapma.
Realtime Database ve Cloud Functions'ı içeren ayrıntılı bir kılavuz mevcuttur. Ayrıca Emulator Suite'e giriş bölümüne de göz atmanız gerekir.
DatabaseReference alma
Veritabanından veri okumak veya veritabanına veri yazmak için DatabaseReference örneğine ihtiyacınız vardır:
DatabaseReference ref = FirebaseDatabase.instance.ref();
Veri yazma
Bu belgede, Firebase verilerini okuma ve yazmanın temelleri açıklanmaktadır.
Firebase verileri bir DatabaseReference öğesine yazılır ve referans tarafından yayınlanan etkinlikler beklenerek veya dinlenerek alınır. Etkinlikler, verilerin ilk durumu için bir kez, veriler her değiştiğinde ise tekrar yayınlanır.
Temel yazma işlemleri
Temel yazma işlemleri için set() kullanarak verileri belirtilen bir referansa kaydedebilir ve bu yoldaki mevcut verilerin yerini alabilirsiniz. Aşağıdaki 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() öğesini bu şekilde kullanmak, belirtilen konumdaki verilerin (alt düğümler dahil) üzerine yazar. Ancak yine de tüm nesneyi yeniden yazmadan bir çocuğu güncelleyebilirsiniz. Kullanıcıların profillerini güncellemesine izin vermek istiyorsanız kullanıcı adını aşağıdaki gibi güncelleyebilirsiniz:
DatabaseReference ref = FirebaseDatabase.instance.ref("users/123");
// Only update the age, leave the name and address!
await ref.update({
"age": 19,
});
update() yöntemi, düğümlere giden bir alt yolu kabul eder. Bu sayede, veritabanındaki birden fazla düğümü aynı anda güncelleyebilirsiniz:
DatabaseReference ref = FirebaseDatabase.instance.ref("users");
await ref.update({
"123/age": 19,
"123/address/line1": "1 Mountain View",
});
Verileri okuma
Değer etkinliklerini işleyerek verileri okuma
Bir yoldaki verileri okumak ve değişiklikleri dinlemek için onValue özelliğini kullanarak DatabaseReference'i dinleyin.DatabaseEvent
Etkinlik sırasında mevcut olduğu şekliyle belirli bir yoldaki verileri okumak için DatabaseEvent kullanabilirsiniz. Bu etkinlik, dinleyici eklendiğinde bir kez, veriler (alt öğeler dahil) her değiştiğinde ise tekrar tetiklenir. Etkinlikte, alt veriler de dahil olmak üzere söz konusu konumdaki tüm verileri içeren bir snapshot özelliği bulunur. Veri yoksa anlık görüntünün exists özelliği false, value özelliği ise null olur.
Aşağıdaki örnekte, bir gönderinin ayrıntılarını veritabanından alan sosyal blog uygulaması gösterilmektedir:
DatabaseReference starCountRef =
FirebaseDatabase.instance.ref('posts/$postId/starCount');
starCountRef.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value;
updateStarCount(data);
});
Dinleyici, DataSnapshot özelliğindeki etkinlik sırasında veritabanında belirtilen konumdaki verileri içeren bir value alır.
Verileri bir kez okuma
get() kullanarak bir kez okuma
SDK, uygulamanızın çevrimiçi veya çevrimdışı olmasına bakılmaksızın veritabanı sunucularıyla etkileşimleri yönetmek için tasarlanmıştır.
Genel olarak, arka uçtaki verilerde yapılan güncellemelerden haberdar olmak için yukarıda açıklanan değer etkinlikleri tekniklerini kullanarak veri okumanız gerekir. Bu teknikler, kullanımınızı ve faturalandırmanızı azaltır. Ayrıca, kullanıcılarınıza internete bağlandıklarında ve internetten çıktıklarında 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() kullanabilirsiniz. get() herhangi bir nedenle sunucu değerini döndüremiyorsa istemci, yerel depolama önbelleğini yoklar ve değer hâlâ bulunamazsa bir hata döndürür.
Aşağıdaki örnekte, kullanıcının herkese açık kullanıcı adının veritabanından tek bir kez nasıl alındığı gösterilmektedir:
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() öğesinin gereksiz kullanımı, bant genişliği kullanımını artırabilir ve performans kaybına yol açabilir. Bu durum, yukarıda gösterildiği gibi gerçek zamanlı bir dinleyici kullanılarak önlenebilir.
once() ile verileri bir kez okuma
Bazı durumlarda, sunucuda güncellenmiş bir değer olup olmadığını kontrol etmek yerine yerel önbellekteki değerin hemen döndürülmesini isteyebilirsiniz. Bu gibi durumlarda, yerel disk önbelleğindeki verileri hemen almak için once() kullanabilirsiniz.
Bu, yalnızca bir kez yüklenmesi gereken ve sık sık değişmesi ya da aktif dinleme gerektirmesi beklenmeyen veriler için kullanışlıdır. Örneğin, önceki örneklerdeki blog uygulaması, kullanıcı yeni bir gönderi yazmaya başladığında 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üncelleme
Bir düğümün belirli alt öğelerine diğer alt düğümleri üzerine yazmadan aynı anda yazmak için update() yöntemini kullanın.
update() işlevini çağırırken anahtar için bir yol belirterek alt düzeydeki alt değerleri güncelleyebilirsiniz. Veriler daha iyi ölçeklendirme için birden fazla konumda depolanıyorsa veri dağıtımı kullanarak bu verilerin tüm örneklerini güncelleyebilirsiniz. Örneğin, bir sosyal blog uygulaması bir gönderi oluşturmak ve aynı anda hem son etkinlik feed'ini hem de gönderiyi yayınlayan kullanıcının etkinlik feed'ini güncellemek isteyebilir. Bunu yapmak için blog uygulaması şu gibi 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 örnekte, push() kullanılarak /posts/$postid adresindeki tüm kullanıcıların yayınlarını içeren düğümde bir yayın oluşturulur ve aynı anda key ile anahtar alınır. Ardından anahtar, kullanıcının /user-posts/$userid/$postid adresindeki yayınlarında ikinci bir giriş oluşturmak için kullanılabilir.
Bu yolları kullanarak, update() için tek bir çağrıyla JSON ağacındaki birden fazla konumda eşzamanlı güncellemeler yapabilirsiniz. Örneğin, bu örnekte yeni gönderi her iki konumda da oluşturulur. Bu şekilde yapılan eşzamanlı güncellemeler atomiktir: Tüm güncellemeler başarılı olur veya tüm güncellemeler başarısız olur.
Tamamlama geri araması ekleme
Verilerinizin ne zaman işlendiğini öğrenmek istiyorsanız tamamlanma geri çağırmalarını kaydedebilirsiniz. Hem set() hem de update(), Future değerini döndürür. Bu değere, yazma işlemi veritabanına işlendiğinde ve çağrı başarısız olduğunda çağrılan başarı ve hata geri çağırmalarını ekleyebilirsiniz.
FirebaseDatabase.instance
.ref('users/$userId/email')
.set(emailAddress)
.then((_) {
// Data saved successfully!
})
.catchError((error) {
// The write failed...
});
Verileri silin
Verileri silmenin en basit yolu, verilerin konumuna yapılan bir referansta remove() işlevini çağırmaktır.
Ayrıca set() veya update() gibi başka bir yazma işlemi için değeri null olarak belirterek de silebilirsiniz. Bu tekniği update() ile birlikte kullanarak tek bir API çağrısında birden fazla alt öğeyi silebilirsiniz.
Verileri işlem olarak kaydetme
Eşzamanlı değişiklikler nedeniyle bozulabilecek verilerle (ör. artımlı sayaçlar) çalışırken runTransaction() işlevine bir işlem işleyici ileterek işlem kullanabilirsiniz. İşlem işleyici, verilerin mevcut durumunu bağımsız değişken 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 tekrar ç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ını kaldırmasına izin verebilir, ayrıca bir gönderinin kaç yıldız aldığını aşağıdaki gibi takip edebilirsiniz:
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ığında etkinlikler oluşturulur. Bu nedenle, işlevi birden çok kez çalıştırırsanız ara durumları görebilirsiniz.
Bu ara durumları bastırmak ve bunun yerine işlemler tamamlanana kadar bekleyip etkinlikleri tetiklemek için applyLocally değerini false olarak ayarlayabilirsiniz:
await ref.runTransaction((Object? post) {
// ...
}, applyLocally: false);
Bir işlemin sonucu, işlemin kaydedilip kaydedilmediği ve yeni anlık görüntü gibi bilgileri içeren bir TransactionResult olur:
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
İşlemi iptal etme
Bir işlemi güvenli bir şekilde iptal etmek istiyorsanız Transaction.abort()'ı arayarak AbortTransactionException atı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 alanında, veritabanına iki değer yazıyoruz: gönderiye yıldız ekleyen/kaldıran kullanıcının kimliği ve artırılmış yıldız sayısı. Kullanıcının yayını yıldızladığını zaten biliyorsak işlem yerine atomik artırma işlemi 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ığı için çakışan bir güncelleme varsa otomatik olarak yeniden çalıştırılmaz. Ancak artırma işlemi doğrudan veritabanı sunucusunda gerçekleştiği için çakışma olasılığı yoktur.
Uygulamaya özgü çakışmaları (ör. kullanıcının daha önce yıldızladığı bir gönderiyi tekrar yıldızlaması) tespit edip reddetmek istiyorsanız bu kullanım alanı için özel güvenlik kuralları yazmanız gerekir.
Verilerle çevrimdışı çalışma
Bir istemcinin ağ bağlantısı kesilirse uygulamanız doğru şekilde çalışmaya devam eder.
Firebase veritabanına bağlı her istemci, etkin verilerin kendi dahili sürümünü korur. Veriler yazılırken önce bu yerel sürüme yazılır. Ardından Firebase istemcisi, bu verileri uzak veritabanı sunucularıyla ve diğer istemcilerle "en iyi çaba" prensibine göre senkronize eder.
Sonuç olarak, veritabanına yapılan tüm yazma işlemleri, sunucuya herhangi bir veri yazılmadan önce yerel etkinlikleri anında tetikler. Bu sayede uygulamanız, ağ gecikmesinden veya bağlantıdan bağımsız olarak yanıt vermeye devam eder.
Bağlantı yeniden kurulduğunda uygulamanız, istemcinin mevcut sunucu durumuyla senkronize olması için uygun etkinlik kümesini alır. Bu işlem için özel kod yazmanız gerekmez.
Çevrimdışı davranış hakkında daha fazla bilgiyi Online ve çevrimdışı özellikler hakkında daha fazla bilgi başlıklı makalede bulabilirsiniz.