I'm really new at back-end stuff and I'm building a Facebook app with multiple photo entries with voting mechanics. You can vote one or multiple entries once per day and it also detects your Facebook ID so that when you vote, the database detects your Facebook ID, the entry number you voted, and the date you voted. When you vote during the current date, a pop-up will appear that says "you successfully voted". Then when you try again the same date, the pop-up's message will change and instead, it will say "try to vote again tomorrow". It also shows the number of votes on the entry page.
My only problem is that when you're a first time user, it works fine, you can vote all the entries on the current date, but then when you come back the next day, when you vote, the pop-up will still say that you already voted, and the vote count will not update.
Here's the code for the PHP page: (edited this, already solved the problem, thanks for the help)
date_default_timezone_set('Asia/Manila');
// Get last vote date: fetch_date_voted returns none if user voted for first time
$current_date = date('Y-m-d');
$fetch_date_return = $this->fetch_date_voted($entry_id, $facebook_id, $table_prefix, $conn);
$last_vote_date = $fetch_date_return['last_date'];
$return['date_last_voted'] = $fetch_date_return;
// Below is a code i got from stackoverflow - 844641242329946 (kristina id)
/*if( $last_vote_date && ($last_vote_date == "none" || (strtotime($last_vote_date) + (60*60*24)) < strtotime($current_date))){*/
// Below is the original code
if( $last_vote_date == "none" || $last_vote_date < $current_date ) {
$table_name = $table_prefix . '_voter';
$query = $conn->query("
INSERT INTO $table_name
(facebook_id, entry_id, date_voted)
VALUES
($facebook_id, $entry_id, '$current_date')
");
//
// After doing INSERT, the variable $query will return a TRUE status
if ( $query ) {
// If the date fetched is TRUE, it will UPDATE
$update = $this->update_count($entry_id, $table_prefix, $conn);
// If update is successful, it will fetch the latest vote_count
if($update) {
$fetched_data = $this->fetch_current_count($entry_id, $table_prefix, $conn);
if ($fetched_data['status']) {
$return['status'] = true;
$return['vote_count'] = $fetched_data['vote_count'];
} else {
$return['status'] = false;
}
} else {
$return['status'] = false;
}
} else {
die('Error : ('. $conn->errno .') '. $conn->error);
$return['status'] = false;
$return['error_response'] = $conn->error;
}
} else {
$return['status'] = false;
$return['error_response'] = 'date_invalid';
}
echo json_encode($return);
//$query->close();
$conn->close();
}
$last_vote_date AND $current_date are string representations of a date, convert them to time and it works:
strotime($last_vote_date) < strotime($current_date)
Beware that this will always return true, because you want them to not use it for a day, so it would be:
if( $last_vote_date && ($last_vote_date == "none" || (strtotime($last_vote_date) + (60*60*24)) < strtotime($current_date))){
to add 24 hours.
Related
I have an "Exam form" in PAGE A, that if submitted writes data into the database, and then redirect to PAGE B.
My problem is that, if I hit back button in PAGE B. It goes back to PAGE A, where I can again submit form and it again writes into the database.
What is the best method to block this back button issue ?
Could I redirect to some other page ?
I am looking for javascript method or php based solution is would be better. SQL based solution is messy.
Note: In SQL database, Primary key is an autogenerated id. I am using a general method save(), to write all data into the db. which basicaly collect all data from the form make it into an object and write it into db. Its a general method that I use in many other forms in the project. I would like to not touch that.
I tried fetching 'apno' & 'qid' from database and compare it while posting second time.
<?php
$j = 1;
foreach ($allquestions as $questions)
{
if (is_post_request())
{
$args = $_POST['answers' . $j];
$check_if_submited = Answers::find_by_objapno($apno);
foreach ($args as $key => $value)
{
if ($key == "qid")
{
$qid = $value;
}
}
if ($check_if_submited->apno == $apno && $check_if_submited->qid == $qid)
{
$session->message('Not Saved. Exam Already Taken.');
redirect_to(url_for('/user/student/showexams.php?id=' . $sid));
}
else
{
$answers = new Answers($args);
$result = $answers->save();
if ($result === true)
{
$answers->aid_create();
if ($j < $qcount)
{
}
else
{
$session->message($j . ' Answers Saved.');
redirect_to(url_for('/user/student/showexams.php?id=' . $sid));
}
}
else
{
echo "Error";
}
}
}
else
{
$answers = new Answers;
}
}
?>
qid is not primary key. db could have multiple qid.
try the following code.
jQuery(document).ready(function($) {
if (window.history && window.history.pushState) {
$(window).on('popstate', function() {
var hashLocation = location.hash;
var hashSplit = hashLocation.split("#!/");
var hashName = hashSplit[1];
if (hashName !== '') {
var hash = window.location.hash;
if (hash === '') {
alert('Back button was pressed.');
window.location='www.example.com';
return false;
}
}
});
window.history.pushState('forward', null, './#forward');
}
});
I have been trying to figure this out, I want to show preloader only once per visit. How can I do that?
Current code:
$(window).on('load', function() {
$('#status').fadeOut();
$('#preloader').delay(350).fadeOut('slow');
$('body').delay(350).css({'overflow':'visible'});
})
Could you try the sessionStorage
if ( ! sessionStorage.getItem( 'doNotShow' ) ) {
sessionStorage.setItem( 'doNotShow', true );
$('#preloader').delay(350).fadeOut('slow');
} else {
$('#preloader').hide();
}
Or take a look on this link How to show website preloader only once
No cookie whether session or persistent will provide a reliable solution to this problem. The only way I can see doing this properly would be to record the ip address of the visitor and check to see if that record matches one in a table:
If it doesn't, load the preloader.
If it does, don't load the preloader.
The only possible problem to this is people periodically clear the temp website data they collect so I would implement a timed record system and set it to a reasonable value like 30 days so if they haven't visited your site in 30 days we will presume they have cleared their cache and they will get the preloader, that would change the criteria to;
If visitor is new, load preloader
If visitor is old but less than 30 days, don't load preloader
If visitor is old and more than 30 days, delete record, create new record, load
preloader
Code example below;
#1: Detect IP
<?php
function myIp() {
$client = #$_SERVER['HTTP_CLIENT_IP'];
$forward = #$_SERVER['HTTP_X_FORWARDED_FOR'];
$remote = $_SERVER['REMOTE_ADDR'];
if(filter_var($client, FILTER_VALIDATE_IP))
{
$ip = $client;
}
elseif(filter_var($forward, FILTER_VALIDATE_IP))
{
$ip = $forward;
}
else
{
$ip = $remote;
}
return $ip;
}
$my_ip = myIp();
global $my_ip;
?>
#2: Operations
<?php
// Set preloader status
$play_preloader = "no";
// Check visitor ip records for current ip
$preloader_one = "select ip from preloader where ip='$my_ip'";
$connect_preloader_one = mysqli_query($con, $preloader_one);
$rows_preloader_one = mysqli_num_rows($connect_preloader_one);
// If no record exists, create a new one
if ($rows_preloader_one == 0) {
$preloader_insert = mysqli_prepare($con, "insert into preloader (ip) values (?)");
mysqli_stmt_bind_param($preloader_insert, "s", $my_ip);
mysqli_stmt_execute($preloader_insert);
$play_preloader = "yes";
// If records exist, find records older than 30 days
} else {
$preloader_two = "select ip,date from preloader where ip='$my_ip' and date < DATE_SUB(now(), INTERVAL 30 DAY)";
$connect_preloader_two = mysqli_query($con, $preloader_two);
// If records older than 30 days found
$rows_preloader_two = mysqli_num_rows($connect_preloader_two);
if ($rows_preloader_two > 0) {
// Delete old records
$preloader_delete = "delete from preloader where ip='$my_ip' and date < DATE_SUB(now(), INTERVAL 30 DAY)";
$preloader_delete_query = mysqli_query($con, $preloader_delete);
// Add new record
$preloader_insert = mysqli_prepare($con, "insert into preloader (ip) values (?)");
mysqli_stmt_bind_param($preloader_insert, "s", $my_ip);
mysqli_stmt_execute($preloader_insert);
$play_preloader = "yes";
// If records exist but no records are older than 1 month
} else {
$play_preloader = "no";
}
}
// Preloader
if ($play_preloader == "yes") {
$preloader = "
enter you html/js/css code for the preloader here
";
} else {
$preloader = "";
}
?>
Now save all the above code in a php file then reference it in your html and then call $preloader;
<html>
<head>
<?php include_once ('mypreloader.php'); ?>
</head>
<body>
<?php echo $preloader; ?>
</body>
</html>
This code has been tested on a running server and is working.
I run a website for a youth sports program that features schedules, standings, and score reporting using simple PHP scripts that manipulate data stored in a MySQL database.
After a game is played, the winning coach will access the score reporting form for that particular game, enter the information, and click submit to update the schedule and standings accordingly. They are then automatically redirected to the schedule page that they came from.
However, several times a season, a coach will unintentionally duplicate a score submission (sometimes creating as many as three or four instances) which does not affect the result posted on the schedule, but does throw the data in the standings out of whack. I'm not sure how exactly this is being accomplished, but I'm trying to fix the problem.
I've been reading up as much as possible on here and the web and believe that I need to implement some sort of token system to the reporting script, but I'm unsure how to exactly write the code? Any advice here would be GREATLY appreciated. Here is the script itself:
<?php
// Connect to the database:
require ('../mysqli_connect.php');
// Validate the school:
if (empty($_POST['school'])) {
echo "You forgot to enter your school.<br>";
$validate = 'false';
} elseif ($_POST['school'] != $_POST['away_team'] && $_POST['school'] != $_POST['home_team']) {
echo "Your school does not match one of the two on file for this game.<br>";
$validate = 'false';
} else {
$school = mysqli_real_escape_string($db, trim($_POST['school']));
$validate = 'true';
}
// Validate the password:
if (empty($_POST['pass'])) {
echo "You forgot to enter your password.<br>";
$validate = 'false';
} else {
$pass = mysqli_real_escape_string($db, trim($_POST['pass']));
$validate = 'true';
}
// Validate the away score:
if (!isset($_POST['away_score'])) {
echo "You forgot to enter the away score.<br>";
$validate = 'false';
} elseif (!is_numeric($_POST['away_score'])) {
echo "You entered an invalid score for the away team.<br>";
$validate = 'false';
} else {
$away_score_confirm = mysqli_real_escape_string($db, trim($_POST['away_score']));
$validate = 'true';
}
// Validate the home score:
if (!isset($_POST['away_score'])) {
echo "You forgot to enter the home score.<br>";
$validate = 'false';
} elseif (!is_numeric($_POST['$home_score']) && $_POST['$home_score'] < 0 ) {
echo "You entered an invalid score for the home team.<br>";
$validate = 'false';
} else {
$home_score_confirm = mysqli_real_escape_string($db, trim($_POST['home_score']));
$validate = 'true';
}
// Determine the winner and loser, and set variables:
if ($_POST['away_score'] > $_POST['home_score']) {
$winner = mysqli_real_escape_string($db, trim($_POST['away_team']));
$winner_score = mysqli_real_escape_string($db, trim($_POST['away_score']));
$loser = mysqli_real_escape_string($db, trim($_POST['home_team']));
$loser_score = mysqli_real_escape_string($db, trim($_POST['home_score']));
$tie = 'no';
} else if ($_POST['away_score'] < $_POST['home_score']) {
$winner = mysqli_real_escape_string($db, trim($_POST['home_team']));
$winner_score = mysqli_real_escape_string($db, trim($_POST['home_score']));
$loser = mysqli_real_escape_string($db, trim($_POST['away_team']));
$loser_score = mysqli_real_escape_string($db, trim($_POST['away_score']));
$tie = 'no';
} else if ($_POST['away_score'] == $_POST['home_score']) {
$tie = 'yes';
$tie1 = mysqli_real_escape_string($db, trim($_POST['away_team']));
$tie2 = mysqli_real_escape_string($db, trim($_POST['home_team']));
$tie_score = mysqli_real_escape_string($db, trim($_POST['away_score']));
}
// Declare remaining hidden inputs as variables:
$league = $_POST['league'];
$table = mysqli_real_escape_string($db, $_POST['table']);
$game_id = mysqli_real_escape_string($db, $_POST['game_id']);
$sport = $_POST['sport'];
// Declare remaining hidden inputs as variables:
$standings_league = $table . "_standings";
// If all conditions are met, process the form:
if ($validate != 'false') {
$q1 = "SELECT school_id FROM user_schools WHERE (school_name='$school' AND pass='$pass')";
$r1 = mysqli_query($db, $q1);
$num = mysqli_num_rows($r1);
if ($num == 1) {
// Get the game ID:
$q2 = "SELECT $game_id FROM $table";
$r2 = mysqli_query($db, $q2);
// Get the row for the game ID:
$row = mysqli_fetch_array($r2, MYSQLI_NUM);
// Perform an UPDATE query to modify the game scores:
$q3 = "UPDATE $table SET home_score='$home_score_confirm', away_score='$away_score_confirm' WHERE game_id=$row[0]";
$r3 = mysqli_query($db, $q3);
if (mysqli_affected_rows($db) == 1) {
$confirm = 'true';
} else {
$confirm = 'false';
}
// Update the winning team in the standings:
$q4 = "SELECT school_id FROM $standings_league WHERE school_name='$winner'";
$r4 = mysqli_query($db, $q4);
// Get the row for the school:
$row2 = mysqli_fetch_array($r4, MYSQLI_NUM);
$q5 = "UPDATE $standings_league SET games=games + 1, win=win + 1, pts_for=pts_for + '$winner_score', pts_against=pts_against + '$loser_score' WHERE school_id=$row2[0]";
$r5 = mysqli_query($db, $q5);
$q6 = "UPDATE $standings_league SET pct=(win / games), avg_for=(pts_for / games), avg_against=(pts_against / games) WHERE school_id=$row2[0]";
$r6 = mysqli_query($db, $q6);
// Update the losing team in the standings:
$q7 = "SELECT school_id FROM $standings_league WHERE school_name='$loser'";
$r7 = mysqli_query($db, $q7);
// Get the row for the school:
$row3 = mysqli_fetch_array($r7, MYSQLI_NUM);
$q8 = "UPDATE $standings_league SET games=games + 1, loss=loss+1, pts_for=pts_for + '$loser_score', pts_against=pts_against + '$winner_score' WHERE school_id=$row3[0]";
$r8 = mysqli_query($db, $q8);
$q9 = "UPDATE $standings_league SET pct=(win / games), avg_for=(pts_for / games), avg_against=(pts_against / games) WHERE school_id=$row3[0]";
$r9 = mysqli_query($db, $q9);
if ($confirm != 'false') {
header('Location: schedules_' . $sport . '_' . $league . '.html?league=' . $league .'&table=' . $table);
} else {
echo "The scores could not be reported due to a system error. Apologies for the inconvenience. If this problem continues, please contact us directly.";
}
} else {
echo "Your school and password combination do not match those on file for this game.";
}
}
mysqli_close($db);
?>
My guess is that these coaches are simply clicking the submit button multiple times while the form is waiting for a response from the server. You could use JS to disable (or hide) the button after the first click:
var button = document.querySelector('input[type=submit]'); // Use whatever selector is appropriate here
button.addEventListener('click', function(ev) {
if (!button.classList.contains('submitting')) { // If this is our first click...
button.className += ' submitting';
} else { // Otherwise prevent submission
ev.preventDefault();
}
});
If you have jQuery available to you, you could also just handle the entire submission process via JS and block it there.
You should be aware of presenting some sort of feedback onto the screen to let the user know that a submission is currently in progress, that'll help alleviate some button mashing as well.
One solution is to add a unique value to the form and when its submitted, add the value to a session. If they hit submit button more than once ( probably what is happening ), it will accept only one submition
Example:
<form>
<input type="hidden" name="submit_id" value="<?php echo mt_rand(); ?>">
// rest of the form
</form>
Php file recieving:
<?php
session_start();
if ( isset( $_POST['submit_id'] ) ) {
if ( !isset( $_SESSION['submit_id'] ) ) {
$_SESSION['submit_id'] = array();
}
if ( !in_array( $_POST['submit_id'], $_SESSION['submit_id'] ) ) {
// validate posted values
// when data is valid, register form as submitted
$_SESSION['submit_id'][] = $_POST['submit_id'];
// add the submitted form data to database
}
else {
echo 'Your data has already been submitted';
}
}
I don't want to read your code so I'll suggest a strategy.
I agree with #relic. Your coach is probably double-clicking the button.
If you can assume that different users will never submit two forms in the same second, then you can "filter" your table to accept only one entry for any given second. Make an index for the (new) seconds column, and make it unique. This will prevent insertions of rows to that table if an entry already exits for that second.
If this leads to conflicts, you can introduce restrictions that enforce every entry to be unique for a combination of other fields in the table. This is called compound keys (SQL). You formulate something like, for this game and user, there can only be one score registration.
MySQL:
create table scores (game_id int, user_id int, score int );
alter table scores add unique index uniq_gus (game_id, user_id, score);
insert into scores (game_id, user_id, score) values (1, 1, 10);
insert into scores (game_id, user_id, score) values (1, 1, 10);
ERROR 1062 (23000): Duplicate entry '1-1-10' for key 'uniq_gus'
In addition, you may want to prevent double-submissions (assuming jQuery):
(function($){
var btn = $('button[type="submit"]');
btn.click(function(event){
event.preventDefault();
btn.attr('disabled','disabled');
$.ajax({
url: 'http://foo.bar/form-endpoint.php',
success: function (data, status, xhr) {
btn.removeAttr('disabled');
},
})
})
})(jQuery);
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
My problem is I have a vote system similar to the one of Stack Overflow. My problem is that a person can spam the vote up button which makes it glitch and make it submit more times than it’s supposed to. For example, if there are 10 up votes on a post, I could repeatedly click the vote up button and the it would add two or three up votes instead of one. Similarly I could do this with the down vote button. How do I prevent this?
Index.php:
<?php
session_start();
require('db.php');
$pid = 2;
$uid = $_SESSION['id'];
$sql = mysqli_query($con, "SELECT * FROM posts WHERE pid = '$pid'"); //check to see how many likes the post has
$r = mysqli_fetch_assoc($sql);
$body = $r['body'];
$likes = $r['likes'];
$sql2 = mysqli_query($con, "SELECT * FROM likes WHERE pid = '$pid' AND uid = '$uid'"); //check to see if user has voted
$n = mysqli_num_rows($sql2);
if ($n == 0) {
//user hasn't liked or down vote anything yet
$liked = "no";
} else {
if ($n > 1) {
//like scammed
echo "<script>alert('Stop spamming for votes. You are banned for spam.')</script>";
exit("You have been banned for spam");
//This isn't fool proof though, and I don't want to ban people for this. It would be best if I could just prevent the vote scam in the first place
}
$r = mysqli_fetch_assoc($sql2);
$type = $r['like_type'];
if ($type == '0') {
$liked = "liked";
} else {
$liked = "disliked";
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script src="//code.jquery.com/jquery-latest.min.js"></script>
<style>
.selected {
color: red;
}
</style>
</head>
<body>
<div class="post">
<p><?php echo $body; ?></p>
</div>
<div class="likes">
Upvote
<span id="votes-<?php echo $pid; ?>"><?php echo $likes; ?></span>
Downvote
</div>
</body>
Javascript vote() function
function vote(type, pid, uid, id, voteId) {
var vote = $('#'+ id);
if (vote.hasClass('selected')) {
//user voted for this
$.post("vote.php", {pid: pid, uid: uid, type: type, vote: 'reset'}, function(d) {
if (d == '0' || d == '1') {
vote.removeClass('selected');
var votes = $('#' + voteId);
var num = votes.text();
if (d == '1') {
votes.text(++num);
} else {
votes.text(--num);
}
} else {
alert('An error occurred')
}
});
} else {
var upVoteId = $('#up-' + pid);
var downVoteId = $('#down-' + pid);
if (upVoteId.hasClass('selected') || downVoteId.hasClass('selected')) {
//user wants to switch votes
$.post('vote.php', {pid: pid, uid: uid, type: type, vote: 'switch'}, function(data) {
var votes = $('#' + voteId);
var num = votes.text();
if (data == '1') {
//downvote successful
votes.text(parseInt(num) - 2);
vote.addClass('selected');
upVoteId.removeClass('selected');
}
if (data == '0') {
//upvote successful
votes.text(parseInt(num) + 2);
vote.addClass('selected');
downVoteId.removeClass('selected');
}
if (d == 'error') {
alert('error');
}
});
} else {
$.post('test2.php', {type: type, pid: pid, uid: uid}, function(d) {
if (d == "1") {
//everything good
$('#' + type + '-<?php echo $pid; ?>').addClass('selected');
var votes = $("#" + voteId).text();
if (type == 'down') {
//downvote
votes = --votes;
$('#' + voteId).text(votes);
} else {
votes = ++votes;
$('#' + voteId).text(votes);
}
} else {
alert('failed');
}
});
}
}
}
}
Vote.php
<?php
session_start();
require('db.php');
if (!isset($_SESSION['id'], $_SESSION['un'])) {
//not logged in
header('Location: index.php');
exit;
} else {
if (!isset($_POST['uid'], $_POST['pid'], $_POST['type'], $_POST['vote'])) {
//form not submitted
header('Location: home.php');
exit;
} else {
$uid = (int)$_SESSION['id'];
$pid = (int)$_POST['pid'];
$type = preg_replace('#[^a-z]#', '', $_POST['type']);
$vote = preg_replace('#[^a-z]#', '',$_POST['vote']); //vote type
if ($vote == 'reset') {
//initiate vote reset
if ($type == 'down') {
//downvote
$sql = mysqli_query($con, "DELETE FROM likes WHERE like_type = '1' AND pid = '$pid' AND uid = '$uid'"); //delete the downvote
$sql2 = mysqli_query($con, "UPDATE posts SET likes = likes + 1 WHERE pid = '$pid'");
if ($sql) {
echo "1"; // 1
exit;
} else {
echo "error";
exit;
}
} else {
//upvote
$sql = mysqli_query($con, "DELETE FROM likes WHERE like_type = '0' AND pid = '$pid' AND uid = '$uid'"); //delete upvote
$sql2 = mysqli_query($con, "UPDATE posts SET likes = likes - 1 WHERE pid = '$pid'");
if ($sql) {
echo "0"; // 0
exit;
} else {
echo "error";
exit;
}
}
}
if ($vote == 'switch') {
//user wanted to switch vote
if ($type == 'down') {
//user had voted up but wants to vote down now
$sql = mysqli_query($con, "DELETE FROM likes WHERE like_type = '0' AND pid = '$pid' AND uid = '$uid'"); //delete the previous vote
$sql2 = mysqli_query($con, "INSERT INTO likes (pid, uid, like_type, date_liked) VALUES ('$pid', '$uid', '1', now())"); //insert new vote
$sql3 = mysqli_query($con, "UPDATE posts SET likes = likes - 2 WHERE pid = '$pid'");
if ($sql AND $sql2 AND $sql3) {
//all three queries were successful
echo "1";
exit;
} else {
echo "error";
exit;
}
} else {
//user had voted down but wants to vote up now
$sql = mysqli_query($con, "DELETE FROM likes WHERE like_type = '1' AND pid = '$pid' AND uid = '$uid'") or die(mysqli_error($con)); //delete the previous vote
$sql2 = mysqli_query($con, "INSERT INTO likes (pid, uid, like_type, date_liked) VALUES ('$pid', '$uid', '0', now())"); //insert new vote
$sql3 = mysqli_query($con, "UPDATE posts SET likes = likes + 2 WHERE pid = '$pid'");
if ($sql AND $sql2 AND $sql3) {
//all three queries were successful
echo "0";
exit;
} else {
echo "error";
exit;
}
}
}
}
}
Test2.php
<?php
require('db.php');
$pid = $_POST['pid'];
$uid = $_POST['uid'];
$type = $_POST['type'];
if ($type == "down") {
//downvote
$type = 1;
$sql = mysqli_query($con, "INSERT INTO likes (uid, pid, like_type, date_liked) VALUES ('$uid', '$pid', '$type', now())");
$sql2 = mysqli_query($con, "UPDATE posts SET likes = likes - 1 WHERE pid = '$pid'");
if ($sql) {
echo '1';
exit;
}
} else {
//upvote
$type = 0;
$sql = mysqli_query($con, "INSERT INTO likes (uid, pid, like_type, date_liked) VALUES ('$uid', '$pid', '$type', now())");
$sql2 = mysqli_query($con, "UPDATE posts SET likes = likes + 1 WHERE pid = '$pid'");
if ($sql) {
echo '1';
exit;
}
}
These are my pages that are currently used. I plan on moving test2.php to vote.php.
In my database I have two tables, one to store all the post details including the number of votes. The second table is to store who voted for what post and if it was an upvote or down.
If I could make my system more efficient, please give me tips or recommendations.
Quick SQL hack: make a unique index on pid,uid so that a user can only ever vote once on a post.
ex: ALTER TABLE vote ADD UNIQUE INDEX pid_uid (pid, uid);
Quick JS hack: set a variable on submit that you don't clear until the response; if the variable is set, you don't submit the form. Thus, spam clicking will do nothing, since every click after the first will be ignored.
ex:
var submitting = false;
function submit_form()
{
if (!submitting)
{
submitting = true;
// example; insert actual arguments for it to work
$.post(
url,
postData,
function (data, textStatus)
{
submitting = false;
// handle data here
},
"json"
);
}
}
Well, there is a lot of improvement to be done.
First you are running open to SQL injection queries. Move that to prepared statement.
Then you can, before insert, check if user already voted for that type, as you already have uid, pid, and like_type. This is server-side.
Client-side your JavaScript could disable clicked button to prevent double click. This will prevent user's to send many requests to the server.
The goal here is let server, PHP, handle the verification if the user already voted to that post, as client -ide is easily manipulated in this case.
Don't forget, move those SQL queries to something safe.
Ultimately, you can only hope to control multiple voting using server-side validation.
Stack Overflow requires the user to sign in to a known account in order to vote, which makes it more difficult (but of course not impossible) for multiple votes.
If you do not require that, the best solution depends on your specific requirements.
A simple, client-only solution is to set a cookie indicating the user has voted. Disable the appropriate UI element if that cookie is set. Someone who clears cookies or uses InPrivate style browsing will easily bypass that. Someone can also write their own client that ignores the cookie. Perhaps it is sufficient for your requirements.
A naive server-side solution is to allow only one vote per IP address. I do not recommend this, but include it so you understand why. Unfortunately, a single user can have multiple IP addresses (just drive down the road on your mobile device and see how many IPs you get), or a single IP can represent multiple physical computers (proxy server).
A solid server-side solution would combine the IP address, user agent, and various aspects of the device to yield a device fingerprint. This is a complex solution, beyond the needs of most websites (but if you need it, there are a few companies out there that offer device fingerprinting). Check out https://panopticlick.eff.org/
Summary
If you can require the user to log in to vote (like StackOverflow), that will often be the best solution.
If you cannot require that, use device fingerprinting if it is in your budget, otherwise rely on a cookie. If you do the latter, it may still be worth logging the IP address and user agent of voters so that you can keep an eye out for blatant cheating.
I'd consider storing a voter's IP address in a mysql table as an INT once they have voted.
After that either just show them the tally, or provide them the option to undo their vote by voting up/down.
Check out the PHP function ip2long:
http://www.php.net/manual/en/function.ip2long.php
Use that to convert the IP address into INT format and store it in your mysql to reference against.
Other Resources:
http://www.php.net/manual/en/function.long2ip.php
http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-aton
I am trying to create a signup form that checks if the user exists in the database, I inserted a sample user and when I tried signing up with that user it didn't say its already been taken. What have I done wrong?
The JavaScript:
function formSubmit()
{
document.getElementById('email_valid').innerHTML = '';
var temail=document.forms["signup_form"]["temail"].value.replace(/^\s+|\s+$/g, '');
var atpos=temail.indexOf("#");
var dotpos=temail.lastIndexOf(".");
if (atpos<1 || dotpos<atpos+2 || dotpos+2>=temail.length)
{
//alert("Not a valid e-mail address");
setTimeout(function(){document.getElementById('email_valid').innerHTML = '<br/>Email must be valid...';},1000);
var temailsub=0;
}
else
{
$.post('/resources/forms/signup/email.php',{email: temail}, function(data){
document.getElementById('email_valid').innetHTML = data;
if(data.exists){
document.getElementById('email_valid').innetHTML = '<br/>The email address you entered is already in use.';
var temailsub=0;
}else{
var temailsub=1;
}
}, 'JSON');
}
if(temailsub==1e)
{
setTimeout(function(){document.getElementById("signup_form").submit();},1000);
}
else
{
return false;
}
}
The PHP file (email.php):
<?php
header('content-type: text/json');
require_once $_SERVER['DOCUMENT_ROOT']."/resources/settings.php";
$query = $pdo->prepare("SELECT * FROM users WHERE email=:email");
$query->execute(array(
":email"=> $_POST['email']
));
echo json_encode(array('exists' => $query->rowCount() > 0));
?>
I have checked and double checked the code, I still cannot see why its not detecting that the email has already been used... what do i need to do to fix this and avoid this in the future?
The problem is that PDOStatement::rowCount() returns the number of rows affected by the last SQL statement. You are performing a SELECT so this value will always be 0. SELECT does not affect any rows. Instead you need to count the number of rows:
$query = $pdo->prepare("SELECT COUNT(*) FROM users WHERE email=:email");
$query->execute(array(
":email"=> $_POST['email']
));
$rows = $query->fetchColumn();
echo json_encode(array('exists' => $rows);
Also from jtheman's comment above, you should replace innetHTML with innerHTML in your JavaScript.