Issues incrementing a field in MySQL/PHP with prepared statements

谁说胖子不能爱 提交于 2019-12-25 02:09:53

问题


I have the following code which is supposed to increment a field value by 1 in a prepared PHP mysql statement:

function db_OP_doVote($pdo, $postid, $votetype)
{
    $prepStatement = $pdo->prepare(
        "UPDATE content_posts SET `:votetype` = `:votetype` + 1 WHERE `id` = :id"
    );

    $prepStatement->execute(array(':votetype' => $votetype, ':id' => $postid));

    echo "Success";
}

This however, does nothing. No error is thrown back about incorrect SQL syntax and the script runs to completion, but my field does not update at all.

The values for this script are fed through a jQuery post() to this script:

//isset checking here
$postID = (int)$_POST['id'];
$voteType = $_POST['type'];

if ($voteType == "y")
{
    $trueType = "v-cool";
}
elseif ($voteType == "m")
{
    $trueType = "v-meh";
}
elseif ($voteType == "n")
{
    $trueType = "v-shit";
}
else 
{
    die();
}

$db = db_Connect();

db_OP_doVote($db, $postID, $trueType);

Which also appears to filter the values and send them fine. I can't wrap my head around what the issue could be. The field being incremented is a BIGINT(20).

What am I missing?

EDIT: Solved the issue.

N.B's comment hit the nail on the head - binding the column name causes it to be quoted, which invalidates the query. Thanks!


回答1:


You can't parameterize column names with PDO. What you can do is have hard-coded values (which you basically already have) and construct the SQL string accordingly. I would check this value in the actual function too though, just to be on the safe side:

function db_OP_doVote($pdo, $postid, $votetype)
{
    if( !in_array( $votetype, array( 'v-cool', 'v-meh', 'v-shit' /*, etc. */ ), true ) )
    {
        throw new InvalidArgumentException( 'Unexpected $votetype: ' . $votetype );
        // or simply return false perhaps
    }

    $sql = '
        UPDATE content_posts
        SET `' . $votetype . '` = `' . $votetype . '` + 1
        WHERE `id` = :id
    ';

    $prepStatement = $pdo->prepare( $sql );

    $prepStatement->execute(array(':id' => $postid));

    echo "Success";
}

However, this strategy suggests your database design could use a little more attention. The way you have it now, is that for every type of vote, you have a column. This is not really efficient and/or flexible database design. What happens if you get asked to add another type of vote?

I'd suggest adding another table, to be more flexible:

CREATE TABLE `content_post_vote` (
  `content_post_id` int(11) NOT NULL,
  `vote_type` enum('cool','meh','shit') NOT NULL, # using enum() to assure valid vote types 
  `votes` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`content_post_id`,`vote_type`)
)

Then your query would be something like:

$sql = '
    INSERT INTO `content_post_vote` (`content_post_id`,`vote_type`,`votes`)
    VALUES( :id, :votetype, 1 )
    ON DUPLICATE KEY UPDATE `votes` = `votes` + 1
';

What this does is insert a vote if there is no record for a certain primary key (content_post_id,vote_type) yet, and else update the record with a vote if the record already exists.

Then to query the database for how many votes of a particular type a particular content_post has gotten, you do this:

 $sql = '
     SELECT `votes` # or perhaps more columns
     FROM `content_post_vote`
     WHERE `content_post_id` = :id AND 
           `vote_type` = :votetype
 ';



回答2:


you can't use binding for the field names.

from the question it seems that your setup is wrong.

you should have another table with votes and vote types as data.



来源:https://stackoverflow.com/questions/7835024/issues-incrementing-a-field-in-mysql-php-with-prepared-statements

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!