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

标签 php mysql wordpress

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

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

global $wpdb;

    $_POST['user_id'];
    $_POST['div_id'];
    $_POST['x_pos'];
    $_POST['y_pos'];

    $user_id    = $_POST['user_id'];
    $div_id     = $_POST['div_id'];
    $x_pos      = $_POST['x_pos'];
    $y_pos      = $_POST['y_pos'];

$wpdb->query($wpdb->prepare(" INSERT INTO coords

   (user_id, div_id, x_pos, y_pos)
    VALUES (%d, %s, %d, %d)
    ON DUPLICATE KEY UPDATE
    x_pos = VALUES(x_pos), y_pos = VALUES(y_pos)",

    $user_id,
    $div_id,
    $x_pos,
    $y_pos
    ));

最佳答案

作为@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”的实例。因此,互联网上的很大一部分运行着这种竞争条件的多个实例,看起来似乎很好,只是为了让您了解我们正在谈论的天文机会。

无论有什么机会,使用它都是不好的做法。评论道,数据库应该关心数据而不是 PHP。

真正的解决方案

无论出于何种原因,Wordpress 都没有“插入重复更新”功能,这意味着您必须每次使用 $wpdb->query 编写一个查询,或者构建自己的函数来处理它。我选择编写一个函数,因为每次编写 wpdb->query 都很痛苦,并且会让用户更接近意外的 mysql 注入(inject)。还有开发速度。

/**
 * 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->update 函数调用一样。

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)
);

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

关于php - 检查唯一键并更新行或创建一个新的 WordPress,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31150241/

相关文章:

php - 提交后显示表单数据

mysql - 每 5 分钟将 MSSQL 数据复制到 MySQL DB?

php - WordPress admin-ajax.php 失败

mysql - b2evolution 到 WordPress 移动和转换

wordpress - 这在我的网站控制台中是什么意思, "JQMIGRATE: Migrate is installed, version 1.4.1 jquery-migrate.min.js?ver=1.4.1:2 "

php - 重置 PDO 结果中的数组指针

php - 如何使用 LIKE 语句创建 PDO 参数化查询?

PHP/OCI - 无法从临时表中的 Oracle 过程中获取结果

mysql - 当 node js 中的 mysql 数据库更改时通知

php - 如何使用 PHP 从用户定义的值增加 mysql 数据库列中的值?