检查唯一键并更新行或创建新的WordPress

时间:2015-06-30 23:31:59

标签: php mysql wordpress wpdb

我有一个可拖动的div,其中位置被保存到数据库,user_id设置为unique。我如何检查user_id是否存在..如果存在,则更新其他3列..如果它不存在,则插入新行。我已阅读使用“ON DUPLICATE KEY UPDATE”,但很难实现它。有什么想法吗?

正如@ N.B所解释的那样。 - 工作解决方案

{{1}}

1 个答案:

答案 0 :(得分:2)

@ N.B.在评论中指出,虽然我提交的第一种方法有效,但它对竞争条件开放。我要感谢他指出这一点。这是第一个有竞争条件的解决方案:

$user_exists = $wpdb->get_var(
    $wpdb->prepare("SELECT user_id FROM coords WHERE user_id = %d", $user_id)
);
if($user_exists) {
    // Do Update
}
else {
    // Do Insert
}

这些竞争条件是天文数字,因为插入必须在第一个查询返回和下一个插入查询之间的时间内完成执行。但是竞争条件是存在的,因此可能会在某个时间点发生。

但是您的数据库仍然是安全的。当它发生时它不会复制数据,而是它会抛出一个wpdb错误,一个唯一的密钥已经存在,插入将无声地失败(它不会终止脚本,除非报告错误,否则它不会输出错误打开了。)

WordPress database error: [Duplicate entry '3' for key 'PRIMARY']

令人惊讶的是,上述技术用于Wordpress核心以及无数的插件和主题作者,我只能在Wordpress核心中找到两个“ON DUPLICATE”正确使用的实例。所以互联网的很大一部分都有这种竞争条件的多个实例似乎很好,只是为了让你了解我们所谈论的天文机会。

无论机会多少,使用它都是不好的做法。作为N.B.评论说,数据库应该担心数据,而不是PHP。

真正的解决方案

Wordpress,无论出于何种原因,都没有“INSERT ON DUPLICATE UPDATE”功能,这意味着您每次都必须使用$ wpdb->查询来编写查询,或者构建自己的函数来处理它。我去编写一个函数,因为每次编写wpdb->查询都很痛苦,并且让用户更接近意外的mysql注入。也是发展速度。

/**
 * Insert on Duplicate Key Update.
 *
 * Wordpress does not have an 'insert on duplicate key update' function, which
 * forces user's to create their own or write standard queries. As writing
 * queries is annoying and open to mysql injection via human error, this function
 * automates this custom query in an indentical fashion to the core wpdb functions.
 * Source: http://stackoverflow.com/a/31150317/4248167
 *
 * @global wpdb $wpdb
 * @param string $table The table you wish to update.
 * @param array $data The row you wish to update or insert, using a field => value associative array.
 * @param array $where The unique keys that you want to update at or have inserted, also a field => value array.
 * @param array $data_formats Wordpress formatting array for the data. Will default to %s for every field.
 * @param array $where_formats Wordpress formatting array for the where. Will default to %s for every field.
 * @return boolean True if successfully inserted or updated the row.
 */
function insertOrUpdate($table, $data, $where, $data_formats = array(), $where_formats = array())
{
    if(!empty($data) && !empty($where)) {
        global $wpdb;
        // Data Formats - making sure they match up with the data.
        $default_data_format = (isset($data_formats[0])) ? $data_formats[0] : '%s';
        $data_formats = array_pad($data_formats, count($data), $default_data_format);
        $data_formats = array_splice($data_formats, 0, count($data));
        // Where Formats - making sure they match up with the where data.
        $default_where_format = (isset($where_formats[0])) ? $where_formats[0] : '%s';
        $where_formats = array_pad($where_formats, count($where), $default_where_format);
        $where_formats = array_splice($where_formats, 0, count($where));
        // Get Fields
        $data_fields = array_keys($data);
        $where_fields = array_keys($where);
        // Create Query
        $query =
            "INSERT INTO $table" .
            " (" . implode(', ', array_merge($data_fields, $where_fields)) . ")" .
            " VALUES(" . implode(', ', array_merge($data_formats, $where_formats)) . ")" .
            " ON DUPLICATE KEY UPDATE";
        // Compile update fields and add to query
        $field_strings = array();
        foreach($data_fields as $index => $data_field) {
            $field_strings[] = " $data_field = " . $data_formats[$index];
        }
        $query .= implode(', ', $field_strings);
        // Put it all together - returns true on successful update or insert 
        // and false on failure or if the row already matches the data.
        return !!$wpdb->query(
            $wpdb->prepare(
                $query,
                array_merge(
                    array_merge(
                        array_values($data),
                        array_values($where)
                    ),
                    array_values($data)
                )
            )
        );
    }
    return false;
}

要使用它,只需输入参数就像使用$ wpdb->更新函数调用一样。

insertOrUpdate(
    'testing_table',
    array('column_a' => 'hello', 'column_b' => 'world'),
    array('id' => 3),
    array('%s', '%s'),
    array('%d')
);

在你的情况下,它将是:

insertOrUpdate(
    'coords', 
    array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos), 
    array('user_id' => $user_id),
    array('%d', '%d', '%d'),
    array('%d')
);

或者您可以使用默认格式:

insertOrUpdate(
    'coords', 
    array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos), 
    array('user_id' => $user_id),
    array('%d')
);

或者您可以忽略默认格式为字符串的格式:

insertOrUpdate(
    'coords', 
    array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos), 
    array('user_id' => $user_id)
);

如果您发现任何问题,请告诉我。