update method in adonisjs controller - javascript

How to update data in the database without creating a new one?
async update ({ params, request, response }) {
const product = await Product.find(params.id)
product.name = request.input('name')
product.descr = request.input('descr')
product.qtd = request.input('qtd')
product.sub_descr = request.input('sub_descr')
product.price = request.input('price')
product.save()
return response.redirect('/')
}
This code is creating a new instance, the product.update() method returns me an error

Adonisjs lucid supports automatic inserts & updates - meaning it intelligently updates or inserts a record based on the input.
According to the docs:
The save method persists the instance to the database, intelligently determining whether to create a new row or update the existing row.
However if you want to perform an update/bulk update you can always build a query as mentioned here.
But the problem you might face it
Bulk updates don’t execute model hooks.
Example for Insert/Update:
const User = use('App/Models/User')
const user = new User()
user.username = 'virk'
user.email = 'foo#bar.com'
// Insert
await user.save()
user.age = 22
// Update
await user.save()
Example for Bulk Update:
const User = use('App/Models/User')
await User
.query()
.where('username', 'virk')
.update({ role: 'admin' })

Related

How to create a nested collection when creating a user in Firebase / Firestore where users can save bookmarked items

I want to be able to have a nested collection in firebase/firestore where I can save an authenticated users favorites. I was trying to create the collection when the user is created so I can just read/write to it later but I can't figure out how to create the collection. I have something like this:
//This function creates a new user. If the user already exists, no new document will be created
export const createUserDocumentFromAuth = async (
userAuth,
additionalInfo = {}
) => {
if (!userAuth) return;
const userDocRef = doc(db, 'users', userAuth.uid); //database instance, collection, identifier
const bookmarkRef = doc(db, 'users', userAuth.id, 'bookmarks'); //This triggers error
const userSnapshot = await getDoc(userDocRef);
if (!userSnapshot.exists()) {
//If user snapshot doesn't exist - create userDocRef
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await setDoc(userDocRef, {
displayName,
email,
createdAt,
...additionalInfo,
});
setDoc(bookmarkRef, { //Try to create a bookmarks collection here
favorites: []
})
} catch (error) {
console.log('Error creating user', error.message);
}
}
//if user data exists
return userDocRef;
};
I can create the user just fine but not another collection at the same time. I also tried just creating the collection when a signed-in user clicks on the bookmark button like this but I get a type error in both cases Uncaught (in promise) TypeError: n is undefined every time.
export const addBookmarkForUser = async (userAuth, showId) => {
const bookmarkRef = doc(db, 'users', userAuth.id, 'bookmarks');
try {
await setDoc(bookmarkRef, {
favorites: showId
});
}catch(error){
console.log('error creating bookmark', error.message)
}
};
I'm pretty new to Firebase / Firestore and all I want is to be able to save an item id in an array for an individual user when they click a button. If saving in an array is not ideal or there is any better way to do this, I am open to any suggestions at this point.
I was trying to create the collection when the user is created so I
can just read/write to it later but I can't figure out how to create
the collection.
A (sub)collection is only created when you create the first document in it. There is no way to materialize an empty collection without a document.
And it is normal that you get an error when using the doc() method as follows
const bookmarkRef = doc(db, 'users', userAuth.id, 'bookmarks');
because this method is used to create a DocumentReference and therefore you need to pass a path with an even number of path segments. In you case you pass 3 segments.
You could very well define the CollectionReference for the bookmarks subcollection as follows, using the collection() method and passing the 3 segments
const bookmarkRef = collection(db, 'users', userAuth.id, 'bookmarks');
but, until you add a document in it, it will not exist in the database.
Conclusion: You will automatically create the user's bookmarks subcollection the first time you create a bookmark for the user.
For example:
const bookmarksCollectionRef = collection(db, 'users', userAuth.id, 'bookmarks');
await bookmarksCollectionRef.add({ ... })

Strapi: query rooms with and without user ID

I want to return some default data along with the authenticated data. So basically I have a table called rooms with two columns user and owner_id with relation to another table. I have a few data which does not have any owner_id associated with it. Those are my default data and I want to always send them and then from within my app I can create custom room which will have owner_id associated with it and for those I want to return only the authenticated users data along with the default one. But my problem is either I can send only the authenticated data or all of the data from default to all authenticated users data. Giving the code and screenshots please help.
async find(ctx) {
const owner_id = ctx.state.user.id;
const dname = await strapi.services.rooms.find();
const name = await strapi.services.rooms.find({owner_id})
return sanitizeEntity(name, { model: strapi.models.rooms });},
Assuming you are using PostGreSQL with strapi, you can easily fetch the rooms which have owner linked and the default by using strapi.query instead of strapi.services
async find(ctx) {
const resp = await strapi
.query('rooms')
.model.query(function (qb) {
if (ctx.state.user) qb.where('owner_id', ctx.state.user.id);
qb.orWhere('owner_id', null);
})
.fetchAll();
const data = resp.toJSON();
return data
}

Create a firestore doc for each auth user in Nextjs (only using sign in with Google)

I am building a user auth system with Nextjs
I am trying to create a document within firestore for each user in my firebase authentication system. I was easily able to do this in previous projects when creating an account with email and password but with the 'sign in with google' feature I can't seem to figure out how.
I don't want to create a new document every time the user logs in..
My only idea is this:
When user signs in, loop through all firestore documents and see if the users e-mail matches any firestore doc email. If not, create document, else return.
I feel like there is another way though..
Simplest way would be to make a custom hook that can be used anywhere across the application.
First in the _app file inside useeffect hook simply try to get the data from doc if data exist well it means user document is already there and if data does not exists, we need to create a document for that, quite simple. Let's see the code now
Make sure you read comments written inside the code to better understand
In _app.js,
useEffect(async () => {
// now this checks if user is logged in or not
const unsubscribe = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
// if logged in it simply passes the userAuth object to handle user profile
// which is a custom hook to check if document for this user pre-exist or not!
// if there wont be any document it will go and create a document and return
// that document.
// If already there is a document created it will simply return that.
const userRef = await handleUserProfile(userAuth);
userRef.onSnapshot((snapshot) => {
// later you can save currentUsr value in any of the state to use it later
const currentUsr = {
id: snapshot.id,
...snapshot.data(),
};
}
});
}
}
});
return () => unsubscribe();
}, []);
Now the custom hook to check if document is already there or not, here comes the tricky part.
export const handleUserProfile = async (userAuth) => {
// as a second check it check if falsy values are returned
if (!userAuth) return;
const { uid } = userAuth;
// first it tries to get data from that uid
const userRef = firestore.doc(`users/${uid}`);
const snapshot = await userRef.get();
// checks if snapshot exist
if (!snapshot.exists) {
// if snapshot does not exist, it will simply create a document with document
// name as this 'uid'
const { displayName, email } = userAuth;
const timeStamp = new Date();
try {
// making use of same userRef that we created above to create
await userRef.set({
displayName,
email,
createdAt: timeStamp,
});
} catch (error) {}
}
// if snapshot exist it will simply return the userRef which contains the
// document only.
return userRef;
};
Voila! :)
There is no reason why you should not use the onAuthStateChanged event on auth. A write would cost you the same as a read to check if the data is already there. But with a read you would sometimes need also a write. In total only writes every time come less expensive in read/write actions.
Just listen to auth state changes and update your firestore data each time it changes:
firebase.auth().onAuthStateChanged(async (user) => {
if (user) {
await firebase.firestore()
.collection("users")
.doc(user.uid)
.set(data, {merge:true});
// User is signed in.
}
});
Make sure to use set with merge turned on. That will ensure that the data will be created if it doens't exist and update only the field you want to update.
Also make sure to store the data under the user uid. With that you ensure that each user has an unique idenfier. It is a bad practice to store users under the email. One of the reasons for that is that emails could have chars that are not supported as keys so would need to remove those when saving and add them again when reading the keys.
Firestore won't create duplicate docs if created when signing in with Google.. so this works:
const signInWithGoogle = () => {
fire
.auth()
.signInWithPopup(google_provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
var credential = result.credential;
// This gives you a Google Access Token. You can use it to access the Google API.
var token = credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
})
.catch((error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
})
// CREATE USER DATA IN FIRESTORE
.then(async () => {
const data = {
//ADD DATA HERE
};
await fire
.firestore()
.collection("users")
.doc(fire.auth().currentUser.email)
.set(data);
});
};

how to get documents with user information (firestore)

I have collection with documents.
Structure (fields):
questions
- question
- userID
and my project uses Firebase Authentication. How to get questions with data about author of each question (avatar and name) in javascript.
You need to store every user in collection (ex. 'users' ) after register, and then make leftJoin with both questions and users collections.
something like this:
async function getQuestionsWithAuthors() {
const result = [];
const questionsSnap = await firebase.firestore().collection('questions').get();
for(const doc of questionsSnap.docs) {
const questionData = doc.data();
const userData = await firebase.firestore().collection('users').doc(questionData.userID).get();
result.push({...questionData, user: userData});
}
return result
}

ORM: Updating all models of a given instance

I'm having an conceptual issue on how to update all instances of a model once I have updated it.
Imagine the following method renameUser (could be any ORM):
async function renameUser(userId: number, newUsername: string) {
const user = await User.findByPk(userId);
await user.update({ username: newUsername });
}
And the following usage:
const user = await User.create({ username: "old" });
// user.username === "old"
this.renameUser(user.id, "new");
// still, user.username === "old"
Obviously this problem wouldn't exist if I would pass the user object directly into the update method, but that only works in this simple example - In my case it is actually not possible to share the same instance, since it can be modified via hooks in an entirely different context.
So, one simple solution would be to call user.reload() after the call, which will pull the latest user data from the database:
const user = await User.create({ username: "old" });
// user.username === "old"
this.renameUser(user.id, "new");
user.reload();
// now: user.username === "new"
However, this requires me to know that the renameUser method will change my user object. In this case it is obvious, but if the method is called that's not always possible.
Is there any pattern I can use to work around this? One thing which came in my mind was to create a UserFactory which ensures that I only have one instance of a user (indexed by its primary key) at any time, and then update that instance. But I was wondering how others solve it? Is it a common problem?
Why you dont use .save() in updateUser function?
const renameUser = async (userId, newUsername) => {
const user = await User.findByPk(userId);
user.username = newUsername;
await user.save();
return user;
}
and use it like this
const user = await User.create({ username: "old" });
// user.username === "old"
user = await this.renameUser(user.id, "new");
// now, user.username === "new"
You can run your queries more efficiently by just running an update using the Model.update() function instead of querying for an Instance and then updating it.
async function renameUser(userId: number, newUsername: string) {
const [ updatedRowCount, updateRowOnPostgresOnly ] = await User.update({
username: newUsername,
}, {
where: {
id: userId,
},
});
// you can use the updated row count to see if the user changed.
const isUpdated = updatedRowCount > 0;
return isUpdated;
}
Now you can await the result to see if the row changed, even without loading it.
const user = await User.create({ username: "old" });
// user.username === "old"
const isUpdated = await this.renameUser(user.id, "new");
if (isUpdated) {
user.reload();
// now: user.username === "new"
}
Note that in your example you are not using await on the async runameUser() function.
This varies from your question a bit, but if you are already working with Model instances then you can use Instance.changed() to get the changed fields.
const user = await User.create({ username: "old" });
user.username = "old";
const changed = user.changed(); // == [ "username" ]
if (changed.length) {
await user.save();
}
*Note that in that example it checks for changed.length but really Sequelize won't do anything if you await instance.save() and nothing is in instance.changed() - change awareness of save.

Categories

Resources