Question
Please note that the solution to this is directly below using Eugen's view idea!
I'm writing a chat module for a PHP/MySQL user-driven site that allows two users to make friends, and have chosen eJabberd for the chat system.
I've set up external authentication successfully using a PHP daemon, and now I've successfully managed to get the friendships in to eJabberd by using mod_roster_odbc
and populating the MySQL rosterusers
table manually. After lots of digging, I managed to find this particular comment very helpful in knowing what to set each column to, in order to represent that friendship in a friends list for the chat module.
My current method for handling friendships is by inserting two rows in to the rosterusers
table:
# Relationship user1 => user2
INSERT INTO rosterusers (username, jid, subscription, ask, server, type)
VALUES ('user1', 'user2@myserver.org', 'B', 'N', 'B', 'item');
# Relationship user2 => user1
INSERT INTO rosterusers (username, jid, subscription, ask, server, type)
VALUES ('user2', 'user1@myserver.org', 'B', 'N', 'B', 'item');
I'm not too happy with this because two rows are required for a reciprocal friendship.
I understand that XMPP, by standard, allows single and dual links between users. As one might deduce by the nature of my question, my own application's friend system uses one row to represent a friendship.
My main questions:
- Is it possible to consolidate this friendship in to just one row? I've tried some combinations from this unofficial documentation but haven't had success. I'm testing by connecting to my XMPP server with the Pidgin client.
- What is the best way to keep the two databases - friends and XMPP roster - in sync? I think that a MySQL
TRIGGER
might be the cleanest option for now.
If not, then my other option is to alter the rosterusers
table and get that to refer to my own application's friend row, so that it functions like a cross database foreign key.
Solution Code
I created a view as Eugen suggested. The code isn't the most elegant, but I've tested it and it works with eJabberd 2.1 on MySQL 5.5.
My exact setup uses two databases, so I am referencing my main application's database explicitly by using main_database.table_name
.
The code is a union of two queries - the first takes User, Friend and then the second inserts Friend, User. I'm using UNION ALL
for speed, and to let "duplicates" through.
I think this is a really great way of handling the problem as no changes in the application are required, and it updates instantly.
CREATE VIEW rosterusers AS
SELECT LCASE(ua1.Username) AS `username`, CONCAT(LCASE(ua2.Username), '@myserver.org') AS `jid`,
'B' AS `subscription`,
'N' AS `ask`,
'N' AS `server`,
'item' AS `type`,
'B' AS `subscribe`,
d1.Created AS `created_at`,
ua2.Username AS `nick`,
'' AS `askmessage`
FROM main_database.User_Friend AS `d1`
INNER JOIN main_database.User AS `ua1` ON `d1`.UserID = `ua1`.ID
INNER JOIN main_database.User AS `ua2` ON `d1`.FriendID = `ua2`.ID
WHERE d1.IsApproved = 1
UNION ALL
SELECT LCASE(ub2.Username) AS `username`, CONCAT(LCASE(ub1.Username), '@myserver.org') AS `jid`,
'B' AS `subscription`,
'N' AS `ask`,
'N' AS `server`,
'item' AS `type`,
'B' AS `subscribe`,
d2.Created AS `created_at`,
ub1.Username AS `nick`,
'' AS `askmessage`
FROM main_database.User_Friend AS `d2`
INNER JOIN main_database.User AS `ub1` ON `d2`.UserID = `ub1`.ID
INNER JOIN main_database.User AS `ub2` ON `d2`.FriendID = `ub2`.ID
WHERE d2.IsApproved = 1;