0

I'm in the process of developing a simple web application that provides ratings. A user can vote only ONCE with their IP address. However, the problem is that there are multiple things to rate but whenever a user enters his rating into one field, he cannot enter rating into other ratings. I want my PHP file to know that I only want to validate IP address for one rating and the user can rate other stuff.

Here's my JQuery code:

<h1>Demo 1</h1>
<script>
    $(document).ready(function () {
        $("#demo1 .stars").click(function () {
            var id = 1;

            $.post('rating.php', {rate:$(this).val()}, function(d) {
                if (d>0) {
                    alert('You already rated');
                } else {
                    alert('Thanks For Rating');
                }
            });

            $(this).attr("checked");
        });
    });
</script>
<fieldset id='demo1' class="rating">
    <input class="stars" type="radio" id="star10" name="rating" value="10" />
    <label class = "full" for="star10" title="Very nice - 10/10"></label>
    <input class="stars" type="radio" id="star9" name="rating" value="9" />
    <label class = "full" for="star9" title="Great - 9/10"></label>
    <input class="stars" type="radio" id="star8" name="rating" value="8" />
    <label class = "full" for="star8" title="Great - 8/10"></label>
    <input class="stars" type="radio" id="star7" name="rating" value="7" />
    <label class = "full" for="star7" title="Good - 7/10"></label>
    <input class="stars" type="radio" id="star6" name="rating" value="6" />
    <label class = "full" for="star6" title="Good - 6/10"></label>
    <input class="stars" type="radio" id="star5" name="rating" value="5" />
    <label class = "full" for="star5" title="Meh - 5/10"></label>
    <input class="stars" type="radio" id="star4" name="rating" value="4" />
    <label class = "full" for="star4" title="Meh - 4/10"></label>
    <input class="stars" type="radio" id="star3" name="rating" value="3" />
    <label class = "full" for="star3" title="Sucks - 3/10"></label>
    <input class="stars" type="radio" id="star2" name="rating" value="2" />
    <label class = "full" for="star2" title="Sucks - 2/10"></label>
    <input class="stars" type="radio" id="star1" name="rating" value="1" />
    <label class = "full" for="star1" title="Sucks - 1/10"></label>
</fieldset>

There are multiple such fieldsets with id = demo2, demo3, etc. I want some unique identifier or variable for each demo.

This is how my PHP processes the request and sends it to the database:

   if (isset($_POST['rate']) && !empty($_POST['rate'])) {

    $rate = $conn->real_escape_string($_POST['rate']);
    $sql = "SELECT `id` FROM `tbl_rating` WHERE `user_id`='" . $ipaddress . "'";
    $result = $conn->query($sql);
    $row = $result->fetch_assoc();

    if ($result->num_rows > 0) {
        echo $row['id'];
    } else {
        $sql = "INSERT INTO `tbl_rating` ( `rate`, `user_id`) VALUES ('" . $rate . "', '" . $ipaddress . "'); ";
        if (mysqli_query($conn, $sql)) {
        echo "0";
        }
    }
}

I could think of just one solution which can trigger a flag on click to 1 for demo1, 2 for demo2, 3 for demo3, etc.

Martijn
  • 15,791
  • 4
  • 36
  • 68
  • Using IP as identifier is often a bad approach since there often are many people behind one IP (companies/families/any open wifi etc). Regardless, you need to have a rating table where you have the object (what the person is rating), the rating and the identifier. Then you simply check if the person have rated specific objects. – M. Eriksson Dec 13 '17 at 08:27
  • Hey Magnus, thanks for your comment. Is there any other validator I can use if I don't want people to vote twice? I don't want them to create new accounts either. Also, I'm fine with people behind one IP address to vote once. I just want every fieldset to be voted using that IP address. Right now, my PHP uses this to check if user has voted already: `$sql = "SELECT `id` FROM `tbl_rating` WHERE `user_id`='" . $ipaddress . "'";` @MagnusEriksson – Aakash Phadtare Dec 13 '17 at 08:29
  • 1
    If you want to stop people from rating several times, you need to get a proper identifier, which you simply can't do with anonymous users. You will need to make then create an account (and verify it through email or similar) or use facebook connect. Neither way is bullet proof, but usually good enough. – M. Eriksson Dec 13 '17 at 08:33
  • 1
    You might be fine with it, but will your users be? – M. Eriksson Dec 13 '17 at 08:34
  • I just checked your edited comment and that is what I want to do but I don't know HOW to. – Aakash Phadtare Dec 13 '17 at 08:35
  • so why don't you just store the rating target identifier in the database & use that in the code (which I guess is the same thing as you refer to with your "only solution that you could think of")? – ejuhjav Dec 13 '17 at 08:37
  • Well, if you have multiple ratings, surely you have some rating identifier to know _what_ the person have rated, no? – M. Eriksson Dec 13 '17 at 08:38
  • Can you check @jeprubio's answer? I want $ratedField for every individual FieldSet but I don't know how. – Aakash Phadtare Dec 13 '17 at 08:39
  • getting the id of the parent element (the fieldset) with jquery should do the work – jeprubio Dec 13 '17 at 08:44

1 Answers1

0

If you can rate more than one thing you should update your tbl_rating adding at least one column and then perform the select and insert with that column (let's say rated_field).

if (isset($_POST['rate']) && !empty($_POST['rate']) && isset($_POST['rateField']) && !empty($_POST['rateField'])) {
    $rate = $conn->real_escape_string($_POST['rate']);
    $rateField = $conn->real_escape_string($_POST['rateField']);
    $sql = "SELECT `id` FROM `tbl_rating` WHERE `user_id` = '" . $ipaddress . "' AND `rated_field` = '" . $ratedField . "' LIMIT 1";
    $result = $conn->query($sql);
    if ($result->num_rows > 0) {
        $row = $result->fetch_assoc();
        echo $row['id'];
    } else {
        $sql = "INSERT INTO `tbl_rating` ( `rate`, `rated_field`, `user_id`) VALUES ('" . $rate . "', '" . $ratedField . "', '" . $ipaddress . "'); ";
        if (mysqli_query($conn, $sql)) {
            echo "0";
        }
    }
}

then send the fieldset id as rate_field value:

$(document).ready(function () {
    $(".stars").click(function () {
        var id = 1;

        $.post('rating.php', {rate:$(this).val(), rateField:$(this).parent().attr('id')}, function(d) {
            if (d>0) {
                alert('You already rated');
            } else {
                alert('Thanks For Rating');
            }
        });

        $(this).attr("checked");
    });
});

It will work.

Also, consider using a method to try to get the real ip.

jeprubio
  • 17,312
  • 5
  • 45
  • 56
  • 1
    Please also add a `limit 1`. You're looking for 0 or 1 exactly, then use `limit 1` unles syou have a damn good reason – Martijn Dec 13 '17 at 08:33
  • Hey, @jeprubio this is exactly what I had in my mind but I have no idea how to get my $ratedField for every individual FieldSet. Can you help with that? – Aakash Phadtare Dec 13 '17 at 08:34
  • 1
    Excess double quote on `INSERT` statement – Goma Dec 13 '17 at 08:34
  • @AakashPhadtare you can use your fieldset id, just add it in your $.post with jQuery. I've just edited the answer with this. – jeprubio Dec 13 '17 at 08:37
  • In large databases, with indexes, this still saves me a lot of time (haha counted in ms, but still). You ony need to check whether or not it exists, dont overselect. Also, Indexes are great when you know how to use them, but as this is an entry level question, I provide entry level suggestions. – Martijn Dec 13 '17 at 08:50
  • If the index is unique there should be no need to add the limit 1, but I agree that it can make no harm. – jeprubio Dec 13 '17 at 08:52
  • @jeprubio I did what you wrote in your solution and added this line: `$ratedField = $conn->real_escape_string($_POST['ratedField']);` but it's still not working. – Aakash Phadtare Dec 13 '17 at 08:53
  • can you print the $POST['ratedField'] value? Has it "demo1" as we expect? Just return it instead of the id for a moment and show it in an alert or log it into a file. – jeprubio Dec 13 '17 at 09:03
  • ```$sql = "INSERT INTO `tbl_rating` ( `rate`, `user_id`) VALUES ('" . $rate . "', '" . $ratedField1 . "'); ";``` this is what I did to see temporarily what's inside ratedField1 I also feel like `$ratedField1 = $conn->real_escape_string($_POST['ratedField']);` is wrong – Aakash Phadtare Dec 13 '17 at 09:13
  • I've posted again your php code with the updates. Remember to add the rate_field column to your tbl_rating in your database before running it. – jeprubio Dec 13 '17 at 09:20
  • Thanks for all the help @jeprubio and Martijn It's all working. Would've been impossible without your help. – Aakash Phadtare Dec 13 '17 at 09:49
  • @jeprubio There's a small bug in the program. The number of popups is equalling the number of fieldsets. For example, I have 8 fieldsets now, so when I rate one of them, I get the first one as _"Thank you"_ and then _"You already voted"_ 7 times. Any idea what's causing this? – Aakash Phadtare Dec 13 '17 at 11:57
  • Maybe it because they have the same id on the html, remove them the id attribute as I think it is not used anywhere and try again. If you need it for the css rules just move them as classes and change the selector to match that classes. – jeprubio Dec 13 '17 at 12:02
  • To change this ids as class attributes you should follow this example: *class="stars" id="star1"* will become *class="stars star1"*. The ids should be unique in all the document. – jeprubio Dec 13 '17 at 12:06
  • The id is unique for all fieldsets and all stars. e.g. for first fieldset the ids are star11,star12,star13,star14,etc.; for second fieldset the ids are star21,star22,star23,star24,etc. – Aakash Phadtare Dec 13 '17 at 12:26
  • yes, but the star1 should be unique **in all the html document**, not unique per fieldset. That's why I propose to move star1 as a class so that different elements from the same document can have the star1 class and the html document still follow the rules to be valid. This could be the cause multiple clicks are triggered even though the jquery code looks good. – jeprubio Dec 13 '17 at 12:27