【问题标题】:Modified PDO & lastInsertId()修改 PDO & lastInsertId()
【发布时间】:2020-01-01 00:42:44
【问题描述】:

这只是我不完全理解的一种新形式的 PDO。我知道与此类似的问题已在此站点上解决。但是我被这个新的(对我来说是新的)基于类的 PDO 系统所吸引。它时尚简洁。我想通了,所有动态用户数据都插入到数据库中就好了。但是,我不知道如何使用这种特殊样式包含 lastInsertId()。用户没有输入帖子 ID,我无法使用 GET 请求,这是我通常获取帖子 ID 的方式。

经过多次失败的尝试,我在下面的内容是我能做的最好的。显然它不起作用。对我的代码的任何更正将不胜感激。

这是数据库类:

functions.php

class DB{

    private static function connect(){
        $pdo = new PDO('mysql:host=localhost;dbname=poetionpics;charset=utf8', 'root', '');
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $pdo;
    }
    public static function query($query, $params = array()){
        $stmt = self::connect()->prepare($query);
        $stmt->execute($params);

        if(explode(' ', $query)[0] == 'SELECT'){
        $data = $stmt->fetchALL();
        return $data;
        }
    }
    public function lastInsertId(){

        $pdo = new PDO('mysql:host=localhost;dbname=poetionpics;charset=utf8', 'root', '');
    $pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

    return $pdo->lastInsertId();
        }


}

这是我的插入代码:

action.php

for($count = 0; $count < count($_POST['hidden_post_title']); $count++){

$post_title = (isset($_POST['hidden_post_title'][$count])) ? strip_tags($_POST['hidden_post_title'][$count]) : NULL;
$post_desc = (isset($_POST['hidden_post_desc'][$count])) ? strip_tags($_POST['hidden_post_desc'][$count]) : NULL;
$newvidurl = (isset($_POST['hidden_vid_url'][$count])) ? strip_tags($_POST['hidden_vid_url'][$count]) : NULL;
$url_1 = (isset($_POST['url1_hidden_id'][$count])) ? strip_tags($_POST['url1_hidden_id'][$count]) : NULL;
$url_2 = (isset($_POST['url2_hidden_id'][$count])) ? strip_tags($_POST['url2_hidden_id'][$count]) : NULL;


DB::query('INSERT INTO cloudbook_posts VALUES (\'\', :post_title,  :post_desc)',
array(':post_title'=>$post_title, ':post_desc'=>$post_desc));

//This is possibly problematic code or wrong location....

$postid = DB::lastInsertId('SELECT id FROM cloudbook_posts WHERE  id=:id',
array(':id'=>$_POST['id']))[0]['id'];

//End problematic code

DB::query('INSERT INTO vid_info VALUES (\'\', :newvidurl, :postid)',
array(':newvidurl'=>$newvidurl, ':postid'=>$postid));

DB::query('INSERT INTO url_1 VALUES (\'\', :url_1, :postid)',
array(':url_1'=>$url_1, ':postid'=>$post_id));
}

echo str_replace(array('hidden_post_title', 'hidden_post_desc',    'url1_hidden_id', 'url2_hidden_id', '{', '}', '"', ',', ':'), '',
htmlspecialchars(json_encode($result), ENT_NOQUOTES));

?>

我在数据库中得到的是这样的:

vid_info table
id  |     newvidurl    | postid
69  |   some user data | 0

我想要的是:

vid_info table
id |       newvidurl    | postid
69 |   some user data   | $postid (see action.php for variable value)

【问题讨论】:

  • [0]['id']的目的是什么?
  • 您使用lastInsertId,但您没有插入,您正在尝试选择。
  • 你似乎想多了。没有理由在 sql 查询中选择最后一个插入 id - php 为您完成这些工作。你几乎只需要使用$postid = $db-&gt;lastInsertId(); 或者你一直在使用它$postid = PDO::lastInsertId()
  • 总的来说这是一份不错的工作,但请考虑阅读我在sleek & concise PDO systems' common mistakes 上的文章。并且 lastInsertId() 方法名称绝对不恰当且具有误导性。至少称它为 insert()
  • 达曼,[0]['id]的目的是从'cloudbook_posts'表中的MySql数据库中选择id字段。我没有显示 cloudbook_posts 表 b/c 这个问题没有必要,但是按照顺序,posts 表是 id | post_title | post_desc.....

标签: php database class pdo last-insert-id


【解决方案1】:

这确实是一个有趣的案例。

lastInsertId() 问题是不当设计(部分是货物崇拜复制粘贴代码)的直接后果。

这种“新形式的 PDO”只是 common mistakes 的一个展示(非常常见,我什至写了一篇关于它的文章)。其中一个问题是:

您必须了解每个 PDO 实例都会创建与数据库服务器的不同连接。因此,您永远不应该在每个函数中打开和关闭新连接。因为它会大大降低您的 PHP 速度,并且不会让您利用一些只能在同一连接中使用的数据库功能 - 即事务或 获取插入 ID

所以现在你可以看出问题出在lastInsertId() 方法中创建了一个新连接。解决方案是始终保持相同的连接。

有两种方法可以解决这个问题:一种是暂时更简单,一种是将来会使事情变得更难,另一种是实现起来有点困难,但会使您的代码耦合度降低,并且更容易维护。两者都在我的另一篇文章中解释了如何制作这种“时尚简洁的基于类的 PDO 系统”(但基于 Stack Overflow 上的大量经验和问题)。

最合适的解决方案是获取那些静态的东西,因为它使hard to maintain code。所以最好的解决方案是创建一个常规类,然后 create a single instance 必须在你的所有代码中传递。在这种情况下,所有对 $this-&gt;dbh 的调用都将有意义并指向同一个 PDO 实例。在这种情况下,您必须将 DB:query() 表示法更改为 $db-&gt;query()

但是,我文章中的代码使用了不同的扩展 PDO 的方法。您的方法更好,所以让我们将您的代码重写为常规类

class DB{

    public function __construct()
    {
        $host = '127.0.0.1';
        $db   = 'poetionpics';
        $user = 'root';
        $pass = '';
        $charset = 'utf8mb4';

        $options = [
            \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
            \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
            \PDO::ATTR_EMULATE_PREPARES   => false,
        ];
        $dsn = "mysql:host=$host;dbname=$db;charset=$charset";
        try {
             $pdo = new \PDO($dsn, $user, $pass, $options);
        } catch (\PDOException $e) {
             throw new \PDOException($e->getMessage(), (int)$e->getCode());
        }
    }
    public function query($query, $params = array())
    {
        $stmt = $this->pdo->prepare($query);
        $stmt->execute($params);
        return $stmt;
    }
    public function lastInsertId()
    {
        return $this->pdo->lastInsertId();
    }
}

但请记住,由于您没有扩展 PDO,因此您必须复制类中的所有 PDO 方法。

如果您当时无法忍受放弃这种时尚方法的想法,那么您可以使用静态方法,但会为您保留一个 PDO 实例。上面链接的文章中再次解释了两种方法。

拥有合适的数据库类后,我们可以重写您的查询

$db = new DB();

$sql = 'INSERT INTO cloudbook_posts VALUES (null, :post_title,  :post_desc)';
$db->query($sql, ['post_title'=>$post_title, 'post_desc'=>$post_desc]);

$postid = $db->lastInsertId();

$sql = 'INSERT INTO vid_info VALUES (null, :newvidurl, :postid)';
$db->query($sql, ['newvidurl'=>$newvidurl, 'postid'=>$postid]);

【讨论】:

  • 哦,谢谢,YCS!没有意识到你给我发了这个答案,我仔细阅读后在你的文章页面上问了一个问题。请忽略这个问题。这是完美的!这是我问过的第一个问题,所以我还在习惯一切。我现在就去批准你的回答,我的朋友。
猜你喜欢
  • 2011-02-10
  • 2010-09-22
  • 2012-06-07
  • 2016-01-19
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多