I'm trying to figure out how to get the last insert id with the database class I'm using and can't seem to get it right. Keep getting "Call to undefined method PDOStatement::lastInsertId()"
Here's how I call it.
require_once '../classes/pdo.php';
$sql="insert into accounts(account_email,account_password) values(:email,:password)";
$values=array(':email'=>'jay.garrick#email.com',':password'=>'blah');
$res = DB::run($sql,$values);
$id = $res->lastInsertId();
echo $id;
Here's my class.
class DB {
protected static $instance = null;
protected function __construct() {}
protected function __clone() {}
public $pdo;
public $lastInsertId = null;
public static function instance()
{
if(self::$instance == null)
{
$opt = array(
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
);
//$options = array_replace($default_options,$options);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHAR;
self::$instance = new PDO($dsn, DB_USER, DB_PASS, $opt);
}
return self::$instance;
}
public static function __callStatic($method, $args)
{
return call_user_func_array(array(self::instance(), $method), $args);
}
public static function run($sql, $args = NULL)
{
if(!$args)
{
return self::instance()->query($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
Related
This question already has an answer here:
php function returns null instead of string [duplicate]
(1 answer)
Closed 3 years ago.
I just tried a PDO wrapper from https://phpdelusions.net/pdo/pdo_wrapper.
First the PDO Wrapper
class MyPDO
{
protected static $instance;
protected $pdo;
public function __construct() {
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
PDO::ATTR_EMULATE_PREPARES => FALSE,
);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHAR;
$this->pdo = new PDO($dsn, DB_USER, DB_PASS, $opt);
}
// a classical static method to make it universally available
public static function instance()
{
if (self::$instance === null)
{
self::$instance = new self;
}
return self::$instance;
}
// a proxy to native PDO methods
public function __call($method, $args)
{
return call_user_func_array(array($this->pdo, $method), $args);
}
// a helper function to run prepared statements smoothly
public function run($sql, $args = [])
{
if (!$args)
{
return $this->query($sql);
}
$stmt = $this->pdo->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
and then the user class
class User
{
/* #var MyPDO */
protected $db;
protected $data;
public function __construct()
{
$this->db = MyPDO::instance();
}
public function find($id)
{
$this->data = $this->db->run("SELECT * FROM user_account WHERE id = ?", [$id])->fetch();
}
}
and then instantiate class user
$user = new User();
$a = $user->find(3);
var_dump($a);
There is a record associated with ID = 3 but the result I get is NULL, why?
As mentioned in the comments, the find method doesn't return anything, it is probably storing it just fine in $this->data but is never returned.
public function find($id)
{
$this->data = $this->db->run("SELECT * FROM user_account WHERE id = ?", [$id])->fetch();
return $this->data;
}
The discussion of using a Singleton or not is out of scope for this question.
That said, I've found online multiple database connection classes using PDO and following the singleton pattern.
Here is one for example :
<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'test');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_CHAR', 'utf8');
class DB
{
protected static $instance = null;
protected function __construct() {}
protected function __clone() {}
public static function instance()
{
if (self::$instance === null)
{
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => FALSE,
);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHAR;
self::$instance = new PDO($dsn, DB_USER, DB_PASS, $opt);
}
return self::$instance;
}
public static function __callStatic($method, $args)
{
return call_user_func_array(array(self::instance(), $method), $args);
}
public static function run($sql, $args = [])
{
if (!$args)
{
return self::instance()->query($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
However, according to this Stackoverflow answer Is it necessary to close PDO connections
, some users suggest that it's a good practice to close all connections at the end of the script.
So:
1. Why does no one include a destroy/close method?
2. How would it be done right (destroying a PDO inside a singleton?)
My approach would have been:
class DB
{
// ... the whole singleton
public static function close()
{
self::$instance = null;
return null;
}
}
$pdo = DB::getInstance();
// do stuff
$pdo = DB::close($pdo);
I've rewritten the connection to database from mysqli to PDO. My connection works with prepared statements. My class PDO look like that:
<?php
define('DB_HOST', 'xxx');
define('DB_NAME', 'xxx');
define('DB_USER', 'xxx');
define('DB_PASS', 'xxx');
define('DB_CHAR', 'xxx');
class DBController
{
protected static $instance;
protected $pdo;
public function __construct() {
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
PDO::ATTR_EMULATE_PREPARES => FALSE,
);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHAR;
$this->pdo = new PDO($dsn, DB_USER, DB_PASS, $opt);
}
// a classical static method to make it universally available
public static function instance()
{
if (self::$instance === null)
{
self::$instance = new self;
}
return self::$instance;
}
// a proxy to native PDO methods
public function __call($method, $args)
{
return call_user_func_array(array($this->pdo, $method), $args);
}
// a helper function to run prepared statements smoothly
public function run($sql, $args = [])
{
if (!$args)
{
return $this->query($sql);
}
$stmt = $this->pdo->prepare($sql);
$stmt->execute($args);
return $stmt;
}
function insert($query) {
if (!$args)
{
return $this->query($sql);
}
$stmt = $this->pdo->prepare($sql);
$stmt->execute($args);
return $stmt;
}
function update($query) {
if (!$args)
{
return $this->query($sql);
}
$stmt = $this->pdo->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
The connection to database with new class PDO works clearly. But, when I want to login as an user I've got an 'Invalid Login'. However, login and password has been correct.
My class Auth look like that:
<?php
require "DBController.class.php";
class Auth {
protected $db;
protected $data;
public function __construct()
{
$this->db = DBController::instance();
}
function getMemberByUsername($username)
{
$this->data = $this->db->run("Select * from `members` where `member_name` = ?", array($username))->fetchAll(0);
}
function getTokenByUsername($username,$expired)
{
$this->data = $this->db->run("Select * from `tbl_token_auth` where `username` = ? and `is_expired` = ?", [$username, $expired])->fetchAll();
}
function markAsExpired($tokenId)
{
$expired = 1;
$this->data = $this->db->run("UPDATE `tbl_token_auth` SET `is_expired` = ? WHERE id = ?", [$expired, $tokenId])->fetchAll();
}
function insertToken($username, $random_password_hash, $random_selector_hash, $expiry_date)
{
$this->data = $this->db->run("INSERT INTO `tbl_token_auth` (username, password_hash, selector_hash, expiry_date) values (?, ?, ?,?)", [$username, $random_password_hash, $random_selector_hash, $expiry_date])->fetchAll();
}
}
Below the code, which works with function getMemberByUsername
if (! empty($_POST["login"])) {
$isAuthenticated = false;
$username = $_POST["member_name"];
$password = $_POST["member_password"];
$user = $auth->getMemberByUsername($username);
if (password_verify($password, $user[0]["member_password"])) {
$isAuthenticated = true;
}
What I did wrong?
I have created a database class which is throwing an error of
Undefined variable: options in C:path/ on line 21
and I cant seem to figure out why.
class Db
{
private static $instance = null;
private $options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
private function __construct() {}
private function __clone() {}
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new PDO('mysql:host=localhost;dbname=mydb', 'root', '', $options);
}
return self::$instance;
}
}
Your $options doesn't exist. You should reference the defined one as $this->options, but that's not a solution for you as you are using it in a static function: the $this context is not available.
You could just define your options in that static method I guess, like below. Not the prettiest solution as now your settings are hidden in your creation-method, but as an example:
class Db
{
private static $instance = null;
private function __construct() {}
private function __clone() {}
public static function getInstance()
{
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
if (!isset(self::$instance)) {
self::$instance = new PDO('mysql:host=localhost;dbname=mydb', 'root', '', $options);
}
return self::$instance;
}
}
The error by the way is quite clear: you are using a variable $options that doesn't exist at that point so you get an error that says it's not defined.
I receive this error whenever I try to fetch something from my database using my PDO database wrapper:
Fatal error: Call to a member function fetch() on boolean in C:\xampp\htdocs\index.php on line 17
index.php
require_once 'class.db.php';
$mysql = [
'hostname' => '127.0.0.1',
'database' => 'lucidcms',
'username' => 'root',
'password' => 'test',
'charset' => 'utf8',
];
$db = new Database($mysql);
$u = "Sentinel";
$result = $db->prepare("SELECT `id` FROM `users` WHERE `username` = :u LIMIT 1")
->execute([':u' => $u])
->fetch();
echo $result;
class.db.php
<?php
class Database {
private static $stmt, $pdo;
public function __construct(array $mysql) {
if(is_null(self::$pdo)) {
try {
self::$pdo = new \PDO(
'mysql:dbname='.$mysql['database'].';host='.$mysql['hostname'].';charset='.$mysql['charset'],
$mysql['username'],
$mysql['password'],
[\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_PERSISTENT => true]
);
} catch(PDOException $e) {
die('An error occurred when trying to communicate with the database.');
//die($e->getMessage());
}
} else {
return new self;
}
}
public function __destruct() {
// Destruct the connection to DB once finished
try {
self::$pdo = null;
} catch(PDOException $e) {
die('An error occurred when trying to close the connection with the database.');
//die($e->getMessage());
}
}
public static function prepare($sql) {
self::$stmt = self::$pdo->prepare($sql);
return self::$stmt;
}
public static function execute($data = []) {
return self::$stmt->execute(isset($data) ? $data : null);
}
public static function count() {
return self::$stmt->rowCount();
}
public static function fetch() {
return self::$stmt->fetchColumn();
}
public static function fetchAll($type = \PDO::FETCH_ASSOC) {
return self::$stmt->fetch($type);
}
public static function lastId() {
return self::$pdo->lastInsertId();
}
}
I can't really seem to find out how to fix this error.
Any ideas of what I've done wrong?
EDIT, new working solution, but the coding is pretty messy:
<?php
class DB {
private static $host;
private static $db;
private static $dbuser;
private static $pass;
private static $char;
private static $pdo, $stmt;
public function data($mysql) {
self::$host = $mysql['hostname'];
self::$db = $mysql['database'];
self::$dbuser = $mysql['username'];
self::$pass = $mysql['password'];
self::$char = $mysql['charset'];
}
protected function __clone() {}
public static function connect() {
if(is_null(self::$pdo)) {
try {
self::$pdo = new \PDO(sprintf("mysql:host=%s;dbname=%s;",
self::$host,
self::$db),
self::$dbuser,
self::$pass,
[
\PDO::ATTR_EMULATE_PREPARES => false,
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
]
);
} catch(PDOException $e) {
die('An error occurred when trying to communicate with the database.');
}
} else {
return self::$pdo;
}
}
public function __destruct() {
// Destruct the connection to DB once finished
try {
self::$pdo = null;
} catch(PDOException $e) {
die('An error occurred when trying to close the connection with the database.');
//die($e->getMessage());
}
}
public static function prepare($sql) {
DB::connect();
self::$stmt = self::$pdo->prepare($sql);
return new self;
}
public static function execute($data = []) {
DB::connect();
try {
self::$stmt->execute(isset($data) ? $data : null);
} catch(PDOException $e) {
die("Execution error: " . $e->getMessage());
}
return new self;
}
public static function fetchAll($type = \PDO::FETCH_BOTH) {
DB::connect();
return self::$stmt->fetch($type);
}
public static function fetch() {
DB::connect();
return self::$stmt->fetchColumn();
}
}
Any way to make the connect() and data() into a __construct() function?
There are many things you did wrong, (completely changed method names alone!) but the error message you get is clear: if you want to use method chaining, you should write every method just like prepare is written() - to make it return the statement.
However, this class couldn't be salvaged, as it will lead to nasty errors in production, due to its statefulness. Instead of this troublesome mess, let me suggest another solution, more lightweight and less error prone
class MyPDO extends PDO
{
public function run($sql, $args = NULL)
{
$stmt = $this->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
with this simple extension you will be able to use method chaining with your SELECT queries without the risk of messing your code up.
$result = $db->run("SELECT `id` FROM `users` WHERE `username` = :u LIMIT 1", [':u' => $u])
->fetch();
it is not that neat as your current approach but again - it doesn't bear its terrible drawbacks.