Reddit-style voting system, how do I avoid duplicate votes? - php

I have a table that stores a post. Each post has an id, title, content and a score. Currently, you can like a post and its score will increment and decrement, if you dislike it.
Now the thing I don't understand: how do I avoid that a user will vote more than once? Surely they can just refresh and vote again. I've read some articles that store cookies, etc. but can't you just disable cookies or clear them and vote again?
I was thinking you would have to store who has voted, or rather, the ID of who has voted too. However, I can't seem to visualize how I would go about this? Would I store the ID of the voter in the post they're voting, or something else?

You will need an extra table posts_vote or something. Put the fields user_id and post_id in it. If a user votes a post up, you insert both IDs in this table. If a user votes down, find the record and delete it.

You can store the voting records in a separate table.
Vote history (Alternative 1)
voter_id
post_id
action (upvote/downvote)
created_at
Vote history (Alternative 2)
voter_id
post_id
points (this field can get negative numbers for downvote)
created_at
So when you have that particular record you can check if the user has voted for the post before and decide to increment/decrement the points in the actual post table.
In the future vote history table will grow and will cause you performance issues, you can sync history records with redis/memcached/etc. and do your checks faster with those storage technologies.
Also using cookies can help you to disable voting without hitting the server at all and will reduce the number of requests to your web server.
You can store the voted post id's in cookies and check them with javascript, don't do the request if user already voted the post.
So you can have two layers for checking if the user has voted.
Cookies and javascript
Server-side persisted data
If number one fails (users can bypass this via changing browser or cleaning cookies), it fallsback to number two and you don't allow to do the voting in the server side.

Just as an example:
Let's say you have tables posts and votes. Then you could have a posts_votes as a look up table.
Visually:

Related

Notification and PHP+MySQL design

I'm making a website that have posts and replies system.
I'd like to do is when someone replies, sending notification to those who have ever replied (or involved) the post.
My thought is to create a table named Notification, contains message and seen (seen/unread) field. Once people replied, INSERT record to the Notification table.
It's seems easy and intuitive, but if there are lots of people involved in, for example, the 31st user replies, 30 people who have ever replied will receive notification. This will make 30 rows of SQL records. And the 32nd user will make 31 records. Then total number of rows will become 30+31=61.
My question is
Is that a good way to handle notification system?
If so, how to deal with the duplicate notification (haven't seen but has new reply)
As above, will this make a huge server load?
Thank you so much.
I was creating similar system. Here is my experience:
My notification table looks like: id (int) | user_id (int) | post_id (int) | last_visited (datetime).
user_id + post_id is an unique composite index.
So when a user opens the page, I'm looking for an entry (user_id + post_id) in the database. If I find it, then I update the last_visited field if I don't find, then create new row.
When I need list messages for notification I'm just query all messages that was created after last_visited time.
Also I have cron sript that clean notification for closed posts or banned users.
As for your questions:
1 and 2: You have to find a balance between the amount of data that will be stored and site performance. If you don't need to store all this data you can follow my way. If this data is needed your way is better.
3: It depends on the number of visitors and other functionality. But here is some advices. You must use indexes for MySql table for better perfomance. Also you should think about cron script that will remove useless notifications. If you have huge amount of visitors more than 700k per day you shoulf think about MogoDb or other high perfomance noSql database.

The most efficient method storing voting data in a MySQL database (like StackOverflow)

I'm trying to replicate SE's voting system. User's on my website should only be able to vote on posts one time, then they are locked in. I currently have two tables, users and posts.
How should I store information on which posts a user has voted on? I was thinking of having a column in posts which would store the uids of users which have voted on it. Another idea would have a column in users with the ids of the posts he/she has voted on.
How should I do this? I want to take scalability into account and easy of determining whether or not the vote is valid.
Create another table to store information from the users and posts tables:
CREATE TABLE votes (
user_id INT
, post_id INT
, PRIMARY KEY (user_id, post_id)
);
With this approach:
I was thinking of having a column in posts which would store the uids of users which have voted on it. Another idea would have a column in users with the ids of the posts he/she has voted on.
Unless you store the values as delimited values (to fit in one cell) or JSON, you'll end up with many rows for just one post. But then, that's a bad approach to start with.
Stick with creating a new table which contains the relationship determining "voting". The table is simple enough to check:
SELECT COUNT(t1.post_id) AS vote_count
FROM votes AS t1
WHERE
t1.user_id = SOME_INTEGER
AND t1.post_id = SOME_INTEGER
Best practice, for something the size of stackoverflow, is to store the individual votes in a separate table. But also keep a derived vote count in a field directly attached to the post. As database sizes grow, it'll become prohibitively expensive to sum up all the votes on every viewing of a post.
Keeping this derived field is relatively easy by use of triggers on this user/votes table.

What is an elegant / efficient way of storing the status of 100 lessons for multiple users?

I'm working on an app in JavaScipt, jQuery, PHP & MySQL that consists of ~100 lessons. I am trying to think of an efficient way to store the status of each user's progress through the lessons, without having to query the MySQL database too much.
Right now, I am thinking the easiest implementation is to create a table for each user, and then store each lesson's status in that table. The only problem with that is if I add new lessons, I would have to update every user's table.
The second implementation I considered would be to store each lesson as a table, and record the user ID for each user that completed that lesson there - but then generating a status report (what lessons a user completed, how well they did, etc.) would mean pulling data from 100 tables.
Is there an obvious solution I am missing? How would you store your users progress through 100 lessons, so it's quick and simple to generate a status report showing their process.
Cheers!
The table structure I would recommend would be to keep a single table with non-unique fields userid and lessonid, as well as the relevant progress fields. When you want the progress of user x on lesson y, you would do this:
SELECT * FROM lessonProgress WHERE userid=x AND lessonid=y LIMIT 1;
You don't need to worry about performance unless you see that it's actually an issue. Having a table for each user or a table for each lesson are bad solutions because there aren't meant to be a dynamic number of tables in a database.
If reporting is restricted to one user at a time - that is, when generating a report, it's for a specific user and not a large clump of users - why not consider javascript object notation stored in a file? If extensibility is key, it would make it a simple matter.
Obviously, if you're going to run reports against an arbitrarily large number of users at once, separate data files would become inefficient.
Discarding the efficiency argument, json would also give you a very human-readable and interchangeable format.
Lastly, if the security of the report output isn't a big sticking point, you'd also gain the ability to easily offload view rendering onto the client.
Use relations between 2 tables. One for users with user specific columns like ID, username, email, w/e else you want to store about them.
Then a status table that has a UID foreign key. ID UID Status etc.
It's good to keep datecreated and dateupdated on tables as well.
Then just join the tables ON status.UID = users.ID
A good option will be to create one table with an user_ID as primary key and a status (int) each row of the table will represent a user. Accessing to its progress would be fast a simple since you have an index of user IDs.
In this way, adding new leassons would not make you change de DB

Storing credits in database

Just a quickey. I am developming website, where you can buy credits and spend them later for things on the website.
My question is, is it ok to store amount of credits with user (user table, column credits and iteger amount) or it is necessary (or just better) to have separate table with user id and amount ?
Thanks
Both actually.
Considering that you'll be dealing with monetary transactions to get those credits, you want to be able to get a log of all transactions (depending of the laws in your country, you will NEED this). Therefore you'll need a credits_transactions table.
user_id, transaction_id, transaction_details, transaction_delta
Since programmatically calculating your current credit balance will be too costly for users with a lot of transactions, you'll also need a credit_balance row in your user table for quick access. Use triggers to automatically update that column whenever a row is inserted from credits_transactions (technically, update and delete shouldn't be allowed in that table). Here's is the code for the insert trigger.
CREATE TRIGGER ct_insert
AFTER INSERT ON credits_transactions
BEGIN
UPDATE users SET credit_balance = credit_balance + NEW.transaction_delta WHERE user_id = NEW.user_id;
END
;;
I also have sites containing credits and found it easiest to store them in the user table, mostly because you need access to it on every page (when the user is logged in). It is only an integer so will not do much harm. I think actually creating a new table for this value might be worse perfomance wise because it needs an index aswel.
A good rule of thumb is to create a user table for the info you need on every page, and normalise the data you dont need on every page (for example adress information, descriptions etc).
Edit:
Seeing the other reactions,
If you want to have transaction logs aswel I would store them seperately as they are mainly for logging (or if the user wants to view them). Calculating them on the fly from the log is fine for smaller sites but if you really have to squeeze performance just store the actual value in the user table.
If you store in separate table, you can keep log of changing the credits. If you store in column, you will have only the current amount of credits.
If you want to keep a record of Credits History Log like
how many credit bought today.
how many spend yesterday.
what did you bought with credits
I think its better to put this in a separate table. In this way you can get these kind of results by applying mathematical operations.
Credits are like money. If a user needs to purchase them, then they are money. Money is tracked using accounts. Account has associated transactions, deposits and withdrawals -- and balance. Search the SO or google for database and account. Here are just a few examples:
one
two
three
I'd have a table which stores the purchases and bought credits, with user id.
Then calculate each time based on this, it should be fast if it's indexed, this way you will be able to easily have a purchase history.

How do forums show you unread topics?

I have user discussion forums I coded in php/mysql, I am wanting to know how the big name forums can make it show you which topics have new posts in them, usually by changing an icon image next to the thread without using hardly any resources?
The simplest way is to track the last time someone was logged in. When they come back to visit, everything which has been updated since then is obviously "new".
This has some problems though, since logging out effectively marks all items as read.
The only other way I could think to do it would be to maintain a table containing all the threads and the latest post in that thread which each user has seen.
user_id thread_id post_id
1 5 15
1 6 19
With that information, if there is a post in thread #5 which has an ID larger than 15, then you know there's unread posts there. Update this table only with the post_id of the latest post on that page. This means if there's 3 pages of new posts, and the user only views the first, it'll still know there's unread posts.
As nickf said above except that the threads the user has actually visited is tracked. so anything the user hasn't visited is considered new for that visitor. for finer grain control any threads created before the user registered are ignored and possibly any threads not visited within a period of time are ignored. this would prevent every unvisited thread as becoming a new thread for them.
Of course there are many ways to skin a cat and depending on what the forum creators wanted the above can be changed to suit
DC
You could log the last time they selected that topic and then see if a post has a later time-stamp then their last "click" on the thread.
You could make a special table in your database with columns like USER_ID and THREAD_ID and with appropriate constraints to your USER and THREAD tables and a primary key containing USER and THREAD IDs.
Now when somebody opens a thread, you just insert that USER-THREAD-PAIR into that special table.
In your thread listings you can now simply outer-join that table on to what ever suits you use there. if your new table contains NULL on any particular spot, that thread is unread. This will enable lists like:
All Threads with "unread" marker
All unread threads
Threads read by user XY
If you add a date column to this table, you can do even more interesting stuff.
Just keep an eye on your keys and indexes to prevent too heavy negative performance impacts. Try to read from the USER-THREAD-table only by joining it into your existing queries. That will work much faster than executing individual queries all the time.
You could have a table that gets an insert whenever a thread gets read, if the user reading it hasn't already. Then when someone adds to the thread you can delete all entries in the table for that thread, thus making it unread for all users.
The table structure would be something like
forum_id thread_id user_id
With the optional extra has_read_id for your primary key, with the other fields making a composite key.

Categories