I'm interesting in creating from scratch such feature to learn, I'm having a hard time understanding and being able to plan such structure. Most programmers from what I've seen
check if someone has already rated the comment or post by their IP address, I dislike that specially due to the fact that each user must be logged in to vote, so why not check the
user iD instead of iP. I also tend to choose performance instead of looks so could someone help me understand how to begin to accomplish this. I've read and searched many posts
on SO(StackOverflow) yet they're not too comprehensive for me. I also read somewhere to have a UNIQUE KEY constraint to only allow one vote, is that correct?
Some suggestions on a table structure and maybe how to design would be greatly appreciated.
I'm confused with such structure, the following tables is what I've got so far.
Posts:
post_iD | message | uid_fk(user id) | voteUp | voteDown
PostsRateSystem:
rate_iD | rate_user_iD(uid_fk from posts table) |
Do I even need a second table to keep track of who has rated.
if(isset($_POST['plus']) && isset($_POST['minus'])){
$plus = $_POST['plus'];
$minus = $_POST['minus'];
$row = insertRate($post_iD, $plus, $minus);
}
<form method="post" action="">
Thumbs Up
Thumbs Down
function insertRate($post_iD, $plus, $minus)
{
if($plus)
{
$sth = $this->db->prepare("UPDATE posts SET voteUp = 1 WHERE post_iD = :postiD");
$sth->execute(array('postiD' => $post_iD));
}
if($minus)
{
$sth = $this->db->prepare("UPDATE posts SET voteDown = 1 WHERE post_iD = :postiD");
$sth->execute(array('postiD' => $post_iD));
}
}
If all users can rate each post once, you'll have to have a third table (votes) that has a userid, a postid and a rating value (either 1 or -1). In that table, you should make the combination of userid and postid unique, because a user is only allowed one vote per post.
To get the total number of votes for a post, you can just sum those values:
SELECT SUM(votes.value) FROM votes WHERE postid = 10
Now, each upvote is counted as one, and each downvote as minus one, because those are the actual values stored.
To save a vote, you'll have to try to update the table first, because the logged in user can already have a vote. If updating fails, probably due to no vote already exists, you can insert a vote for the user for that post.
To speed up things, you can also increment or decrement a vote counter in the post table itself. That one doesn't keep track of all users, but it does keep track of the total score. That way, you don't have to query the sum for each page view of a vote. Keep in mind though, that if a use changes their upvote to a downvote, you'll have to subtract 2 from that score and vice versa. Alternatively, you can use the SELECT SUM query to calculate the votes and store it in the post, after each change to the votes of a post. So after voting for post 10, do this:
UPDATE posts
SET score = (SELECT SUM(value) FROM votes WHERE postid = 10)
WHERE postid = 10
Related
I want to create an up vote and down vote system for my website where a unique user can vote up/down for one post and next time he only allow to opposite to get off from database and after that he again can up or down vote.
In this case I have:
users table :
id
name
debates table :
id
post
upvotes table:
id
user_id
debate_id
and similarly downvote table:
id
user_id
debate_id
Is that a good way to manage and track up vote and down vote concept?
I think, you can use a single table to track the votes and the structure could be something like this
Table : votes
id | user_id | debate_id | vote
Here, vote field could be tinyInt with defauld null.
And, in vote field, you just keep two different values depending on the vote type, for example, if a user up votes then insert a value of 1 in the vote field and for down vote, insert the value of 0. So, your table may look something like this
id | user_id | debate_id| vote
1 | 10 | 4 | 1 <-- up
2 | 11 | 4 | 0 <-- down
3 | 12 | 4 | 1 <-- up
In this case, two users with id = 10 and id = 12 up voted the post whose debate_id = 4 and another user with user_id = 11 down voted on the same post.
IN this case, you may find out how many up or down votes a post got by counting the vote field's value, for example, you may count for up votes for debate_id = 4 using something like this
$count = Debate::where('debate_id', '=', 4)->where('vote', '=', 1)->count();
Also, you may use something Query Scope, this is just an idea and it's not possible to make an answer which covers everything in this scope. You should start using some basic idea and if you stuck at a certain point, then you may ask specific questions with your code.
Also, I would like to mention that, if you find a user id in the votes table with a certain debate_id then this user has voted on this post and to find out the vote type, just check the vote field 1 or 0.
I would prefer to only have one table containing the votes, this could be done with an extra column such as is_downvote int(1).
It seems that you havn't tried much which is always a negative. For this scenario the Laravel Eloquent Documentation should be plenty to figure this out.
I would of written this as a comment but it's pretty lengthy now.
I am currently developing an article driven site and would like to know the best way to track page hits so I can display something along the lines of "Most Viewed" or "Most Popular Articles". I display my articles through a $_GET on a single PHP page (e.g article.php?id=2). I read somewhere that an INSERT INTO was the way to go and I tried to do:
$page_views = $conn->query("INSERT INTO blog_posts (views) VALUES (views = views+1");
Alas, this did not work.
In summary I want to be able to:
Add 1 to the number of page views on an specific id for each time someone lands on the page (e.g articles.php?id=3) not just articles.php
Thanks in advance.
(Sorry if I haven't made this clear enough. If you want anything clarifying just ask.)
Do you intend to keep a separate table for the counts? If so,
|page_views |
|blog_id|count|
+-------+-----+
| 1 | 12 |
| 2 | 33 |
with blog_id being a primay key with unique enforced
you could use on duplicate key to create a row if it doesn't exist and update the count if it does
("INSERT INTO page_views (blog_id,count) VALUES ($blog_id,1) ON DUPLICATE KEY UPDATE count = count + 1")
Why don't you try with update statement:
"UPDATE blog_posts SET views = views+1 WHERE id = $id"
Your insert statement will add a new row in the database, what probably is not what you want.
Use an update query to increment the counter.
UPDATE blog_posts SET views = views+1 WHERE id = 'SOME NUMBER'
Where your each row in the blog_posts table has a numberic views field and an id.
SQL Fiddle:
http://sqlfiddle.com/#!2/0915b/1
Hello I have voting system on my website but I would like to disable multiple voting from users. If the user votes he/she cannot vote again. Here is one solution which I have but is not good imo.
I created a column in users table: users_avoted where I would store the IDs of articles which he/she voted like tihs:
1|2|5|6
From this I would be able to check whether the user had already voted. But the thing is there will be a lot of articles so VARCHAR(255) will not be enough to store all of those IDs. Any other solutions?
Thanks
Don't denormalize it like that. Just have a two column table, userVotes, with a composite Key for UserID and ArticleID.
example data:
userid | articleid
1 1
1 2
1 67
2 1
This is better than something like
userID | articleID
1 1,2,67
Which is what it looks like you are suggesting. Please don't do this, and keep it normalized unless you want to do gymnastics every time you query. To elaborate, implementing what you suggested would defeat the purpose of using a RDBMS, in order to 'cheat' on one specific query.
You could instead make a table like:
users_avoted {
article_id,
user_id,
answer_id
}
Make a unique index out of article_id and user_id. That would only allow a single record to be input for that.
So, say they choose option 5 on article 2, and they are user 1.
Now, when you attempt to insert into the table:
INSERT INTO users_avoted VALUES (2, 1, 5)
You can't because the user already voted.
The benefit of this approach is that you don't have to check if they voted already first.
Store the votes in a table layed out like: id,username,voteid,vote for example.
Then when the user votes, check that table for matching rows.
For example:
if ($username == $dbusername && $voteid == $dbvoteid){
$disallowed = true;
}
We have to make a ballot system that makes users vote for various candidates on different positions. There's a login for voters. How do you store a vote of one voter and then get that vote added to the previous votes? The only possible way is to store every vote on a database right? But what would the structure of the database look like? And how do you count it?
edit:
The voting system doesnt only have one group of candidates to vote. It has mayor, vice-mayor, senator, etc. There are too many. that's why I'm confused. If only it was just a voting system of a president, it would be easier. So if I have a table for the voter, with a column of his/her voted candidate, it's not possible since the voter votes for many candidates.
A better way would be to have a different table to store Votes. And that table will have two attributes (VoterId, CandidateId)
And you can fetch the Vote Count if you are allowing multiple votes from this table..
But it would be better to make VoterId a Primary key in this table.. To avoid multiple voting
CandidateType: - (TypeId(PK), typeName, maxVotePerVoterForThisType)
Voter Table: - (voterId(PK), voterName, otherInfo)
Candidate Table: - (candidateId(PK), candidateName, constituency,
otherInfo, TypeId(FK))
Votes:- (voterId(PK, FK), TypeId(PK, FK), candidateId(FK))
*EDIT:- Schema edited with changed requirement in original post
*EDIT: - Added a field in CandidateType table to allow multiple votes.(E.g.: Now a voter can vote for 10 Senators, if maxVotePerVoter for this type is set to 10..)
You should e store each candidate in a table, positions in another table then make relations based on ID, the voting system is relatively simple:
database:
id, position_id, candidate_id, votes
then PHP
$query = "UPDATE `votes` SET `votes`=`votes`+1 WHERE `position_id`=1 AND candidate_id=1"; // adds 1 vote where position_id is 1 and candidate_id is 1
These 3 tables are required for your accounts (the voters) and candidates.
Account: Id (PK), Name, Password
Candidates: Id (PK), Name
Votes: AccountId (PK), CandidateId (PK)
Insert a row into votes when a vote is cast. This prevents duplicate voting due to the PK's.
I simple language i can say you are storing voting users in a table called users and you can use a field vaotecount (int) type initilize with zero and track their ip and vote for topic id on which they have voted for and when they vote you can check with IP and topic_id that they are not voting twice and if they passed you can increment the vaotecount field by one
and while you are counting for votes count the users with topic id for which you counting votes. Simple :D
From the explanation of problem, I can suggest you the following database structure:
tlbUserVote:
UserID(PK) | UserName | Vote | CandidateId(FK)
tlbCandidate:
CandidateId(PK) | CandidateName | TotalVotes
By this structure you can add the current votes with previous ones (by taking previous votes first and adding it to the current).
Total votes of the candidates will get updated, too.
So I have this query that pulls from my links and votes table and I need one last column of data. My votes table consists of every user's vote, a user can only vote once per link and their vote value is either -1, 0 or 1. There is a user_id foreign key in the votes table and I want to somehow gather the current user's vote. I feel the complexity of the current query might require a second query but I really want to avoid that if possible. I simply need to know what the current logged in user's vote was. A link will never have more than one vote value because a user can only vote on a link once.
A few notes
All links start automatically with at least one vote entry by the current user
A user that votes on a link then deselects that vote will keep that vote entry with a 0 delta
SQL:
SELECT links.*, (SUM(votes.karma_delta)) AS karma
FROM links, votes
WHERE links.id = votes.link_id
GROUP BY votes.link_id
ORDER BY (SUM(votes.karma_delta) - 1) / POW((TIMESTAMPDIFF(HOUR, links.created, NOW()) + 2), 1.5) DESC
LIMIT 0, 100
While optimization is great, right now I just want to get the selected links karma_delta by a specified user.
I'm really not sure of what you're asking, but it sounds like you want to keep the information you're already returning and simply augment it with the sum of votes pertaining to the current user for each link.
If that's the case, then something like this should do it:
SELECT links.*,
SUM(votes.karma_delta) AS karma,
SUM(
IF(votes.user_id = current_user_id,
votes.karma_delta,
0)
) AS user_vote
FROM links, votes
WHERE links.id = votes.link_id
GROUP BY votes.link_id
ORDER BY (SUM(votes.karma_delta) - 1) /
POW(
TIMESTAMPDIFF(HOUR, links.created, NOW()) + 2,
1.5
) DESC
LIMIT 0, 100
Here is one way you can make this faster: if you need to show the scores for each of the links frequently, and you vote on the links not very frequently, I'd denormalize the data structure in the following way:
Create a column on the link table called "current score"
Whenever you make a modification to the votes table, also update the current score
If you ever worry about the two getting out of sync, run a daemon that overrides the values of the current score with the "aggregation of all votes".
Then, showing the score of each of the links is mega-fast; of course, the cost you're paying here is at the vote time (you're doing two inserts/updates instead of one), as well as some extra complexity.