修改后的 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]);
这只是我不完全理解的一种新形式的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 上看到的大量经验和问题)。
最合适的解决方案是获取静态内容,因为它使得 $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]);