本日こそ最終回
次の課題へ掛かります。
https://myapp-7bf76.web.app/#top
トランザクションを使う前
アカウント削除時に
・全体チャットデータの削除
・firestoreに出したプロフィール画像他の参照可能データの削除
これらを対応しなくてはならず。
以下のように記述していたら、案の定うまくいかなかったのです。
// アカウント削除 document.querySelector("#remove_account_form").addEventListener("submit", function(event) { if(confirm('アカウントを削除します。本当によろしいですか?')){ document.querySelector('#remove_account_form button').setAttribute('disabled', true); document.querySelector('#remove_account_form button').innerText = '削除中...'; var user = firebase.auth().currentUser; var credential = firebase.auth.EmailAuthProvider.credential( user.email, getElementValue('remove_account_password') ); user.reauthenticateWithCredential(credential).then(function() { // User re-authenticated. user.delete().then(function() { var db = firebase.firestore(); // チャットデータを削除 var openchatsRef = db.collection("openchats"); var query = openchatsRef.where("uid", "==", user.uid); query.get() .then(function(querySnapshot) { querySnapshot.forEach(function(doc) { db.collection("openchats").doc(doc.id).delete().then(function() { console.log("Document successfully deleted!"); }).catch(function(error) { console.error("Error removing document: ", error); }); // // doc.data() is never undefined for query doc snapshots // console.log(doc.id, " => ", doc.data()); }); }) .catch(function(error) { console.log("Error getting documents: ", error); }); // プロフィールデータを削除 db.collection("users").doc(user.uid).delete().then(function() { console.log("Document successfully deleted!"); }).catch(function(error) { console.error("Error removing document: ", error); }); alert('アカウントを削除しました。ご利用ありがとうございました。'); firebase.auth().signOut(); }).catch(function(error) { var errorCode = error.code; var errorMessage = error.message; alert(errorCode + ', ' + errorMessage); }); }).catch(function(error) { var errorCode = error.code; var errorMessage = error.message; alert(errorCode + ', ' + errorMessage); }); } event.preventDefault(); }, false);
全体チャットから削除する対象のdocumentを検索してきてforEachで削除するまでは正しかったのですが、そちらの処理が終わる前にsignOutが走ってしまいます。
これを解決する為にはトランザクションを使います。
トランザクションを使ってみます
但し公式のサンプルはdocument idが指定されていますが、私の場合は検索してループさせないとなりませんでした。
// Create a reference to the SF doc. var sfDocRef = db.collection("cities").doc("SF"); // Uncomment to initialize the doc. // sfDocRef.set({ population: 0 }); return db.runTransaction(function(transaction) { // This code may get re-run multiple times if there are conflicts. return transaction.get(sfDocRef).then(function(sfDoc) { if (!sfDoc.exists) { throw "Document does not exist!"; } // Add one person to the city population. // Note: this could be done without a transaction // by updating the population using FieldValue.increment() var newPopulation = sfDoc.data().population + 1; transaction.update(sfDocRef, { population: newPopulation }); }); }).then(function() { console.log("Transaction successfully committed!"); }).catch(function(error) { console.log("Transaction failed: ", error); });
完遂した実装は以下の通り。
最初に22行目にもreturnをつけるのに気がつかず、37行目の公開プロフィール情報の削除の方が先に動いてしまい元の木阿弥でした。
22行目にreturnを付け、定義したtransactionが全て完了した段階でthenに飛ばすことができました。
// アカウント削除 document.querySelector("#remove_account_form").addEventListener("submit", function(event) { event.preventDefault(); if(confirm('アカウントを削除します。本当によろしいですか?')){ document.querySelector('#remove_account_form button').setAttribute('disabled', true); document.querySelector('#remove_account_form button').innerText = '削除中...'; var user = firebase.auth().currentUser; var db = firebase.firestore(); // チャットデータを削除 // var openchatsRef = db.collection("openchats"); var user = firebase.auth().currentUser; var credential = firebase.auth.EmailAuthProvider.credential( user.email, getElementValue('remove_account_password') ); const openchatsRef = db.collection('openchats'); // .where("uid", "==", user.uid) const usersRef = db.collection('users').doc(user.uid); return db.runTransaction(function(transaction) { // 全体チャットデータの削除 var query = openchatsRef.where("uid", "==", user.uid); query.get() .then(async function(querySnapshot) { querySnapshot.forEach(function(doc) { return transaction.get(openchatsRef.doc(doc.id)).then(function(sfDoc) { transaction.delete(openchatsRef.doc(doc.id)); }); }); // console.log(querySnapshot.docs); }); // 公開プロフィール情報削除 return transaction.get(usersRef).then(function(sfDoc) { transaction.delete(usersRef); }); }).then(function() { // storageのプロフィール画像削除 var storageRef = firebase.storage().refFromURL(user.photoURL); // Delete the file storageRef.delete().then(function() { // File deleted successfully console.log('File deleted successfully'); user.reauthenticateWithCredential(credential).then(function() { user.delete().then(function() { alert('アカウントを削除しました。ご利用ありがとうございました。'); firebase.auth().signOut(); location.reload(); }); }); }).catch(function(error) { // Uh-oh, an error occurred! }); }).catch(function(error) { console.log(error.line); console.log("Transaction failed: ", error); }); } event.preventDefault(); }, false);
加えて、43行目と46行目でしれっとプロフィール画像の処理を追加し、ひととおりやりたいことはやれたので、エキシビジョン的に動く状態で公開しておきます。
なおgithubはこちらに公開しています。