问题
I currently have an app where users can log in and make posts. Next to each post, there is button that if pressed, the current user can send a message to the user who created the post.
I have a posts model and a user model. Each user can post as many posts as they want, but each post only belongs to one user.
const User = db.define(
"User",
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
},
email: {
type: Sequelize.STRING,
},
password: {
type: Sequelize.STRING,
},
},
);
const Post = db.define(
"Post",
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
title: {
type: Sequelize.STRING,
},
subTitle: {
type: Sequelize.STRING,
},
userId: {
type: Sequelize.INTEGER,
},
},
);
User.hasMany(Post,
{ foreignKey: "userId" }
);
Post.belongsTo(User,
{ foreignKey: "userId" }
);
What i am trying to implement now is the messaging functionality.
Here is my messages Model so far:
const Messages = db.define("Messages", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
senderId: {
type: Sequelize.STRING,
},
receiverId: {
type: Sequelize.STRING,
},
message: {
type: Sequelize.STRING,
},
});
User.hasMany(Messages, {
foreignKey: 'senderId'
});
User.hasMany(Messages, {
foreignKey: 'receiverId'
});
Messages.associate = (models) => {
Messages.belongsTo(models.Post, {
foreignKey: "PostId",
});
Messages.belongsTo(models.User, {
foreignKey: "senderId",
});
Messages.belongsTo(models.User, {
foreignKey: "receiverId",
});
};
User.hasMany(Conversations, {
foreignKey: 'conversationId'
});
And here is my conversation model so far:
const Conversations = db.define("Conversations", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
}
});
module.exports = Conversations;
Conversations.associate = (models) => {
Conversations.hasMany(models.Message);
models.Message.belongsTo(Conversations);
};
Also, the message can be sent from 1 user to another not to a group of users.
Are my associations and table structures correct?
Update
User.hasMany(Messages, {
foreignKey: 'senderId'
});
User.hasMany(Messages, {
foreignKey: 'receiverId'
});
Messages.associate = (models) => {
Messages.belongsTo(models.User, {
foreignKey: "senderId",
});
Messages.belongsTo(models.User, {
foreignKey: "receiverId",
});
Messages.belongsTo(models.Conversations,{
foreignKey: "conversationId",
});
};
Conversations.associate = (models) => {
Conversations.hasMany(models.Messages,{
foreignKey: "conversationId",
});
Conversations.belongsTo(models.Post,{
foreignKey: "PostId",
})
};
Post.hasMany(Conversations, { foreignKey: "PostId" });
User.hasMany(Conversations, {
foreignKey: 'user1'
});
User.hasMany(Conversations, {
foreignKey: 'user2'
});
Using this implementation when i try sending a message, the conversationId in the messages table is null.
Here is my post request:
router.post("/",
auth,
async (req, res) => {
const post = await Post.findOne({where:{id:req.body.postId}})
if (!post) return res.status(400).send({ error: "Invalid postId." });
const targetUser = await User.findOne({where:{post.userId}})
if (!targetUser) return res.status(400).send({ error: "Invalid
userId." });
await Conversations.findOne({
where:{
user1:{[Op.or]:[req.user.id,post.userId]},
user2:{[Op.or]:[req.user.id,post.userId]},
PostId:req.body.postId,
}
}).then(conversation=>{
if(conversation){
return conversation
}else{
return Conversations.create({
user1: req.user.id,
user2: post.userId,
PostId:req.body.postId,
})
}
}
)
Messages.create({
senderId: req.user.id,
receiverId: post.userId,
message: req.body.message,
conversationId:conversation.id //MY PROBLEM IS HERE
})
.then(
res.status(201).send({
msg: "upload successful",
}));
const { expoPushToken } = targetUser;
if (Expo.isExpoPushToken(expoPushToken))
await sendPushNotification(expoPushToken, message);
});
回答1:
All models look good. The issues are with associations.
If you define more then one association between the same two models you should indicate different aliases to distinguish them from each other in queries.
User.hasMany(Messages, {
foreignKey: 'senderId',
as: 'OutgoingMessages'
});
User.hasMany(Messages, {
foreignKey: 'receiverId',
as: 'IncomingMessages'
});
Messages.belongsTo(models.User, {
foreignKey: "senderId",
as: 'Sender'
});
Messages.belongsTo(models.User, {
foreignKey: "receiverId",
as: 'Receiver'
});
Also it's better to define associations in the same manner either directly after model definition or in a static method like associate. The latter approach is preferable because it allows to define each model in its own module without any cross-references using the models parameter in associate method to access other models that should be associated with a given model.
Last note: try to define associations where a model on the left side of an association definition in its own associate method.
It means that
models.Message.belongsTo(Conversations);
should be in Message model associate method:
Message.belongsTo(models.Conversations);
That way you always know where to find all associations that define links from a certain model to other models.
UPDATE
You should store a found or a created conversation to a variable in order to use it while creating a message:
let conversation = await Conversations.findOne({
where:{
user1:{[Op.or]:[req.user.id,post.userId]},
user2:{[Op.or]:[req.user.id,post.userId]},
PostId:req.body.postId,
}
})
if (!conversation){
conversation = await Conversations.create({
user1: req.user.id,
user2: post.userId,
PostId:req.body.postId,
})
}
const newMessage = await Messages.create({
senderId: req.user.id,
receiverId: post.userId,
message: req.body.message,
conversationId:conversation.id
})
res.status(201).send({
msg: "upload successful",
});
Don't try to mix up then/catch and await. If you use await you will already have a result or an exception (which you can handle using try/catch).
来源:https://stackoverflow.com/questions/65168123/sequelize-how-to-structure-the-chat-part-of-the-app