Android Firebase Cloud Firestore Kullanımı

Merhaba arkadaşlar,

Bu dersimizde Google’un öne sürdüğü Firebase platformunun Cloud Firestore bulut servisini inceleyeceğiz. Ayrıca, cloud firestore özelliğini kullanarak CRUD (Create, Read, Update, Delete ) işlemlerini nasıl yapabileceğimizi de anlatacağım. Bildiğiniz üzere Firebase platformu sayesinde bir çok işlemi gerçekleştirebiliyoruz ve bu platform  ( Realtime database, cloud messaging, crash reporting, storage, authentication gibi özellikleri kullanabilmemizi sağlıyor. ) Firebase ayrıca NOSQL bir veritabandır yani hiç bir SQL sorgusu yazmadan gerekli tabloları oluşturup json formatı ile kontrol ederiz.

 

Bildiğiniz üzere Realtime-Database ( bu dersimizden inceleyebilirsiniz. ) veritabanı işlemleri için kullanılıyordu fakat hızla değişen ve gelişen büyük projelerde kullanılan veritabanı karmaşık bir hale geldiği için daha kullanışlı ve efektif olan Cloud Firestore yapısı karşımıza çıktı. Kısacası Cloud Firestore ; Google’nin büyük ve karmaşık yapıdaki verileri saklamak ve hızlıca sorgulamak amacıyla geliştirdiği dokuman tabanlı Nosql veritabanıdır.

Cloud Firestore Avantajları

Daha iyi sorgulama ve daha yapılandırılmış veriler: Sorgulama yapısı olarak daha çok seçenek bulunmaktadır ayrıca verileriniz belgeler ve koleksiyonlardan oluşmaktadır. Belgelere bağlı olarak başka belgeler oluşturulabilir. Böylece iç içe (Nested) verileri saklarken ve sorgularken Cloud Firestore daha performanslı ve esnek bir yapı sağlar.

Daha ölçeklendirilebilir bir yapı: Sorgularınızın veri kümenizle değil, sonuç kümenizin boyutuyla ölçeklendiğini belirtmek önemlidir. Dolayısıyla, veri kümeniz ne kadar büyük olursa olsun arama hızlı kalır.

Farklı ücretlendirme modeli: Firebase Realtime Database öncelikle depolama veya ağ bant genişliğine göre ücret alırken, Cloud Firestore öncelikle gerçekleştirdiğiniz işlem sayısına göre ücretlendirir. Buradaki avantaj geliştirdiğiniz uygulamaya göre değişir.

Zengin platform desteği: Bir çok platform da kullanılabilir. (Android, IOS, Web, Java, Node.js, Php, Go, Python..)

Çevrimdışı mod desteği: Offline modu desteği ile internet olmadığında cihazın localinden veriler okunabilir, yazılabilir.   İnternet geldiğinde otomatik olarak cloud veritabanına senkronizasyon yapılır. Yani client tarafındaki her değişiklik cloud veritabanımızı, cloud veritabanındaki her değişiklikte client tarafını etkilemektedir.

Cloud Firestore da verilerimiz koleksiyonlar (collection) halinde düzenlenmiş belgelerde (document) saklanır. Aşağıdaki resimde tam olarak veri saklama mantığını görmekteyiz.

Şimdi konuyu daha iyi anlayabilmek için Firestore uygulamasını oluşturalım. Firestore bağlantısı için Android Studio da bulunan özelliği kullanıyoruz ve hızlı bir şekilde uygulamamızda kullanacağımız yapıyı oluşturuyoruz. Realtime Database dersimde anlattığım gibi bağlantı ayarları Firestore için de aynı şekilde yapılmaktadır bu yüzden uzun uzun oralarda zaman kaybetmeden kodlama kısmına geçelim. Aşağıda gördüğümüz gibi Firestore seçeceğini seçip adım adım anlatılan işlemleri uygulayabilirsiniz. ( Uygulamamızda kullanacağımız Firestore kütüphanesinin gradle tarafına eklenmesini ve gerekli ayarların yapılandırılması sağlamış oluyoruz )

 

En sonunda https://console.firebase.google.com sitesinde projemizi Firestore için oluşturmuş oluyoruz. Android Studio da yer alan uygulamamızla buradaki proje adımız aynı. ( FirebaseCloudFirestoreProject olarak isimlendirdik )

 

Artık Firebase cloud yapımız oluştuğuna göre bazı kural setlerini belirtmemiz gerekiyor. Bu veritabanı üzerinde yazma ve okuma işlemleri yapabilmemiz için aşağıdaki şekilde bir ayarlama yapıyoruz ve yazmayıda okumayıda true olarak belirtiyoruz aksi takdirde veritabanı işlemleri yaparken izin verilmediği için sorunlar yaşabilirsiniz. Buradaki kural seti daha da kapsamlı olabilir onu siz kendiniz istediğiniz şekilde belirleyebilirsiniz. örneğin sadece sisteme authenticate olmuş kişilerin yazma ve okuma yapabilmesi gibi.

allow read, write: if true;

Android uygulamamızın build.gradle kısmı aşağıdaki gibidir. Firestore ile ilgili kütüphane denendencies lerin build.gradle içerisinde yer aldığını görüyoruz.

build.gradle

apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'

android {
    compileSdkVersion 28
    buildToolsVersion "28.0.3"
    defaultConfig {
        applicationId "com.mobilhanem.firebasecloudfirestoreproject"
        minSdkVersion 17
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'com.android.support:cardview-v7:28.0.0'
    implementation 'com.android.support:design:28.0.0'
    implementation 'android.arch.lifecycle:extensions:1.1.1'
    implementation 'com.android.support:multidex:1.0.3'
    implementation 'com.google.firebase:firebase-firestore:15.0.0'
    implementation "com.firebaseui:firebase-ui-firestore:3.3.1"
    implementation "com.github.nikartm:fit-button:1.2.1"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
apply plugin: 'com.google.gms.google-services'

Yapacağımız örnek uygulamada kişi ekleme yapabileceğiz, eklediğimiz kişileri listeleyebileceğiz. Ayrıca kişilerle ilgili queryler oluşturup filtreleme yapabileceğiz. Bu filtrelemeler sonucunda belirli değişkenlere göre sıralama yapabileceğiz. Son olarak kişilerin özelliklerini update edip; seçtiğimiz kişiyi veritabanından silebileceğiz. 

Öncelikle kişi ekleme ve listeme yapacağımız için bizim bir modele ihtiyacımız var kişi bilgilerini içinde tutan bir model oluşturalım:

Person.java

package com.mobilhanem.firebasecloudfirestoreproject;

public class Person {

    private String id;
    private String title;
    private String name;
    private String surname;
    private int age;

    public Person() {
    }

    public Person(String title, String name, String surname, int age) {
        this.title = title;
        this.name = name;
        this.surname = surname;
        this.age = age;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

Modelimizi oluşturduk şimdi kişileri listeleyeceğimiz için ekranda gözükmesini istediğiniz satırları oluşturmamız gerekiyor bunun için bir layout oluşturuyoruz.

person_item.xml 

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
                                    android:id="@+id/cardview"
                                    android:layout_height="wrap_content"
                                    android:layout_width="match_parent"
                                    android:layout_marginStart="10dp"
                                    android:layout_marginEnd="10dp"
                                    android:layout_marginTop="10dp">

    <RelativeLayout
        android:padding="8dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:text="Alper"
            android:id="@+id/personNameAndSurname"
            android:layout_toStartOf="@+id/personAge"
            android:layout_alignParentStart="true"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            android:ellipsize="end"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <TextView
            android:id="@+id/personAge"
            android:text="28"
            android:layout_alignParentTop="true"
            android:layout_alignParentEnd="true"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <TextView
            android:text="Android Developer"
            android:layout_below="@+id/personNameAndSurname"
            android:id="@+id/personTitle"
            android:textSize="18sp"
            android:layout_alignParentStart="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>


    </RelativeLayout>

</android.support.v7.widget.CardView>

Ekran görünümü:

Şimdi Adapter yapısını oluşturuyoruz; burada dikkat edilmesi gereken unsur normalde Adapter oluştururken RecylcerView adapter dan ya da custom adapter şeklinde oluşturulurdu ama biz FirestoreRecyclerAdapter sınıfını kullanıyoruz. Bu adapter da zaten RecyclerView.Adapter dan türeyen Firestore için geliştirilmiş bir adapter yapısıdır.

PersonAdapter.java

package com.mobilhanem.firebasecloudfirestoreproject;

import com.google.firebase.firestore.DocumentSnapshot;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.firebase.ui.firestore.FirestoreRecyclerAdapter;
import com.firebase.ui.firestore.FirestoreRecyclerOptions;

public class PersonAdapter extends FirestoreRecyclerAdapter<Person, PersonAdapter.PersonHolder> {

    private IOnItemClickListener onItemClickListener;

    public PersonAdapter(@NonNull FirestoreRecyclerOptions<Person> options) {
        super(options);
    }

    @Override
    protected void onBindViewHolder(@NonNull PersonHolder holder, int position, @NonNull Person person) {

        holder.personNameAndSurname.setText(person.getName() + " " + person.getSurname());
        holder.personTitle.setText(person.getTitle());
        holder.personAge.setText(String.valueOf(person.getAge()));
    }

    @NonNull
    @Override
    public PersonHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.person_item, viewGroup, false);

        return new PersonHolder(view);
    }


    public void deleteItem(int position) {
        getSnapshots().getSnapshot(position).getReference().delete();
    }

    public void setOnItemClickListener(IOnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }


    public interface IOnItemClickListener {
        void onItemClick(DocumentSnapshot documentSnapshot, int position);
    }

    class PersonHolder extends RecyclerView.ViewHolder {

        TextView personTitle;
        TextView personNameAndSurname;
        TextView personAge;

        public PersonHolder(View itemView) {

            super(itemView);

            personTitle = itemView.findViewById(R.id.personTitle);
            personAge = itemView.findViewById(R.id.personAge);
            personNameAndSurname = itemView.findViewById(R.id.personNameAndSurname);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = getAdapterPosition();
                    if (pos != RecyclerView.NO_POSITION && onItemClickListener != null) {
                        onItemClickListener.onItemClick(getSnapshots().getSnapshot(pos), pos);
                    }

                }
            });
        }


    }
}

Şimdi activity sınıfımızda gerçekleştireceğiniz için işlemleri activity sınıfımızın layout xml oluşturuyoruz. Kişi Ekle ve Liste Sorgula butonu olduğunu görüceksiniz. Butonların altında da RecylcerView konumlandırdığımızı göreceksiniz.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@drawable/app_background"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/topLayout"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:orientation="horizontal">

        <com.github.nikartm.button.FitButton
            android:id="@+id/addPerson"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="48dp"
            android:layout_margin="16dp"
            android:gravity="center"
            app:fb_shape="rectangle"
            app:fb_cornerRadius="15dp"
            app:fb_textColor="#F5F5F5"
            app:fb_text="Kişi Ekle"
            app:fb_textPaddingStart="7dp"
            app:fb_iconPosition="left"
            app:fb_iconColor="#F5F5F5"
            app:fb_backgroundColor="#8BC34A"/>

        <com.github.nikartm.button.FitButton
            android:id="@+id/queryPerson"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="48dp"
            android:layout_margin="16dp"
            android:gravity="center"
            app:fb_shape="rectangle"
            app:fb_cornerRadius="15dp"
            app:fb_textColor="#F5F5F5"
            app:fb_text="Liste Sorgula"
            app:fb_textPaddingStart="7dp"
            app:fb_iconPosition="left"
            app:fb_iconColor="#F5F5F5"
            app:fb_backgroundColor="#8BC34A"/>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/personLayout"
        android:layout_below="@+id/topLayout"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:visibility="gone">

        <EditText
            android:hint="Ünvan giriniz.."
            android:id="@+id/userTitle"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_width="match_parent"/>

        <EditText
            android:hint="Ad giriniz.."
            android:id="@+id/userName"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_width="match_parent"/>

        <EditText
            android:hint="Soyad giriniz.."
            android:id="@+id/userSurname"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_width="match_parent"/>

        <EditText
            android:hint="Yaş giriniz.."
            android:id="@+id/userAge"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_width="match_parent"/>

        <com.github.nikartm.button.FitButton
            android:layout_gravity="center"
            android:id="@+id/savePerson"
            android:layout_width="100dp"
            android:layout_height="48dp"
            android:layout_margin="16dp"
            android:gravity="center"
            app:fb_shape="rectangle"
            app:fb_cornerRadius="15dp"
            app:fb_textColor="#F5F5F5"
            app:fb_text="Kaydet"
            app:fb_textPaddingStart="7dp"
            app:fb_iconPosition="left"
            app:fb_iconColor="#F5F5F5"
            app:fb_backgroundColor="#8BC34A"/>


    </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recylcerView"
        android:scrollbars="vertical"
        android:layout_below="@+id/personLayout"
        android:layout_height="match_parent"
        android:layout_width="match_parent"/>

</RelativeLayout>

 

Ekran görünümü:

 

 

 

 

 

 

 

 

 

 

 

Şimdi bu layout a bağlı olan Activity sınıfımızı oluşturalım.

MainActivity.java

package com.mobilhanem.firebasecloudfirestoreproject;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QuerySnapshot;

import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.firebase.ui.firestore.FirestoreRecyclerOptions;
import com.github.nikartm.button.FitButton;

import java.util.HashMap;
import java.util.Map;


public class MainActivity extends AppCompatActivity {

    public static final String TITLE = "title";
    public static final String NAME = "name";
    public static final String SURNAME = "surname";
    public static final String AGE = "age";
    private FitButton addPerson, savePerson, queryPersonList;
    private View personLayout;
    private FirebaseFirestore firebaseFirestoreDb;
    private EditText userTitle, userName, userSurname, userAge;
    private RecyclerView recyclerView;
    private CollectionReference collectionReference;
    private PersonAdapter adapter;
    private Query query;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getSupportActionBar().hide();

        firebaseFirestoreDb = FirebaseFirestore.getInstance();
        collectionReference = firebaseFirestoreDb.collection("employees");

        query = collectionReference.orderBy(AGE, Query.Direction.ASCENDING);
        //query = collectionReference.whereGreaterThan(AGE, 30);
        //query = collectionReference.whereEqualTo(TITLE, "Android Developer");

        Init();

        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {

            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
                return false;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int position) {

                adapter.deleteItem(viewHolder.getAdapterPosition());
            }
        };
        ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
        mItemTouchHelper.attachToRecyclerView(recyclerView);

        savePerson.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                savePersonDb();
            }
        });

        addPerson.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (personLayout.getVisibility() == View.VISIBLE) {
                    personLayout.setVisibility(View.GONE);
                } else {
                    personLayout.setVisibility(View.VISIBLE);
                }
            }
        });

        queryPersonList.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                collectionReference.whereEqualTo(TITLE, "Android Developer").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<QuerySnapshot> task) {

                        Toast.makeText(getApplicationContext(), "Android Developer sayısı: " + task.getResult().size(), Toast.LENGTH_LONG).show();
                    }
                });
            }
        });
    }

    private void savePersonDb() {

        if (userTitle.getText().toString().length() > 0 && userName.getText().toString().length() > 0 && userSurname.getText().toString().length() > 0 && userAge.getText().toString().length() > 0) {

            Map<String, Object> employeesList = new HashMap<>();
            employeesList.put(TITLE, userTitle.getText().toString());
            employeesList.put(NAME, userName.getText().toString());
            employeesList.put(SURNAME, userSurname.getText().toString());
            employeesList.put(AGE, Integer.parseInt(userAge.getText().toString()));


            firebaseFirestoreDb.collection("employees").document().set(employeesList).addOnSuccessListener(new OnSuccessListener<Void>() {
                @Override
                public void onSuccess(Void aVoid) {
                    clearAllFields();
                    Toast.makeText(getApplicationContext(), "Başarılı Kayıt", Toast.LENGTH_LONG).show();

                }
            }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Toast.makeText(getApplicationContext(), "Genel bir hata oluştu!", Toast.LENGTH_LONG).show();

                }
            });


        } else {

            Toast.makeText(getApplicationContext(), "Lütfen gerekli alanları doldurunuz!", Toast.LENGTH_LONG).show();

        }

    }

    private void clearAllFields() {
        userTitle.setText("");
        userName.setText("");
        userSurname.setText("");
        userAge.setText("");
    }

    private void Init() {
        addPerson = findViewById(R.id.addPerson);
        personLayout = findViewById(R.id.personLayout);
        savePerson = findViewById(R.id.savePerson);
        queryPersonList = findViewById(R.id.queryPerson);
        userTitle = findViewById(R.id.userTitle);
        userName = findViewById(R.id.userName);
        userSurname = findViewById(R.id.userSurname);
        userAge = findViewById(R.id.userAge);
        recyclerView = findViewById(R.id.recylcerView);
    }


    @Override
    protected void onStart() {
        super.onStart();
        FirestoreRecyclerOptions<Person> options = new FirestoreRecyclerOptions.Builder<Person>()
                .setQuery(query, new CustomParser())
                .build();

        adapter = new PersonAdapter(options);

        adapter.setOnItemClickListener(new PersonAdapter.IOnItemClickListener() {
            @Override
            public void onItemClick(final DocumentSnapshot documentSnapshot, int position) {

                final AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                final View customLayout = getLayoutInflater().inflate(R.layout.popup_view, null);
                builder.setView(customLayout);
                builder.setCancelable(false);
                builder.setMessage("Kişi Bilgileri");
                final EditText personNameEdit = customLayout.findViewById(R.id.personNameEdit);
                final EditText personSurnameEdit = customLayout.findViewById(R.id.personSurnameEdit);
                final EditText personTitleEdit = customLayout.findViewById(R.id.personTitleEdit);
                final EditText personAgeEdit = customLayout.findViewById(R.id.personAgeEdit);
                final Person person = documentSnapshot.toObject(Person.class);
                if (person != null) {
                    personNameEdit.setText(person.getName());
                    personSurnameEdit.setText(person.getSurname());
                    personAgeEdit.setText(String.valueOf(person.getAge()));
                    personTitleEdit.setText(person.getTitle());
                }
                builder.setPositiveButton("Güncelle", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {

                        if (person != null && personNameEdit.getText().toString().length() > 0 && personSurnameEdit.getText().toString().length() > 0 && personTitleEdit.getText().toString().length() > 0 && personAgeEdit.getText().toString().length() > 0) {

                            person.setTitle(personTitleEdit.getText().toString());
                            person.setName(personNameEdit.getText().toString());
                            person.setSurname(personSurnameEdit.getText().toString());
                            person.setAge(Integer.parseInt(personAgeEdit.getText().toString()));
                            collectionReference.document(documentSnapshot.getId()).set(person).addOnCompleteListener(new OnCompleteListener<Void>() {
                                @Override
                                public void onComplete(@NonNull Task<Void> task) {
                                    Toast.makeText(getApplicationContext(), "Güncellendi.", Toast.LENGTH_LONG).show();
                                }
                            });
                        }

                        dialogInterface.dismiss();
                    }
                });
                builder.setNegativeButton("Vazgeç", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                    }
                });
                final AlertDialog dialog = builder.create();
                dialog.show();
            }
        });


        recyclerView.setAdapter(adapter);
        adapter.startListening();
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (adapter != null) {
            adapter.stopListening();
        }
    }

}

Cloud Firestore CRUD İşlemleri

Şimdi yukarıda yer alan kod bloğunu incelemeye başlayalım;

  • Firestore veritabanına erişimi sağlamaktadır
firebaseFirestoreDb = FirebaseFirestore.getInstance();
  • Firestore veritabanı içerisinde yer alan collection kısmını elde etmemizi sağlar. Verilen keye göre biz gördüğünüz gibi kod kısmında “employees” olarak tanımladık; eğer var olan keye göre bir collection varsa bize o collection dönmesini sağlar yok ise de kendisi “employees” adında bir collection oluşturur.
collectionReference = firebaseFirestoreDb.collection("employees");
  • MainActivity içerisinde yer alan savePersonDb methodunu incelediğimizde; Kişi Ekle butonuna basıldığında tetiklendiğini görüyoruz. Kullanıcı tarafından girilen değerler Edittext den alınıp HashMap içerisinde key-value şeklinde atılır daha sonra oluşan bu liste aşağıda gördüğünüz gibi document olarak set edilir. Listenerlar yardımcı ile de eğer kaydetme işlemi başarılı ise onSuccess methodu tetiklenir; değil ise onFailure methodu tetiklenir.
            Map<String, Object> employeesList = new HashMap<>();
            employeesList.put(TITLE, userTitle.getText().toString());
            employeesList.put(NAME, userName.getText().toString());
            employeesList.put(SURNAME, userSurname.getText().toString());
            employeesList.put(AGE, Integer.parseInt(userAge.getText().toString()));

            firebaseFirestoreDb.collection("employees").document().set(employeesList).addOnSuccessListener(new OnSuccessListener<Void>() {
                @Override
                public void onSuccess(Void aVoid) {
                    clearAllFields();
                    Toast.makeText(getApplicationContext(), "Başarılı Kayıt", Toast.LENGTH_LONG).show();

                }
            }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Toast.makeText(getApplicationContext(), "Genel bir hata oluştu!", Toast.LENGTH_LONG).show();

                }
            });

Kişi ekleme ekranı:

Aşağıdaki resimde Console firebase kısmına baktığımızda kaydedilen verilerin nasıl gözüktüğünü görmekteyiz:

not: Her bir belgenin kendine özgü bir ID si olmak zorundadır. Eğer biz yaratırken bir ID vermezsek Firestore otomatik olarak bir ID yaratacaktır. 

  • MainActivity sınıfı içerisinde yer alan onStart da startListening kodunu göreceksiniz. Bu yapı Adaptera özgüdür (FirestoreRecyclerAdapter) ve ekranda listelediğimiz öğelerin sürekli olarak dinlenmesi sağlar. Yani kaydettiğimiz verilerde herhangi bir değişiklik olması durumunda Adapter tetiklenerek RecyclerView kendini otomatik olarak günceller.
adapter.startListening();
  • MainActivity sınıfı içerisinde yer alan onStop da stopListening kodunu göreceksiniz. Bu yapıda yine Adaptera (FirestoreRecyclerAdapter) özgüdür. Activity onStop tetiklendiğinde dinleme olayının durdurulmasını sağlar.
adapter.stopListening();
  • MainActivity sınıfı içerisinde yer alan Query ise dersi anlatırken bahsettiğimi sorguların oluşturulmasını sağlar. Kodu incelediğinizde collection yapımızı yaşa göre sıraladığımızı göreceksiniz. Yaşa göre sıralarkende ister küçükten büyüğe, istersek de büyükten küçüğe sıralama query si oluşturabiliriz.
query = collectionReference.orderBy(AGE, Query.Direction.ASCENDING);
  • MainActivity sınıfı içerisinde Adapter sınıfımıza gönderdiğimiz FirestorerRecyclerOptions yapısını göreceksiniz. Bu yukarıdaki query sınıfından gelen sorguyu alır ve build ederek oluşturmasını sağlar. Daha sonra Adaptera bunu göndererek veri kümesinin istenildiği şekilde oluşturulmasını sağlar.
FirestoreRecyclerOptions<Person> options = new FirestoreRecyclerOptions.Builder<Person>()
                .setQuery(query, new CustomParser())
                .build();
  • MainActivity sınıfı içerisinde ItemTouchHelper yapısını göreceksiniz bu yapı CardView lerin swipe edilmesini sağlar. Sağa ve sola doğru swipe edebiliriz. Biz koddan sadece sol doğru kaydırılmasına izin verdik. Sola doğru kaydırılınca ilgili verimizin veritabanından silinmesini ve listemizin güncellenmesini istiyorsak; adapter sınıfında oluşturduğumuz deleteItem çağırıyoruz.
 ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {

            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
                return false;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int position) {

                adapter.deleteItem(viewHolder.getAdapterPosition());
            }
        };
        ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
        mItemTouchHelper.attachToRecyclerView(recyclerView);

Adapter sınıfı içerisinde yer alan deleteItem ise; gelen position değerine göre Snapshot ler içerisinden ilgili dokumanı bulup silinmesi sağlamaktadır.

getSnapshots().getSnapshot(position).getReference().delete();
  • Şimdi de güncelleme işlemine bakalım; PersonAdapter yapısı içerisinde bir interface oluşturduk daha sonra listelenen verilerden herhangi birine tıklandığında tıklanma olayını MainActivity sınıfında yakaladık. Oluşturduğumuz interface yardımı ile onItemClick methoduna tıklanılan DocumentSnapshot değerini gönderdik.
 itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = getAdapterPosition();
                    if (pos != RecyclerView.NO_POSITION && onItemClickListener != null) {
                        onItemClickListener.onItemClick(getSnapshots().getSnapshot(pos), pos);
                    }

                }
            });

Listelenen verilerden herhangi birine tıklandığında da kullanıcıya bir AlertDialog gösterdik. Yukarıda anlattığım DocumentSnapshot değerini aldıktan sonra Person objesine çevirdik. Sonuçta kaydettiğimiz veriler aslında birer Person sınıfı.

Person person = documentSnapshot.toObject(Person.class);

Artık Person objesi elimizde var kullanıcının listeden tıkladığı değer hangisi ise onunla ilgili verileri biliyoruz o verileri Edittext lere set ettik. Kullanıcı set edilen bu değerlerde değişiklik yapıp Güncelle butonuna bastığında ise aşağıdaki kod tetiklendi. Burada önemli nokta ilgili dokumanın id si güncelleme işleminide id ile yaptığımızı görüyorsunuz. set methodu ile de Person objesini güncelledik eğer güncelleme işlemi tamamlanırsa da onComplete methodunun tetiklendiğini göreceksiniz.

  person.setTitle(personTitleEdit.getText().toString());
  person.setName(personNameEdit.getText().toString());
  person.setSurname(personSurnameEdit.getText().toString());
  person.setAge(Integer.parseInt(personAgeEdit.getText().toString()));
  collectionReference.document(documentSnapshot.getId()).set(person).addOnCompleteListener(new OnCompleteListener<Void>() {
     @Override
     public void onComplete(@NonNull Task<Void> task) {
          Toast.makeText(getApplicationContext(), "Güncellendi.", Toast.LENGTH_LONG).show();
   }
});

Kişi güncelleme ekranı :

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • Liste Sorgula butonuna basıldıktan sonra gerçekleşen işlemleri incelersek; whereEqualTo ile Title değeri “Android Developer” olan verileri getirmesini istiyoruz. İşlem bitince onComplete methodu tetikleniyor ve gelen task üzerinden task.getResult().size() dediğimiz de ise kaç tane değer döndüğünü görmüş oluyoruz.
collectionReference.whereEqualTo(TITLE, "Android Developer").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {

      Toast.makeText(getApplicationContext(), "Android Developer sayısı: " + task.getResult().size(), Toast.LENGTH_LONG).show();
    }
});

 

Evet arkadaşlar Firebase Cloud Firestore Kullanımını temel bir proje yaparak sizlere anlatmaya çalıştım. Uygulamamızda CRUD (Create – Read- Update – Delete) işlemlerini inceledik. Projeyi indirip kaynak kodlarını da inceleyebilirsiniz. Firestore yapısını sıfırdan kendiniz oluşturabilirsiniz buradaki anlatımda kendi console firebase yapımı kullandığım için verileri console tarafından da değiştirmek isterseniz bu mümkün olmayacaktır. O yüzden dersteki kodları kendi projenizde uygulamanız daha verimli olacaktır. Umarım herkese faydalı olur.

Tüm Android Ders, Proje ve Kaynak Kodlar için tıklayınız.

Mobilhanem.com üzerinden anlattığımız mobil uygulama geliştirme derslerine devam edeceğiz. Konu hakkında sorunuzu yorum alanından sorabilirsiniz. Konu dışı sorularınızı ve tüm yazılımsal sorularınızı sorucevap.mobilhanem.com sitemizden de sorabilirsiniz.

Bir dahaki dersimizde görüşmek dileğiyle..

30

Alper Beyler

Yüksek Lisans: Çankaya Üniversitesi / Bilgisayar Mühendisliği
Lisans: Çankaya Üniversitesi / Bilgisayar Mühendisliği (4/3.30) (2010-2014)
Lisans : Viyana Teknik Üniversitesi / Bilgisayar Bilimleri (2013)

Yorum Yaz

Haftalık Bülten

Mobilhanem'de yayınlanan dersleri haftalık mail almak ister misiniz?