I am running an insert query using PDO and then getting the newly created Id with lastInsertId(). This is all working on my localhost environment.
When I move the exact same code onto a server, the lastInsertId() is always returning blank, even though the insert statement works and inserts the new row into the database. Would this be a setting in my configuration? Any help is appreciated.
$insertstmt = $dbinsert->prepare($insertsql);
// bindParams ...
$insertstmt->execute();
// always blank
$id = $dbinsert->lastInsertId();
$dbinsert = null;
I ended up replacing lastInsertId() by using this method: http://php.net/manual/en/pdo.lastinsertid.php#105580
$temp = $sth->fetch(PDO::FETCH_ASSOC);
I've describe a solution for this problem on my blog. I couldn't directly modify sql queries in the code, so I extended the PDO class in this way
class BetterPDO extends \PDO
{
protected $stmt;
protected $withIdentitySelect;
public function prepare($statement, $driver_options = null)
{
$this->$withIdentitySelect = false;
if (preg_match('/^insert/i', $statement)) {
$statement = "{$statement}; select SCOPE_IDENTITY() AS 'Identity';";
$this->$withIdentitySelect = true;
}
$this->stmt = parent::prepare($statement, is_array($driver_options) ? $driver_options : []);
return $this->stmt;
}
public function query($statement)
{
$this->$withIdentitySelect = false;
if (preg_match('/^insert/i', $statement)) {
$statement = "{$statement}; select SCOPE_IDENTITY() AS 'Identity';";
$this->$withIdentitySelect = true;
}
$this->stmt = parent::query($statement);
return $this->stmt;
}
public function lastInsertId($name = null)
{
$lastIndex = 0;
if (($this->$withIdentitySelect) && ($this->stmt->columnCount() > 0)) {
$lastIndex = $this->stmt->fetchColumn(0);
$this->stmt->closeCursor();
}
return $lastIndex;
}
}
Related
Is it possible to get the full structure of a mySQL database in json or xml format?
what I'm looking for is a way to get the schema (stored data doesn't matter) to use it to create a backend/crud for the application.
This is not a fully comprehensive answer to the question, as it doesn't return a json. But it's a simple class that gives all relevant info about a table as an object. This could easily be transformed to return a json.
The class relies on having passed a db object that implements a rawQuery method, as PHP-Mysqli-Database-Class does. But you could easily rewrite the method extractTableInfo if you need.
<?php
Class dbHelper {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function extractTableInfo($tableName) {
$result = $this->db->rawQuery("DESCRIBE `".$tableName."`");
$tableInfo = array();
foreach ($result as $key => $value) {
$info = new \stdClass();
$info->name = $value['Field'];
$info->type = $this->_getColumnType($value['Type']);
$info->length = $this->_getLength($value['Type']);
$info->hasNull = $this->_getNull($value['Null']);
$info->default = $this->_getDefault($value['Default']);
$tableInfo[] = $info;
}
return $tableInfo;
}
public function getIndexes($tableName) {
$result = $this->db->rawQuery("SHOW INDEX FROM `".$tableName."`");
return $result;
}
private function _getLength($info) {
$pattern = '/\({1}([\d\W]*)\){1}/';
preg_match($pattern, $info, $matches);
return isset($matches[1]) ? $matches[1] : NULL;
}
private function _getColumnType($info) {
$pattern = '/([\w]*)(\([\d\W]*\))*/';
preg_match($pattern, $info, $matches);
return isset($matches[1]) ? $matches[1] : NULL;
}
private function _getNull($info) {
if($info==='NO') {
return false;
} else {
return true;
}
}
private function _getDefault($info) {
if($info>='') {
return $info;
} else {
return NULL;
}
}
}
Usage:
$db = YOUR DATABASE OBJECT/CLASS;
$dbHelper = new dbHelper($db);
$tableName = "test";
$tableInfo = $dbHelper->extractTableInfo($tableName);
echo json_encode($tableInfo);
To get a list of all tables in a database you'll need:
SHOW TABLES [in dbname]
or follow this question
Still, there are many alternatives with different benefits/outputs:
using mysql workbench
using phpmyadmin
using command line mysqldump -u root -p --no-data dbname > schema.sql
In my code I use methods to insert data to MySQL database. The method then return either true or false.
If the method return true I would like to use lastInsertId() to run a second method.
Like
if($db->insertMethod($data)){
$lastId = $db->lastInsertId();
$db->secondInsertMethod($lastId, $data2)
}
db.class.php
public function insertMethod($data) {
$db = new DB();
$insert = //Run SQL insert
if($insert > 0 ) {
return true;
}else{
return false;
}
}
public function lastInsertId() {
return $this->pdo->lastInsertId();
}
In this case $db->lastInsertId(); will return 0. One workaround would be to have insertMethod to return this instead of just true but there must another way as well?
Edited!
the solution is correct
The data2 is linked to the data ?
you can also make :
public function insertMethod($data, $data2) {
$db = new DB();
$insert = //Run SQL insert
if($insert > 0 ) {
$this->insertMethod(this->lastInsertId(), $data2);
return true;
} else {
return false;
}
}
or if data2 is linked to the data
public function insertMethod($data) {
$db = new DB();
$insert = //Run SQL insert
if($insert > 0 ) {
$data2 = // get data2
$this->insertMethod(this->lastInsertId(), $data2);
return true;
} else {
return false;
}
}
public function insertMethod($id, $data) {
// your code...
}
or
public function insertMethod($data, $id = null) {
$db = new DB();
if($id == null)
// code to insert without id
else {
$insert = //Run SQL insert
if($insert > 0 ) {
$data2 = // get data2
$this->insertMethod($data2, this->lastInsertId());
}
}
}
The solutions are correct. For you to see who you think is the most appropriate in your situation.
The Situation
I'm fairly new to object-oriented programming in PHP and currently I'm creating a small CMS for learning purposes. I've learned a lot about OOP on my way, but I'm facing a weird issue at the moment. I've created a Singleton class to deal with the database connection and queries.
public static function getInstance()
{
if(!isset(self::$instance))
{
self::$instance = new Database();
}
return self::$instance;
}
In the same class, there also is a method to execute queries. It takes two parameters, the query and an optional array with parameters to bind for the prepared statements. You can see its source below.
public function execute($query, $params = array())
{
$this->error = false; // Set 'error' to false at the start of each query.
if($this->query = $this->pdo->prepare($query))
{
if(!empty($params))
{
$index = 1;
foreach($params as $parameter)
{
$this->query->bindValue($index, $parameter);
++$index;
}
}
if($this->query->execute())
{
$this->results = $this->query->fetchAll(PDO::FETCH_OBJ);
$this->count = $this->query->rowCount();
}
else
{
$this->error = true;
}
}
return $this;
}
The Problem
If I have multiple queries on the same page, the results and count variables still contain the values of the first query. Imagine the following - the first query retrieves all users from my database. Let's say there are 15. The second query retrieves all blog posts from the database, let's say there are none. If no posts are present, I want to display a message, otherwise I run a loop to display all results. In this case, the loop is executed even though there are no blog posts, because the count variable is used to determine if there are posts in the database and it still holds the 15 from the first query somehow.
This obviously leads to some errors. Same with results. It still holds the value from the first query.
$query = Database::getInstance()->execute('SELECT * FROM articles ORDER BY article_id DESC');
if(!$query->countRes())
{
echo '<h2>There are no blog posts in the database.</h2>';
}
else
{
foreach($query->results() as $query)
{
echo '<article>
<h3>'.$query->article_heading.'</h3>
<p>'.$query->article_preview.'</p>
</article>';
}
}
The countRes() and results() methods simply return the variables from the DB class.
I hope that I have explained the problem understandable. Responses are very appreciated.
I would use a response object to avoid attaching query specific data to the global database object.
Example:
<?php
class PDO_Response {
protected $count;
protected $results;
protected $query;
protected $error;
protected $success = true;
public function __construct($query){
$this->query = $query;
try{
$this->query->execute();
}catch(PDOException $e){
$this->error = $e;
$this->success = false;
}
return $this;
}
public function getCount(){
if( is_null( $this->count ) ){
$this->count = $this->query->rowCount();
}
return $this->count;
}
public function getResults(){
if( is_null( $this->results ) ){
$this->results = $this->query->fetchAll(PDO::FETCH_OBJ);
}
return $this->results;
}
public function success(){
return $this->success;
}
public function getError(){
return $this->error;
}
}
Then in your database class:
public function execute($query, $params = array())
{
if($this -> _query = $this -> _pdo -> prepare($query))
{
if(!empty($params))
{
$index = 1;
foreach($params as $parameter)
{
$this -> _query -> bindValue($index, $parameter);
++$index;
}
}
return new PDO_Response($this->_query);
}
throw new Exception('Some error text here');
}
UPDATE: Moved execution into response class for error handling
Example usage (not tested)
$select = $database->execute('SELECT * FROM table');
if( $select->success() ){
//query succeeded - do what you want with the response
//SELECT
$results = $select->getResults();
}
$update = $database->execute('UPDATE table SET col = "value"');
if( $update->success() ){
//cool, the update worked
}
This will help fix your issue in the event that subsequent queries fail, there will not be any old query data attached to the database object.
I have a DB class that I've created several functions in to return various values. One of the functions returns (or is supposed to) a "user" class object that represents a logged in user for the application.
class user {
public $guid = '';
public $fname = '';
public $lname = '';
public function __construct() {}
public function print_option() {
return "<option value='$this->guid'>$this->name</option>";
}
}
In the DB class I have the following 2 functions:
public function get_user($uid) {
$sql = '';
$usr = new user();
$sql = "SELECT guid, fname, lname FROM ms.users WHERE guid=?";
if($sth = $this->conn->prepare($sql)) {
$sth->bind_param('s', $uid);
if($sth->execute()) {
$sth->bind_result($usr->guid, $usr->fname, $usr->lname);
$sth->fetch();
print_r($usr); // PRINTS OUT CORRECTLY
return $usr;
}
else {return null;}
}
else {return null;}
}
public function get_practice_user_list($pguid) {
$ret = '';
$sql = "SELECT user_guid FROM ms.perm WHERE p_guid=?";
if($sth = $this->conn->prepare($sql)) {
$sth->bind_param('s', $pguid);
if($sth->execute()) {
$usr = new user();
$guid = '';
$sth->bind_result($guid);
while($sth->fetch()) {
print_r($guid); // PRINTS GUID correctly
$usr = $this->get_user($guid);
print_r($usr); // PRINTS NOTHING object is null so prints "error" two lines later.
if($usr != null) $ret .= $usr->print_option();
else print "error";
}
return $ret;
}
else {return null;}
}
else {return null;}
}
I'm just not understanding why the "user" object is not returning in this instance. Others calls to the get_user function work just fine and return the user class object pertaining to that user.
TIA
I guess you guid may be an integer so
$sth->bind_param('s', $uid);
bind_param's first param should be 'i' not 's';
http://www.php.net/manual/en/mysqli-stmt.bind-param.php
The problem was with the query. Since the code was just looping through one query (get_practice_user_list), then calling the get_user function and attempting a second query MySQL came back with an error of out of sync message. When I looked that up, I was able to fix it by doing a fetch_all on the first query then looping through that array to get the users.
I am trying to make a 1 on 1 chat website by learning as I progress, but I've come to a hault.
I can't write to the database.
I have four php files linked below.
Index
Init:
session_start();
define('LOGGED_IN', true);
require 'classes/Core.php';
require 'classes/Chat.php';
?>
Chat
Core:
class Core {
protected $db, $result;
private $rows;
public function __construct() {
$this->db = new mysqli("localhost","root","");
}
public function query($sql) {
$this->result = $this->db->query($sql);
}
public function rows() {
for($x = 1; $x <= $this->db->affected_rows; $x++) {
$this->rows[] = $this->result->fetch_assoc();
}
return $this->rows;
}
}
?>
I have a MySql database set with WAMP.
P.S. Yes, I have opened the "< ? php"
but it doesn't get displayed here.
From what I have seen you do not select a default database. You must either give a default database in
$this->db = new mysqli("localhost","root","", "mydatabase");
or select one later with
$this->db->select_db("mydatabase");
You also don't check the return values of the mysql calls. For example, add
public function query($sql) {
$this->result = $this->db->query($sql);
if ($this->result === false) {
echo $this->db->error;
}
}
after your mysql statements, in order to see whether the statements succeed or fail.
For debugging purposes you can display the sql and corresponding result
public function query($sql) {
var_dump($sql);
$this->result = $this->db->query($sql);
var_dump($this->result);
echo $this->db->error;
}