Maintenant que vous êtes à l'aise avec le fonctionnement de Firestore, nous allons pouvoir mettre à profit toutes ces nouvelles connaissances pour améliorer la gestion de nos utilisateurs.
En effet, nous aimerions pouvoir les intégrer dans notre base de données sous la Collection users, pour pouvoir, par la suite, indiquer si ces derniers sont mentors ou encore modifier leur nom d'utilisateur par exemple.
Impatient d'implémenter tout ça ? C'est parti !
Créez des requêtes sur Firestore
Nous allons donc créer un ensemble de requêtes réseau qui vont nous permettre de récupérer, créer, mettre à jour ou encore supprimer des documents directement sur le Firestore.
Le mieux dans tout cela ? Généralement, sur une infrastructure plus standard, vous auriez dû demander à un développeur web de vous créer une API afin de vous ouvrir un accès sécurisé à la base de données, puis utiliser une librairie comme Retrofit pour effectuer vos appels réseaux (comme nous l'avons vu dans un précédent cours).
Mais ça, c'était avant ! Avec Firebase, vous allez voir à quel point ce processus devient facile :
Place au code ! Nous allons modifier notre UserRepository comme suit :
Extrait de UserRepository.java :
public final class UserRepository {
private static final String COLLECTION_NAME = "users";
private static final String USERNAME_FIELD = "username";
private static final String IS_MENTOR_FIELD = "isMentor";
...
// Get the Collection Reference
private CollectionReference getUsersCollection(){
return FirebaseFirestore.getInstance().collection(COLLECTION_NAME);
}
// Create User in Firestore
public void createUser() {
FirebaseUser user = getCurrentUser();
if(user != null){
String urlPicture = (user.getPhotoUrl() != null) ? user.getPhotoUrl().toString() : null;
String username = user.getDisplayName();
String uid = user.getUid();
User userToCreate = new User(uid, username, urlPicture);
Task<DocumentSnapshot> userData = getUserData();
// If the user already exist in Firestore, we get his data (isMentor)
userData.addOnSuccessListener(documentSnapshot -> {
if (documentSnapshot.contains(IS_MENTOR_FIELD)){
userToCreate.setIsMentor((Boolean) documentSnapshot.get(IS_MENTOR_FIELD));
}
this.getUsersCollection().document(uid).set(userToCreate);
});
}
}
// Get User Data from Firestore
public Task<DocumentSnapshot> getUserData(){
String uid = this.getCurrentUserUID();
if(uid != null){
return this.getUsersCollection().document(uid).get();
}else{
return null;
}
}
// Update User Username
public Task<Void> updateUsername(String username) {
String uid = this.getCurrentUserUID();
if(uid != null){
return this.getUsersCollection().document(uid).update(USERNAME_FIELD, username);
}else{
return null;
}
}
// Update User isMentor
public void updateIsMentor(Boolean isMentor) {
String uid = this.getCurrentUserUID();
if(uid != null){
this.getUsersCollection().document(uid).update(IS_MENTOR_FIELD, isMentor);
}
}
// Delete the User from Firestore
public void deleteUserFromFirestore() {
String uid = this.getCurrentUserUID();
if(uid != null){
this.getUsersCollection().document(uid).delete();
}
}
}
Ce dernier nous permet de réaliser facilement des requêtes réseau sur le Firestore et de gérer à notre place beaucoup de choses automatiquement, sans configuration particulière (par exemple le mode Hors-Ligne). Il dispose de deux principales méthodes :
document()
qui permet de récupérer la référence d'un document dont le chemin est renseigné en paramètre de la méthode. Retourne un objet de type DocumentReference.collection()
qui permet de récupérer la référence d'une collection dont le chemin est renseigné en paramètre de la méthode. Retourne un objet de type CollectionReference.
Tout est une question de référence sur Firestore !
Une fois que nous obtenons la référence d'une donnée, nous pouvons réaliser des requêtes réseau de type CRUD :
Pour une référence DocumentReference :
get()
récupère le Document correspondant en fonction de l'identifiant passé en paramètre.set()
crée (ou écrase) un Document à partir d'un objet POJO.update()
met à jour les champs du Document correspondant.delete()
supprime le Document correspondant.collection()
accède à une Collection qui se trouve à l'intérieur du Document.
Pour une référence CollectionReference :
add()
ajoute un nouveau Document.document()
accède à un Document ou en crée un nouveau. Si un identifiant est passé en paramètre de celle-ci, ce dernier sera utilisé. Sinon, Firestore en générera un automatiquement.
Et bien entendu, vous pouvez "chaîner" (sous certaines conditions) l'ensemble de ces méthodes les unes à la suites des autres en fonction de la manière dont vous avez structuré votre base de données.
Sachez simplement qu'un Document peut contenir uniquement (en plus de ses champs) une ou plusieurs Collections, et qu'une Collection peut contenir uniquement un ou plusieurs Documents.
Mais comment définissons-nous l'identifiant unique de chaque utilisateur de notre base de données ?
Bonne question ! En effet, dans une base de données, chaque utilisateur doit posséder un identifiant unique afin de le reconnaître spécifiquement et surtout sans l'ombre d'un doute.
Par défaut, si vous ajoutez un Document sur le Firestore sans mentionner d'identifiant, ce dernier en générera un pour vous.
Cependant dans notre cas, nous n'avons pas besoin que Firestore nous génère un identifiant, puisque que le module "Authentication" l'a déjà fait pour nous ! Ainsi, nous allons utiliser l'identifiant issu de notre précédente authentification (Facebook, Google ou email) grâce à la méthodeuser.getUid()
(correspondant en vrai à FirebaseAuth.getInstance().getCurrentUser().getUid()
).
Cet identifiant sera utilisé pour créer un Document (donc un utilisateur) et l'identifier uniquement.
Avec toutes ces informations, je vous laisse relire les différentes méthodes (et donc requêtes) que nous avons ajoutées à notre Repository, et qui doivent maintenant vous paraître beaucoup plus compréhensibles.
Implémentez des requêtes Firestore sur l'interface graphique
Maintenant que nos requêtes CRUD sont créées, il faut les appeler au bon endroit. Vous l'aurez compris, pour les utiliser dans notre interface graphique, il faudra passer par notre Manager. Pour rappel, nous souhaitons exécuter les méthodes suivantes de la classe :
createUser()
pour créer un utilisateur sur le Firestore. Nous l'appellerons dès que nous saurons que l'utilisateur est correctement authentifié à Firebase, c'est-à-dire dans MainActivity.getUserData()
pour récupérer les informations d'un utilisateur sur le Firestore afin de mettre à jour nos vues dans l'activité ProfileActivity.updateUsername()
etupdateIsMentor()
pour mettre à jour les informations de l'utilisateur (son nom et si ce dernier est un mentor ou non) sur Firestore depuis l'activité ProfileActivity.deleteUserFromFirestore()
pour supprimer un utilisateur du Firestore quand ce dernier clique sur le bouton "Supprimer mon compte".
Commençons par modifier notre UserManager:
Extrait de UserManager :
public class UserManager {
...
public void createUser(){
userRepository.createUser();
}
public Task<User> getUserData(){
// Get the user from Firestore and cast it to a User model Object
return userRepository.getUserData().continueWith(task -> task.getResult().toObject(User.class)) ;
}
public Task<Void> updateUsername(String username){
return userRepository.updateUsername(username);
}
public void updateIsMentor(Boolean isMentor){
userRepository.updateIsMentor(isMentor);
}
public Task<Void> deleteUser(Context context){
// Delete the user account from the Auth
return userRepository.deleteUser(context).addOnCompleteListener(task -> {
// Once done, delete the user datas from Firestore
userRepository.deleteUserFromFirestore();
});
}
}
Explications : Rien de spécial ici à part 2 éléments :
continueWith()
est une fonction qui permet de récupérer le résultat d'une task, d'effectuer un traitement et de renvoyer une nouvelle task avec un nouvel object.
Ici, nous récupérons une task avec un DocumentSnapshot que l'on convertit en objet User. De cette manière depuis notre UI, nous récupérons uniquement ce dont nous avons besoin : un object UserDans la fonction deleteUser, une fois que l'user est supprimé du système d'authentification firebase, nous le supprimons également de notre base de donnée Firestore.
Ensuite modifions notre ProfileActivity.
Extrait de ProfileActivity :
public class ProfileActivity extends BaseActivity<ActivityProfileBinding> {
...
private void setupListeners(){
// Mentor Checkbox
binding.isMentorCheckBox.setOnCheckedChangeListener((compoundButton, checked) -> {
userManager.updateIsMentor(checked);
});
// Update button
binding.updateButton.setOnClickListener(view -> {
binding.progressBar.setVisibility(View.VISIBLE);
userManager.updateUsername(binding.usernameEditText.getText().toString())
.addOnSuccessListener(aVoid -> {
binding.progressBar.setVisibility(View.INVISIBLE);
});
});
...
}
...
private void updateUIWithUserData(){
// If user is logged
if(userManager.isCurrentUserLogged()){
...
getUserData();
}
}
...
private void getUserData(){
userManager.getUserData().addOnSuccessListener(user -> {
// Set the data with the user information
String username = TextUtils.isEmpty(user.getUsername()) ? getString(R.string.info_no_username_found) : user.getUsername();
binding.isMentorCheckBox.setChecked(user.getIsMentor());
binding.usernameEditText.setText(username);
});
}
...
}
Explications : Dans un premier temps, nous ajoutons les events sur la checkbox isMentor et sur le bouton update. Les listeners vont appeler les fonctions adéquates du manager afin de mettre à jour les données.
La fonction getUserData()
va aller récupérer les information de notre utilisateur dans Firestore et mettre à jour nos vues avec les infos correspondantes. Cette fonction est appelée à la création de la vue, si l'utilisateur est connecté.
Enfin, modifions notre MainActivity afin de créer l'utilisateur dans notre base de données une fois que celui-ci est connecté.
Extrait de MainActivity :
public class MainActivity extends BaseActivity<ActivityMainBinding> {
...
// Method that handles response after SignIn Activity close
private void handleResponseAfterSignIn(int requestCode, int resultCode, Intent data){
...
if (requestCode == RC_SIGN_IN) {
// SUCCESS
if (resultCode == RESULT_OK) {
userManager.createUser();
showSnackBar(getString(R.string.connection_succeed));
} else {
// ERRORS
...
}
}
}
}
Explications : Nous appelons simplement la fonctioncreateUser()
de notre manager lorsque la connexion s'est terminée avec succès.
Essayons maintenant tout cela !
Lancez maintenant votre application Android FirebaseOC et réalisez une inscription d'utilisateur. Puis, tentez de modifier ses informations personnelles (nom d'utilisateur et son statut de mentor). Vous devriez voir apparaître les informations mises à jour depuis l'interface Web de Firebase.
Pour terminer, supprimez le compte de l'utilisateur en cliquant sur le bouton "Supprimer mon compte" depuis votre application Android FirebaseOC. Les données relatives à cet utilisateur devraient également avoir disparu du Firestore.
En résumé
Vous pouvez accéder à vos données via des CollectionReference et DocumentReference
Il existe diverses méthodes afin de manipuler vos données : get(), set(), update().... Entrainez-vous à les utiliser !