如何在PDO(或MySQLi)查询中正确处理关联数组?

3
我是在一个项目中使用MySQLi(但可以轻松地切换到PDO,见底部的编辑说明)。我想要的是能够处理一个关联数组,并将其转换为mysql查询,而不需要手动键入每个键/值对。我知道这可能很简单,但当涉及到MySQLi/PDO时,我仍在学习过程中。为了清楚起见,这里有一个例子:
假设我有这个关联数组:
$data = array(
    'name' => 'Objective',
    'short_name' => 'OB',
    'generation' => 1
);

我想要创建一个像这样的查询:

UPDATE signals SET ? = ? WHERE sid = '1'

将它变成一个查询,类似于这样(或者可以是UPDATE、INSERT、SELECT等):
UPDATE signals SET
    name = 'Objective', 
    short_name = 'OB', 
    generation = 1 
WHERE sid = '1'

基本上我的问题是:MySQLi或PDO本身是否能够实现这一点?如果可能,我该怎么做?我已经阅读了关于准备语句(preparing statements)之前执行的内容,但我还没有搞清楚。希望有人能帮帮我。
编辑:我还处于项目的早期阶段,所以我也可以使用PDO,但同样的问题依然存在。我确实查看过PDO和mysqli,但我不确定如何使用PDO。因此,为了问题的完整性,我将在标签中添加PDO并在问题中提及它。

2
认真地,使用PDO。这将让你的生活变得轻松许多。 - DaveRandom
1
我应该补充说明,我可以使用PDO,因为项目还处于早期阶段。但是我也不确定如何在那里使用它。 - Bob
你的意思是你想要列名和值都是动态的吗? - Martin Bean
1
是的。上述数组中的键是列名,而值是应存储在记录中的数据。 - Bob
4个回答

5

这是一个函数,它将接收一个输入数组,并生成可以直接用于查询的内容,只要你只更新单个表:

function array_to_pdo_params($array) {
  $temp = array();
  foreach (array_keys($array) as $name) {
    $temp[] = "`$name` = ?";
  }
  return implode(', ', $temp);
}

现在您可以像这样执行PDO查询:
// The data
$data = array(
  'name' => 'Objective',
  'short_name' => 'OB',
  'generation' => 1
);

// The parameter for the WHERE clause
$sid = 1;

// Build the query string
$query = "
  UPDATE `signals`
  SET ".array_to_pdo_params($data)."
  WHERE `sid` = ?
";

// Convert the data array to indexed and append the WHERE parameter(s) to it
$queryData = array_values($data);
$queryData[] = $sid;

$stmt = $db->prepare($query); // Obviously add the appropriate error handling

$stmt->execute($queryData);

谢谢你,Dave。我喜欢这种方法。它接近我要做的事情:拆分数组并手动构建查询。 - Bob

2
您可以通过传递一个输入值数组来使用PDO的执行语句,用于准备查询。参考链接:http://php.net/manual/en/pdostatement.execute.php 示例:
<?php
/* Execute a prepared statement by passing an array of insert values */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
?>

谢谢您的评论。我看到了这个,但是这仍然需要我设置整个查询,我至少必须定义每个列名。这怎么能让我的生活更轻松呢? - Bob
如果你正在寻找一种简单的方法,那么考虑学习一个框架吧。因为大部分你需要的东西都会被预先写成库、帮助程序等形式。例如:CodeIgniter、CakePHP、Zend等等。其中,我使用过CodeIgniter,相对于Zend来说它很容易学习(在我看来)。CodeIgniter框架中有一个叫做“ActiveRecord”的概念,这可能会对你有所帮助:http://codeigniter.com/user_guide/database/active_record.html 就个人而言,我总是尝试手写SQL代码,而不是采用简单的方式。 - Akhilesh B Chandran
2
好的,我知道了。我担心你会这么说哈哈。其实我并不害怕使用框架,但我现在没有时间去学习它。此外,这个项目并不大,一个简单的基于PDO的类就足够了。 - Bob

0

我们可以使用 http_build_query 来构建查询,示例如下:

$columns = array(
  'name',
  'short_name',
  'generation', ...
);

$data = array(
  'name' => 'Objective',
  'short_name' => 'OB',
  'generation' => 1
);

$to_update = array_intersect_key($data, array_flip($columns));
$update_string = urldecode(http_build_query(array_combine(array_keys($columns), array_fill(0, count($columns), '?')), '', ', '));

try {
 $stmt = $db->prepare(sprintf('UPDATE table SET %s WHERE condition=?', $update_string));
 $result = $stmt->execute(array_values($data));

 ...

} catch (\PDOException $ex) { 

}

请确保将 WHERE 条件的参数放在您的 $data 数组末尾。


有趣且稍微有些奇怪的方法,但我猜它应该是可行的。不过它不会引用列名,这有点危险。 - DaveRandom

0

Zend_DB可以让你轻松实现这一点。

虽然我们为此创建了一个包装器以使其更简单,但我真的认为它是最简单的,并且它会为您完成所有神奇的事情。

一旦您学会了它,您真的应该尝试一下;它可以节省大量时间。


尽管我很感激您的评论,但目前我不会在这个项目中这样做。我将来会考虑这个问题。但现在,我想知道如何通过MySQLi或PDO以最佳方式完成此操作。 - Bob
你的项目刚刚开始。MySQLi可以让你传递变量,但它无法动态构建查询。例如列名等,我在我的Github上有一个类可以完全做到这一点,但当我了解到Zend时,它被替换了。如果你害怕Zend或其他解决方案,你可以自己制作。为什么你认为人们创建它们...它有用处。 - user1454661

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接