修改后的 PDO 和 lastInsertId()

Modified PDO & lastInsertId()

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

在多次失败的尝试之后,我所拥有的是我所能做到的最好的。显然这是行不通的。对我的代码的任何更正将不胜感激。

这是数据库class:

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)

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

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

这个 "new form of PDO" 只是 common mistakes 的一个展示(太常见了,我什至写了一篇关于它的文章)。其中一个问题是:

You have to understand that each PDO instance creates a distinct connection to DB server. Thus, you should never ever opening and closing a new connection in each function. Because it will considerably slow down your PHP and won't let you utilize some DB features that can be used only within the same connection - i.e. transactions or getting the insert id.

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

有两种方法可以解决此问题:一种目前比较容易,但将来会使事情变得更难,另一种实现起来有点困难,但可以降低代码的耦合度并更易于维护。两者都在我的另一篇关于制作这样的文章中进行了解释 "sleek & concise class-based system of PDO"(但基于在 Stack Overflow 上看到的大量经验和问题)。

最合适的解决方案是获取静态内容,因为它使得 . So the best solution would be to create a regular class and then create a single instance 必须在所有代码中传递。在这种情况下,所有对 $this->dbh 的调用都将有意义并指向同一个 PDO 实例。在这种情况下,您必须将 DB:query() 符号更改为 $db->query().

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

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,因此您必须在 class.

中复制所有 PDO 方法

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

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

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