【问题标题】:shorthand PDO query速记 PDO 查询
【发布时间】:2016-09-27 01:01:58
【问题描述】:

目前要使用 PDO 执行查询,我使用以下代码行:

$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

经过一番研究,我发现了一种执行相同命令的更短方法:

$stmt_test = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$stmt_test->execute([$id])->fetchAll(PDO::FETCH_ASSOC);
$result = $stmt_test->fetchAll(PDO::FETCH_ASSOC);

从那里我认为我可以使用以下代码使其更短:

$stmt_test = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$result = $stmt_test->execute([$id])->fetchAll(PDO::FETCH_ASSOC);

但我收到以下错误:

致命错误:在非对象上调用成员函数 fetchAll() /home/.../index.php 第 20 行

问题:为什么会出现此错误?据我了解,$stmt_test->execute([$id]) 应该首先执行,然后其结果将执行->fetchAll(PDO::FETCH_ASSOC) 并从那里将数组返回到$result,但是由于发生了错误,所以我的逻辑一定有缺陷。我究竟做错了什么?另外,有谁知道执行上一个查询的更好的速记方法吗?

【问题讨论】:

标签: php mysql pdo


【解决方案1】:

因此,您已经得到了“为什么我收到此错误”问题的答案,但没有得到“速记 PDO 查询”的答案。

为此,我们需要一些叫做“编程”的东西。

关于编程的一个有趣的事情是,我们不局限于现有的工具,就像其他职业一样。通过编程,我们总是可以创建自己的工具,然后开始使用它而不是一整套旧工具。

而面向对象编程尤其擅长于此,因为我们可以获取现有对象并添加一些功能,其余部分保持不变。

例如,假设我们想要一种在 PDO 中运行准备好的查询的简写方式。我们所需要的只是用一种新的速记方法扩展 PDO 对象。最难的部分是为新方法命名。

剩下的很简单:你只需要几行代码

class MyPDO extends PDO
{
    public function run($sql, $bind = NULL)
    {
        $stmt = $this->prepare($sql);
        $stmt->execute($bind);
        return $stmt;
    }
}

这是您需要的所有代码。您可以将其存储在存储数据库凭据的同一文件中。请注意,此添加不会以任何方式影响您现有的代码 - 它保持完全相同,您可以像往常一样继续使用所有现有的 PDO 功能。

现在你只需在 PDO 构造函数中更改 2 个字母,将其称为

$conn = new MyPDO(...the rest is exactly the same...);

您可以立即开始使用您闪亮的新工具:

$sql = "SELECT * FROM myTable WHERE id = :id";
$result = $conn->run($sql, ['id' => $id])->fetchAll(PDO::FETCH_ASSOC);

或者,给它一点优化,

$result = $conn->run("SELECT * FROM myTable WHERE id = ?", [$id])->fetchAll();

因为您始终可以一次性设置默认提取模式,并且对于单个变量,命名占位符没有用处。与公认的答案相比,这使得这段代码成为真正的简写

$stmt_test = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$stmt_test->execute([$id]);
$result = $stmt_test->fetchAll(PDO::FETCH_ASSOC);

甚至是迄今为止你得到的最佳答案,

$result = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$result->execute([$id]);

更不用说后者并不总是可用的,因为它只适合获取一个数组。使用 real 速记时,任何结果格式都是可能的:

$result = $conn->run($sql, [$id])->fetchAll(); // array
$result = $conn->run($sql, [$id])->fetch(); // single row
$result = $conn->run($sql, [$id])->fetchColumn(); // single value
$result = $conn->run($sql, [$id])->fetchAll(PDO::FETCH_*); // dozens of different formats

【讨论】:

  • 大声笑点很好,这是一个非常完整的答案,毫无疑问,与光荣的绿色复选标记相反
  • 谢谢。这个标记实际上不是给我的,而是给其他正在寻找速记 PDO 功能的开发人员。因为,我想,我的解决方案是唯一真正回答问题的解决方案,而不仅仅是承认问题。
  • 为 PDO 创建一个包装类在 imo 中似乎有点过头了,绝对是为了创建一种方法,将两种方法简单地组合成一个方法。答案是没有更快的方法使用 PDO 执行准备好的语句。这是一个很大的答案,但必须同意你的观点。
  • @JasonK 它不是一个包装类。它是一个类扩展——对于 OOP 来说就像人类的呼吸一样自然。你会称呼吸为矫枉过正吗?
  • @YourCommonSense 你会不会对 pdo / mysql 中的事务了解很多?以下问题急需一个 pdo 向导:stackoverflow.com/questions/37557110/…
【解决方案2】:

$stmt_test->execute([$id]) 返回一个 boolean 值。这意味着

$result = $stmt_test->execute([$id])->fetchAll(PDO::FETCH_ASSOC);

无效。相反,你应该这样做

$stmt_test->execute([$id]);
$result = $stmt_test->fetchAll(PDO::FETCH_ASSOC);

【讨论】:

  • 完美答案。当时间限制到期时,我会接受这个答案。没有意识到 $stmt_test 正在返回一个布尔值,因此 fetchAll 被应用于该返回值而不是 $stmt_test 本身。
  • @Webeng 我不会把这个答案称为完美的,因为它不会让你的代码更短。
【解决方案3】:

您遇到的错误来自 PDO 的设计方式。 PDOStatement::execute() 不返回语句,而是一个表示成功的布尔值。因此,您想要的快捷方式是不可能的。

http://php.net/manual/en/pdostatement.execute.php中的函数定义

另外让我补充一点,forEach() 通常(并非总是)是一种代码异味,并且占用相对较多的内存,因为它必须将所有行存储为 PHP 值。

【讨论】:

  • 哦,那你建议我改成while ($row = $stmt_test->fetch(PDO::FETCH_ASSOC)){...},这样我就不用上面的代码做foreach($result as ...)了吗?
  • PDOStatement 实现了可遍历,所以你可以做到 foreach ($stmt_test as ...) (虽然我必须承认,我不确定它使用哪种模式,可能是 PDO::FETCH_BOTH)用这种方法你总是只是将当前元素保存在内存中并只迭代一次而不是两次
  • 哪种方法可以重复两次? xD 我不确定我是否理解我正在执行的代码是否执行了两次,现在我真的很想知道哈哈
  • @webeng 您可以通过指定构造函数的第四个选项参数来指定实例化时的获取模式,例如[PDO::ATTR_DEFAULT_FETCH_MODE=> PDO::FETCH_ASSOC]
  • "foreach 是一种代码异味" - 因此,为了消除异味,我将停止将数组传递给模板,并在查询执行后立即开始回显 HTML。确实是个好建议,从现在开始会使用它。
【解决方案4】:

我相信 PDO 的 execute() 方法返回 true 或 false。正如错误已经告诉你的那样:fetchAll() 需要一个对象。使用三行代码将是最短的方法。

另一种选择是使用像propel 这样的 ORM,它运行起来非常流畅,可以为您节省大量时间。

【讨论】:

  • 推荐工具的答案将始终受益于适用于 OP 需求的真实示例。否则他们将不会受到关注。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-09
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多