How to take votes and rank images using PHP and MySQL - php

I'm trying to build a php ranking system where users can rank an image on a scale of 1-5.
Depending on how an image is ranked decides what its place on the leaderboard (rank number) would be. The rank should change depending on the different ratings it receives from users.
An example of this is the ranking system here. http://www.newgrounds.com/portal/view/601966 (Right hand side, lower down the page.)
I'm just looking for any information which would help me achieve this.
Thanks.

Create a table called votes and tie it to your images table:
VOTES:
vote_id INT(11) PK
user_id INT(11)
image_id INT(11)
score TINYINT(1)

Here are some things you're going to need to know:
You need a database. In your database you need to store each of the images you're ranking, do this in a table called "images". In this table you will give each image an "auto-incrementing" primary key. (this means that for each new row you add to the database the primary key will AUTOMATICALLY be +1 from the row before). This means that each image has a UNIQUE row number next to it - identifying that specific row. Call this column id. (we will reference it in other tables in the column image_id).
Next you need a table called "votes". In this table you can store all sorts of information you might need, but simply all you'll need to store is the unique image number from the "images" table and the value of the vote that someone has cast. You'll end up with something like this:
image_id | vote_value
1 | 3
2 | 5
1 | 3
4 | 1
4 | 3
Now you can query this information to get your leaderboard. The query might look something like this:
SELECT image_id, SUM(vote_value) AS rank FROM votes GROUP BY image_id ORDER_BY rank;
That will give you a list of "image_id"s ordered by their rank (i.e. the total of all the votes).
Then you can go back to your images table and get the information for that image out of that table.
SELECT name, url FROM images WHERE id=#image_id we got above#;
Hope this helps you. :) If you get stuck come back and ask again.

Related

Adding database entries together

I am making a site which allows admins to basically add points for a user.
At this point in time, I have a table, where id_child is unique, and id_points changes. So a constant stream of id_points can come in, however, it will only show the latest id_points, not the total.
I am wondering how I could go about creating a PHP script that could add all of those together.
From the image, the idea is that I want all id_points values added together to give a total, and this is for the same id_child
Use SQL sum() funciton:
select sum(id_points) from table `table_name` where `id_child` = 1
Hope i understood right.
First if you want to show only the latest points added you have to create another table #__points where you will keep every new change of points.
You need 3 columns id as PRIMARY and AUTO_INCRENMENT , pts and user_id . user_id will be FK to id_child.
So when you want to add a new record :
INSERT INTO `#__points` (pts,user_id) VALUES ("$pts",$id)
When you want to select last inserted value for each admin :
SELECT * from `#__points` where user_id=$id ORDER BY id ASC LIMIT 1

How to link a row of MySQL database table to another row in another table

I know it makes little sense... and i'm new to using MySQL...
What i'm trying to do here is, link one tables row to another tables row...
for an example there are two tables..
one table is for user registration and same table is used for login as well...
and the next table is for user posts.. like status updates and all...
here is how i want it...
user_log_info:-
id ( primary )
firstname
lastname
username
email
password
posts:-
id ( primary )
userposts
posted_by
date_post
so as you can see, i want the user_log_info tables username to be automatically copied to posts posted_by row... And i have no idea how i can archive this...
You haven't given nearly enough information to give a full answer, but I'll do my best with what you've given.
Tables
+-----------------+ +-----------------+
| users_log_info | | posts |
+-----------------+ +-----------------+
| int ID (primary)| | int ID (primary)|
+-----------------+ | int posted_by |
+-----------------+
(I left off fields that are irrelevant to what you seem to want to do, I'm just simplifying it)
posted_by is an unofficial foreign key, or referencing the primary key of another table.
To insert, what you can do is along the lines of this:
INSERT INTO posts(...., posted_by) VALUES (...., user.ID)
Where .... is referencing all of your other information to insert
Then, to find information on someone who posted something:
SELECT * FROM users_log_info WHERE ID = Post.posted_by
Or if you want to find all posts by a user:
SELECT * FROM posts WHERE posted_by = user.ID
So, if Bob, who is User ID 3 wants to post "Hi", you might be able to do:
INSERT INTO posts(content, posted_by) VALUES('Hi', bob.ID)
And then when you are outputting the post you might do this:
post = (however you choose the post to put on the page)
userPosted = SELECT * FROM users_log_info WHERE ID = post.posted_by
print post.content + " posted by: " userPosted.Name
Essentially, the field "posted_by" is, to "posts" an arbitrary number, but you know that it links to, or references, a user. It does that by referencing "ID", which is the primary key of users_log_info, so that when you want to get information from users_log_info, is all you need to do is select the entry which has the ID that corresponds to "posted_by". I do recommend naming it something like posterID, however, for easier identification.

Storing user activity? PHP, MySQL and Database Design

Ok so a user comes to my web application and gets points and the like for activity, sort of similar (but not as complex) as this site. They can vote, comment, submit, favorite, vote for comments, write description etc and so on.
At the moment I store a user action in a table against a date like so
Table user_actions
action_id - PK AI int
user_id - PK int
action_type - varchar(20)
date_of_action - datetime
So for example if a user comes along and leaves a comment or votes on a comment, then the rows would look something like this
action_id = 4
user_id = 25
action_type = 'new_comment'
date_of_action = '2011-11-21 14:12:12';
action_id = 4
user_id = 25
action_type = 'user_comment_vote'
date_of_action = '2011-12-01 14:12:12';
All good I hear you say, but not quite, remember that these rows would reside in the user_actions table which is a different table to the ones in which the comments and user comment votes are stored in.
So how do I know what comment links to what row in the user_actions?
Well I could just link to the unique comment_id in the comments table to a new column, called target_primary_key in the user_actions table?
Nope. Can't do that because the action could equally have been a user_comment_vote which has a composite key (double key)?
So the thought I am left with is, do I just add the primary keys in a column and comma deliminate them and let PHP parse it out?
So taking the example above, the lines below show how I would store the target primary keys
new_comment
target_primary_keys - 12 // the unique comment_id from the comments table
user_comment_vote
target_primary_keys - 22,12 // the unique comment_id from the comments table
So basically a user makes an action, the user_actions is updated and so is the specific table, but how do I link the two while still allowing for multiple keys?
Has anyone had experience with storing user activity before?
Any thoughts are welcome, no wrong answers here.
You do not need a user actions table.
To calculate the "score" you can run one query over multiple tables and multiply the count of matching comments, ratings etc. with a multiplier (25 points for a comment, 10 for a rating, ...).
To speed up your page you can store the total score in an extra table or the user table and refresh the total score with triggers if the score changes.
If you want to display the number of ratings or comments you can do the same.
Get the details from the existing tables and store the total number of comments and ratings in an extra table.
The simplest answer is to just use another table, which can contain multiple matches for any key and allow great indexing options:
create table users_to_actions (
user_id int(20) not null,
action_id int(20) not null,
action_type varchar(25) not null,
category_or_other_criteria ...
);
create index(uta_u_a) on users_to_actions(user_id, action_id);
To expand on this a bit, you would then select items by joining them with this table:
select
*
from
users_to_actions as uta join comments as c using(action_id)
where
uta.action_type = 'comment' and user_id = 25
order by
c.post_date
Or maybe a nested query depending on your needs:
select * from users where user_id in(
select
user_id
from
users_to_actions
where
uta.action_type = 'comment'
);

MySQL: How to get a sequential number with rows?

How can I number my results where the lowest ID is #1 and the highest ID is the #numberOfResults
Example: If I have a table with only 3 rows in it. whose ID's are 24, 87, 112 it would pull like this:
ID 24 87 112
Num 1 2 3
The reason why I want this, is my manager wants items to be numbered like item1, item2, etc. I initially made it so it used the ID but he saw them like item24, item87, item112. He didn't like that at all and wants them to be like item1, item2, item3. I personally think this is going to lead to problems because if you are deleting and adding items, then item2 will not always refer to the same thing and may cause confusion for the users. So if anyone has a better idea I would like to hear it.
Thanks.
I agree with the comments about not using a numbering scheme like this if the numbers are going to be used for anything other than a simple ordered display of items with numbers. If the numbers are actually going to be tied to something, then this is a really bad idea!
Use a variable, and increment it in the SELECT statement:
SELECT
id,
(#row:=#row+1) AS row
FROM table,
(SELECT #row:=0) AS row_count;
Example:
CREATE TABLE `table1` (
`id` int(11) NOT NULL auto_increment,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
INSERT INTO table1 VALUES (24), (87), (112);
SELECT
id,
(#row:=#row+1) AS row
FROM table1,
(SELECT #row:=0) AS row_count;
+-----+------+
| id | row |
+-----+------+
| 24 | 1 |
| 87 | 2 |
| 112 | 3 |
+-----+------+
How it works
#row is a user defined variable. It is necessary to set it to zero before the main SELECT statement runs. This can be done like this:
SELECT #row:=0;
or like this:
SET #row:=0
But it is handy to tie the two statements together. This can be done by creating a derived table, which is what happens here:
FROM table,
(SELECT #row:=0) AS row_count;
The the second SELECT actually gets run first. Once that's done, it's just a case of incrementing the value of #row for every row retrieved:
#row:=#row+1
The #row value is incremented every time a row is retrieved. It will always generate a sequential list of numbers, no matter what order the rows are accessed. So it's handy for some things, and dangerous for other things...
Sounds like it would be better just making that number in your code instead of trying to come up with some sort of convoluted way of doing it using SQL. When looping through your elements, just maintain the sequentiality there.
What is the ID being used for?
If it's only for quick and easy reference then that's fine, but if it's to be used for deleting or managing in any way as you mentioned then your only option would be to assign a new ID column that is unique for each row in the table. Doing this is pointless though because that duplicates the purpose of your initial ID column.
My company had a similar challenge on a CMS system that used an order field to sort the articles on the front page of the site. The users wanted a "promote, demote" icon that they could click that would move an article up or down.
Again, not ideal, but the strategy we used was to build a promote function and accompanying demote function that identified the current sort value via query, added or subtracted one from the previous or next value, respectively, then set the value of the initially promoted/demoted item. It was also vital to engineer the record insert to accurately set the initial value of newly added records so inserts wouldn't cause a duplicate value to be added. This was also enforced at the DB level for safety's sake. The user was never allowed to directly key in the value of the sort, only promote or demote via icons. To be honest, it worked quite well for the user.
If you have to go this route.....it's not impossible. But there is brain damage involved....

Rating System in PHP and MySQL

If we look at the stackoverflow website we have votes. But the question is what is the bestway to store who has voted and who has not. Lets also simplify this even more and say that we can only vote Up, and we can only Remove the Up vote.
I was thinking having the table to be in such form
question - Id(INT) | userId(INT) | title(TEXT) | vote(INT) | ratedBy(TEXT)
Thre rest is self explanitory but ratedBy is a Comma Seperated Id values of the Users.
I was thinking to read the ratedBy and compare it with the userId of the current logged in User. If he dosent exist in the ratedBy he can vote Up, otherwise he can remove his vote. Which in turn will remove the value from ratedBy
I think to make another table "vote" is better. The relationship between users and votes is n to n, therefore a new table should be created. It should be something like this:
question id (int) | user id (int) | permanent (bool) | timestamp (datetime)
Permanent field can be used to make votes stay after a given time, as SO does.
Other fields may be added according to desired features.
As each row will take at least 16B, you can have up to 250M rows in the table before the table uses 4GB (fat32 limit if there is one archive per table, which is the case for MyISAM and InnoDB).
Also, as Matthew Scharley points out in a comment, don't load all votes at once into memory (as fetching all the table in a resultset). You can always use LIMIT clause to narrow your query results.
A new table:
Article ID | User ID | Rating
Where Article ID and User ID make up the composite key, and rating would be 1, indicating upvote, -1 for a downvote and 0 for a removed vote (or just remove the row).
I believe your design won't be able to scale for large numbers of voters.
The typical thing to do is to create to tables
Table 1: question - Id(INT) | userId(INT) | title(TEXT)
Table 2: question - ID(INT) | vote(INT) | ratedBy(TEXT)
Then you can count the votes with a query like this:
SELECT t1.question_Id, t1.userId, t1.title, t2.sum(vote)
FROM table1 t1
LEFT JOIN table2 t2 ON t1.question_id = t2.question_id

Categories