I have been working on a Social Network to further my knowledge with PHP, however I have come to the point where I will be pulling posts from he database depending on who the user is "friends" with.
My database structure for friends is as follows:
USER_A | USER_B | CREATED_AT | IS_BLOCKED
And then for the posts:
USER | UPDATE | STATUS
So, I want to show posts selected from my post table if the user logged in is friends with the user who posted it.
So, basically, wondering if anyone has any suggestions. It won't be anymore complicated than that, not interested in getting friends of friends or anything like that. So, just wondering if someone could point me in the right direction with this one.
I read it would require JOINs and such, however I have never really stepped into that side of SQL so therefore would have no idea what I would be doing with it, so links to relevant questions and articles on that, if required, would be appreciated.
Thanks
(I am essentially looking for help with the SQL statement, not with the PHP.)
Update #1:
I have got the newsfeed working, now I need to be able to join a statement to get the users names to be displayed. My current status is on this: http://sqlfiddle.com/#!2/b07793/1/0
Before anyone goes crazy about storing passwords in plaintext, this is not a public system, it is purely for me to test, and hashing passwords for a non-public script that will only ever be used by me with fake passwords is hardly a security risk, it just makes debugging a pain.
Something like this should work :
SELECT * FROM posts
JOIN friends ON posts.user = friends.user_b
WHERE user_a = 'someuser'
In this statement we are JOINING to tables : posts and friends, based on the value of two column, we are putting in relation the column user of the post table with the column user_b of the friends table, in this way we will obtain a result set with only the value of the two table where the two columns in relation are equal ...
If I understood correctly the db schema you described, this will bring you all posts that have been written by a user that is friends with the logged in user.
SELECT * FROM posts WHERE posts.user IN (SELECT user_b FROM friendships WHERE user_a=$logged_in_user)
The query is pretty simple. You get all the rows from table posts that the creator of that post has a line in your friendships table as user_b while user_a is set to be the logged in user.
I'm assuming a few things, if they're not correct you'll have to modify this stuff but if I'm right, these operations won't need any JOINs:
$userSession variable contains the user who's logged in's ID.
$userToView variable contains the user who he's trying to view's ID.
Your database structure will only have one entry for the two users who are friends, so we have to check both combinations.
To determine if the user is a friend:
SELECT user_a, user_b FROM friends
WHERE (user_a = $userSession AND user_b = $userToView)
OR (user_a = $userToView AND user_b = $userSession)
LIMIT 1;
Then you'd use an if statement in PHP (it's usually better to keep business logic in the application, rather than in the database), if it's true return the posts.
$results = $mysqli->query($query)->fetch_row();
if($results) {
... return posts ...
} else {
echo 'Not friends';
}
That would be a query something like this:
SELECT * FROM posts
WHERE user = $userToView;
You will need to read up about mysqli (do not use mysql plugin, it's deprecated) and about SQL injection attacks and how to prevent them. It may sound like a hassle, but it's far less daunting to understand this stuff than to deal with the fallout if you don't.
Related
I'm looking for a pattern for dealing with a situation where a record in a table may be related to 1 and only one of several other records and I need to know which. It's difficult to describe so I will use an example with an oauth client from a recent project.
A client has an id and secret and teams or users may own clients
clients
client_id|client_secret
user_clients
user_id|client_id
team_clients
team_id|client_id
The issue here is when someone attempts to use client credentials to get an access token I need to know if those credentials belong to a team or a user. I'm not really sure of the best way to deal with this, I control the database so changes can be made.
Some options I've considered:
Modify clients and remove user_clients and team clients
clients
client_id|client_secret|team_id(nullable)|user_id(nullable)
Create a client owners table and remove user_clients and team_clients
client_owners
team_id(nullable)|user_id(nullable)|client_id
Adding a type to the clients table and doing 2 queries or a conditional query
clients
client_id|client_secret|type(user or team)
Left joins and filtering/mapping in code.
select * from clients c left join user_clients u ... left join team_clients t ... where c.client_id = ?
None of these options feel great so I am wondering if there is a better pattern. This is a simplified example, sometimes these objects are much more complicated and have many more possible relationships so the queries can get pretty hairy and also difficult to manage when using an orm (though requiring raw sql for parts of the system is not an issue).
I'd love to hear how people are solving this problem.
Thanks in advance.
Design
What you describe are 1:n relations:
One user can own multiple clients
One team can own multiple clients
So, the first approach seams appropriate: The client table has a user column and a client column.
Now you say a client can only be owned by either a user or a client. You would have a check contraint on the client table for this to ensure that only one of the IDs is set. (And if every client must have an owner, this constraint would also care about this.)
Some example queries
If you want to get all clients owned by users:
select * from clients where user_id is not null;
If you want to know whether a client is owned by a user or a team:
select
c.*,
case when user_id is not null then 'user'
when team_id is not null then 'team'
else 'none'
end as owner
from clients c;
If you want user or team information:
select c.*, coalesce(u.name, t.name) as owner_name
from clients c
left join users u on u.user_id = c.user_id
left join teams t on t.team_id = c.team_id;
The idea with the left joins is something you can go with. Leave your clients table with just clientID and left join it on user_clients and team_clients.
You could also combine that with your first option and add a column for user_clientsID and team_clientsID and at adding to the left join clients.user_clientsID <> 0 which is going to help you with the.
The 2nd and 3rd option is something I have seen used as well. With the second option perhaps being the one that can sometimes create the most headaches.
Also an option you can consider is creating a view table with all the information you might need
I am looking for the best solution for the way the mySQL db should be set up for my app.
My app works like a noticeboard with two sections, "New Notices" and "Seen Notices".
Now when a user has viewed a notice, they click a button and it moves from New to Seen. But ONLY for this person.
Each person will have all of the notices viewable - but not necessarily in the same sections - as users will view them at different times and check them off as seen at different times.
My guess is having one table "Notices" for all notices, and a seperate table called "Seen" with the rows "UserID" and "noticeID". This means that for each notice it will need to consult the "Seen" table to find out if it should be shown or not. Is this ideal or is there another way?
Having a table with NoticeID and UserID is correct, I'd also add viewed date.
You can use 3 tables
Users
Notices
SeenNotices(maybe not the best name)
In the SeenNotices table have three columns UserID, NoticesID, HaveSeen. The have HaveSeen column will tell you if the user has seen it.
The way you are thinking should work, although over time you'll end up with a very big 'Seen' table, which is not scalable. An easy alternative is to use 'Unseen' table instead. This way the table gets smaller as people view the notice and you can also delete very old entries (old notices may no longer relevant so doesn't matter if they are not shown as Unseen to user).
Using the 'unseen' table your query will look like this:
SELECT n.notice_id, n.notice_msg, IF(u.user_id, 'new', 'seen') AS status
FROM notice n
LEFT JOIN unseen u ON (u.user_id = $user_id AND n.notice_id = u.notice_id)
WHERE user_id = $user_id;
Summary: What is the most efficient way to store information similar to the like system on FB. Aka, a tally of likes is kept, the person who like it is kept etc.
It needs to be associated with a user id so as to know who liked it. The issue is, do you have a column that has a comma delimited list of the id of things that were liked, or do you have a separate column for each like (way too many columns). The info that's stored would be a boolean value (1/0) but needs to be associated with the user as well as the "page" that was liked.
My thought was this:
Column name = likes eg.:
1,2,3,4,5
Aka, the user has "like" the pages that have an id of 1, 2, 3, 4 and 5. To calculate total "likes" a tally would need to be taken and then stored in a database associated with the pages themselves (table already exists).
That seems the best way to me but is there a better option that anyone can think of?
P.S. I'm not doing FB likes but it's the easiest explanation.
EDIT: Similar idea to the plus/neg here on stackoverflow.
In this case the best way would be to create a new table to keep track of the likes. So supposing you have table posts, which has a column post_id which contains all the posts (on which the users can vote). And you have another table users with a column user_id, which contains all the users.
You should create a table likes which has at least two columns, something like like_postid and like_userid. Now, everytime a user likes a post create a new row in this table with the id of the post (the value of post_id from posts) that is liked and the id of the user (the value of user_id from users) that likes the post. Of course you can enter some more columns in the likes table (for instance to keep track of when a like is created).
What you have here is called a many-to-many relationship. Google it to get some more information about it and to find some more advice on how to implement them correctly (you will find that a comma seperated lists of id's will not be one of the best practices).
Update based on comments:
If I'm correct; you want to get a list of all users (ordered by name) who have voted on an artist. You should do that something like:
SELECT Artists.Name, User.Name
FROM Artists
JOIN Votes
ON Votes.page_ID = Artists.ID
JOIN Users
ON Votes.Votes_Userid = Users.User_ID
WHERE Artists.Name = "dfgdfg"
ORDER BY Users.Users_Name
There a strange thing here; the column in your Votes table which contains the artist id seems to be called page_ID. Also you're a bit inconsistent in column names (not really bad, but something to keep in mind if you want to be able to understand your code after leaving it alone for 6 months). In your comment you say that you only make one join, but you actually do two joins. If you specify two table names (like you do: JOIN Users, Votes SQL actually joins these two tables.
Based on the query you posted in the comments I can tell you haven't got much experience using joins. I suggest you read up on how to use them, it will really improve your ability to write good code.
I have an app that manages conversations between users on a website. It does one to one conversations as well as having multiple people in a single conversation.
Here is the layout for the mysql tables
conversations
conversations_meta
The conversations_meta table links users to the conversations by logging user_id and conversation_id. It also holds meta info about the conversation specific to each user in the conversation.
What I am having trouble with is detecting if a conversation with the same people already exist.
For example if a conversation between Eric Jason and bob exists but maybe it's old and the user forgot about it and then tries to create an addition conversation with the same users I would like to notify them of the conversation.
So the query should look in conversations_meta table and compare user_id and conversation_id to see if the same conversation exists already. Also I wouldn't want it to return conversations that include all the same users and additional users as well.
The main reason I posted this question on here is to get the fastest query possible to accomplish this task since there will be thousands of conversations.
What about this:
SELECT conversations_meta.conversations_id FROM conversations_meta
where (conversations_meta.user_id=1) or (conversations_meta.user_id=2)
group by conversations_id HAVING count(*) = 2
NOTE: this is a case for only 2 people in the conversation. Easily expanded to the case of 3 or or more.
What is the best way to store user relationships, e.g. friendships, that must be bidirectional (you're my friend, thus I'm your friend) in a rel. database, e.g. MYSql?
I can think of two ways:
Everytime a user friends another user, I'd add two rows to a database, row A consisting of the user id of the innitiating user followed by the UID of the accepting user in the next column. Row B would be the reverse.
You'd only add one row, UID(initiating user) followed by UID(accepting user); and then just search through both columns when trying to figure out whether user 1 is a friend of user 2.
Surely there is something better?
I would have a link table for friends, or whatever, with 2 columns both being PK's, and both being FK's to the User table.
Both columns would be the UID, and you would have two rows per friend relationship (A,B and B,A). As long as both columns are PK's, it should still be in normal format (although others are free to correct me on this)
Its a little more complex of a query, but nothing that can't be abstracted away by a stored procedure or some business logic, and its in Normal Format, which is usually nice to have.
You could check which of the two user_id's is the lowest and store them in a specific order. This way you don't need double rows for one friendship and still keep your queries simple.
user_id_low | user_id_high
a simple query to check if you're already friends with someone would be:
<?php
$my_id = 2999;
$friend_id = 500;
$lowest = min($my_id, $friend_id);
$highest= max($my_id, $friend_id);
query("SELECT * FROM friends WHERE user_id_low=$lowest AND user_id_high=$highest");
?>
Or you could find the lowest/higest userid using mysql
<?php
query("SELECT * FROM friends WHERE user_id_low=LEAST($my_id, $friend_id) AND user_id_high=GREATEST($my_id, $friend_id)");
?>
And to get all your friends id's
<?php
query("SELECT IF(user_id_low=$my_id,user_id_high,user_id_low) AS friend_id FROM friends WHERE $my_id IN (user_id_low, user_id_high)");
?>
Using double rows, while it creates extra data, will greatly simplify your queries and allow you to index smartly. I also remember seeing info on Twitter's custom MySQL solution wherein they used an additional field (friend #, basically) to do automatic limiting and paging. It seems pretty smooth:
https://blog.twitter.com/2010/introducing-flockdb
Use a key value store, such as Cassandra for example.