PHP/MYSQL разрешает только один голос на члена?

Я давал задание на работе настроить систему голосования за награды, я не слишком много знаю о php и mysql. Но я знаю об этом больше, чем кто-либо здесь, а мой босс в отпуске. Но я повторно использовал код, который ранее оставался в нашей системе, и адаптировал его для этого года.

В основном система голосования работает нормально, и я настроил новые таблицы в mysql для сбора данных. Однако я нашел один довольно большой недостаток в существующем коде и не знаю, как его изменить. По сути, код позволяет людям голосовать столько раз, сколько они хотят в данный момент. Я хочу ограничить его только одним голосом на члена, чтобы все было честно.

Итак, на данный момент участники входят в систему с номером участника, а затем голосуют. Голоса хранятся в таблицах mysql, и затем я могу суммировать голоса, запрашивая данные.

Я надеялся, что кто-нибудь поможет мне добавить одну-две строки кода, которые будут просто проверять, проголосовал ли уже участник. Когда член голосует, его член нет. хранится в таблицах sql вместе с их выборами голосов. Так что, возможно, лучший способ — посмотреть, существует ли уже мемеберид в таблице, и, если он есть, сообщить пользователю, что он уже проголосовал, или сказать что-то в этом роде.

<?php
//Insert into volunteer awards
$coach=mysql_real_escape_string($_SESSION['coach']);
$official=mysql_real_escape_string($_SESSION['official']);
$young_volunteer=mysql_real_escape_string($_SESSION['young_volunteer']);
$volunteer=mysql_real_escape_string($_SESSION['volunteer']);

$memberid=$_SESSION['MM_Username'];
$association=$_SESSION['MM_Association'];
$region=$_SESSION['Region'];


$sql_query = mysql_query("INSERT INTO awards_2009_votes (`id`, `member_id`, `region`, `coach`, `official`, `volunteer`, `young_volunteer`) VALUES ('', '$memberid', '$region', '$coach', '$official', '$volunteer', '$young_volunteer')") or die (mysql_error());
?>

Спасибо


person Community    schedule 15.09.2009    source источник


Ответы (4)


Вот быстрый и грязный подход:

$sql_query = "SELECT FROM awards_2009_votes WHERE member_id = '$memberid'";
$sql_result = mysql_query($sql_query);
$num_rows = mysql_num_rows($sql_result);

if ($num_rows > 0) {
    // this member has already voted
} else {
    // carry on
}

Однако, как указал Писквор, это решение имеет (по крайней мере) два ограничения:

  1. Он ограничен содержащим его методом, поэтому он не предотвращает множественное голосование вообще — только с помощью этого конкретного метода. (Вы можете написать функцию, содержащую ту же самую проверку, но вам все равно придется вызывать эту функцию везде, где пользователь пытается проголосовать.)
  2. Это приводит к дополнительной нагрузке на базу данных, что может быть неприемлемо в сценарии с высоким трафиком.

Имея в виду эти моменты, я бы рекомендовал сначала запустить сценарий для проверки наличия повторяющихся значений member_id в вашей таблице голосов, удалить все, кроме одного, в каждом случае, а ЗАТЕМ добавить ограничение UNIQUE в вашу таблицу. Оттуда вы можете быть уверены, что в вашей таблице никогда не будет более одной строки с одним и тем же member_id.

person Dan Tao    schedule 15.09.2009
comment
Это работает - вы делаете дополнительный запрос к базе данных. Это может быть проблемой, а может и не быть, в зависимости от трафика и т. д. Кроме того, эта проверка только предотвратит добавление нескольких голосов вашим конкретным скриптом. - person Piskvor left the building; 15.09.2009
comment
Да, ваше решение лучше. Мне показалось, что snoop123, возможно, спрашивал просто о каком-либо способе получить искомую функциональность; Я предложил (как мне показалось) самый простой способ. Одна потенциальная проблема с вашим ответом заключается в том, что он не сработает, если участник уже проголосовал более одного раза; нужно было бы сначала войти и удалить все повторяющиеся строки, а ЗАТЕМ обновить таблицу. (Конечно, учитывая требование snoop123, это, вероятно, следует сделать в любом случае.) - person Dan Tao; 15.09.2009
comment
я знаю, что этот ответ устарел, но он предназначен для будущих читателей, чтобы они не забывали о SQL-инъекциях. - person Raymond Nijland; 11.06.2019

Вы можете добавить ограничение UNIQUE в свою таблицу. Это одноразовая операция - вам не нужно делать это каждый раз при запуске вашего скрипта, это изменение структуры таблицы. Запустите это в своем инструменте администрирования MySQL (например, phpMyAdmin, Navicat, HeidiSQL и т. д.):

ALTER TABLE awards_2009_votes ADD UNIQUE (member_id);

После этого изменения будет невозможно добавить второе голосование с тем же идентификатором члена — ВСТАВКА (или ОБНОВЛЕНИЕ) завершится ошибкой.

Преимущество здесь в том, что проверка выполняется автоматически в базе данных, поэтому вам не нужно беспокоиться о 1) проверке дубликатов с помощью вашего кода или 2) людях, добавляющих несколько голосов вручную.


Как говорит @middaparka, вы должны использовать INSERT IGNORE, чтобы избежать ошибки "дублирующий ключ":

$sql_query = mysql_query("INSERT IGNORE INTO awards_2009_votes (`member_id`, `region`, `coach`, `official`, `volunteer`, `young_volunteer`) VALUES ('$memberid', '$region', '$coach', '$official', '$volunteer', '$young_volunteer')") or die (mysql_error());
if (mysql_insert_id()) {
    // row was inserted - vote added
} else {
    // row was not inserted - already voted
}
person Piskvor left the building    schedule 15.09.2009
comment
Вам также нужно будет изменить вставку голосования, чтобы использовать INSERT IGNORE, чтобы избежать дублирования ошибки ключа, но это кажется лучшим способом, который я бы подумал. - person John Parker; 15.09.2009
comment
@middaperka, у меня та же проблема, что и в исходном вопросе. Я сделал макет своей базы данных похожим на тот, который dnagirl предложил в другом ответе. Когда пользователь пытается проголосовать, он получает ошибку дубликата ключа. Что такое ВСТАВИТЬ ИГНОРИРОВАТЬ? - person irl_irl; 15.09.2009
comment
Как говорится в документации MySQL (первый результат Google для INSERT IGNORE): Если вы используете ключевое слово IGNORE, ошибки, возникающие при выполнении инструкции INSERT, вместо этого рассматриваются как предупреждения. Например, без IGNORE строка, которая дублирует существующий индекс UNIQUE или значение PRIMARY KEY в таблице, вызывает ошибку дублирования ключа, и оператор прерывается. С IGNORE строка по-прежнему не вставляется, но ошибка не выдается. - person Piskvor left the building; 15.09.2009
comment
INSERT IGNORE — разумный вариант. Но если вы хотите, чтобы ваши пользователи могли изменять свои голоса, вы можете подумать о REPLACE INTO, который работает так же, как INSERT INTO, за исключением того, что он заменяет строки, в которых могут появиться повторяющиеся записи. - person dnagirl; 15.09.2009
comment
@dnagirl - Еще лучше, если вы используете MySQL 5.x, вы можете использовать INSERT... ON DUPLICATE KEY UPDATE. - person John Parker; 16.09.2009
comment
@dnagirl: вопреки названию, если REPLACE встречает существующую строку, она выполняет DELETE, за которым следует INSERT (что важно при использовании триггеров, для полей auto_increment и т. д.). - person Piskvor left the building; 18.06.2010
comment
я знаю, что этот ответ устарел, но он предназначен для будущих читателей, чтобы они не забывали о SQL-инъекциях. - person Raymond Nijland; 11.06.2019
comment
Хорошая точка зрения; это должно быть написано с параметрами. Попытался проиллюстрировать суть вопроса, не переписывая его полностью. - person Piskvor left the building; 11.06.2019

Из вашего кода я не уверен, какова ваша структура таблицы, но следующая структура таблицы ограничит голосование пользователей одним голосом за тему:

Таблица пользователей

user_id int unsigned not null auto_increment,
username varchar
// and other user info fields

Таблица тем

topic_id int unsigned not null auto_increment,
topic_title varchar
// and other topic info fields

Таблица для голосования

user_id,
topic_id,
vote_value,
primary key (user_id,topic_id) //this is the constraint that will allow only one vote
person dnagirl    schedule 15.09.2009

Вы можете написать несколько строк следующим образом, хотя я не проверял, но они должны работать...

$result=mysql_query("select count(*) count from awards_2009_votes where member_id='$memberid'");

$has_voted_array=mysql_fetch_array($result);
if($has_voted['count']!=0)
echo "You have already registered youur vote";
else
{
//do normal operation
$sql_query = mysql_query("INSERT INTO awards_2009_votes (id, member_id, region, coach, official, volunteer, young_volunteer) VALUES ('', '$memberid', '$region', '$coach', '$official', '$volunteer', '$young_volunteer')") or die (mysql_error());
}
person Xinus    schedule 15.09.2009
comment
я знаю, что этот ответ устарел, но он предназначен для будущих читателей, чтобы они не забывали о SQL-инъекциях. - person Raymond Nijland; 11.06.2019