Mysqli last insert id not work - php

I am trying to get last query insert id.but my code always return zero.
My Code
private function Cnn()
{
return mysqli_connect('localhost','root','root','db');
}
protected function MyCommandInsertId($sql)
{
if(mysqli_query($this->Cnn(),$sql))
{
return mysqli_insert_id($this->Cnn());
}
$this->err = mysqli_error($this->Cnn());
return false;
}
public function Insert()
{
$sql = "insert general_info(name,email,password)
values('".$this->ms($this->name)."','".$this->ms($this->email)."','".md5($this->password)."')";
//print''.$sql.'';
$last_insert_id=$this->MyCommandInsertId($sql);
}
here return mysqli_insert_id($this->Cnn()); always return zero

Please check the definition of mysqli_insert_id:-
The mysqli_insert_id() function returns the id (generated with AUTO_INCREMENT) used in the last query.
It means either column in your table that have AUTO_INCREMENT value is not exist.
Or
No insertion of data happen programmatically on your table in the curren program flow. Since no insert query is fired in your code, it returned Zero.
Note:- Just do an insert query first in your code and then check what is the output given by mysqli_insert_id. Then you can understand easily what i am trying to say.Thanks
A working example of my local is here:-
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
function Cnn()
{
$conn = mysqli_connect('localhost','root','','stack');
return $conn;
}
function MyCommandInsertId($sql)
{
$conn = Cnn();
if(mysqli_query($conn,$sql))
{
$lastid = mysqli_insert_id($conn);
return $lastid;
}else{
$err = mysqli_error($conn);
return $err;
}
}
function Insert()
{
$sql = "INSERT INTO bom(bom_description,product_id,finish_product,bom_quantity,UOM) values('Table cleaner','','','','')";
$st_insert_id = MyCommandInsertId($sql);
echo $st_insert_id;
}
Insert();
?>
Output:- http://prntscr.com/9u2iyv (inserted id) and http://prntscr.com/9u2j6i(table view after insertion)

According to
http://php.net/manual/en/mysqli.insert-id.php
There 2 possible problems
no previous query on the connection
the query did not update an AUTO_INCREMENT value
solutions:
Check there is an new record inserted in your table
Check that whether your field is AUTO_INCREMENT or not.
By CBroe, you are make a new connect.
So, it considers you did not insert the new record.
rewrite the function Cnn() and change it into a private variable.

In my case, my sqli was declared outside the current function scope
$conn = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
What I needed to do was to add global in front of my $conn
function want() {
///...... query stuff
global $conn;
$conn->insert_id; // Now works
}

Related

How to use lastInsertId in this case?

What I want is to get and store the last inserted ID, but I am not sure how. The scenario is, after a user add a record he/she will be redirected to a page that will show him/her of the summary of what he/she saved. But I am not sure how I can do that, to retrieved the recently added record.
I have a class which look like this record.php
<?php
class Record {
private $conn;
private $table_name_1 = "name_of_the_table_1";
private $table_name_2 = "name_of_the_table_2";
public $name;
public $age;
public function __construct($db){
$this->conn = $db;
}
function newRecord(){
$query = "INSERT INTO " . $this->table_name_1 . " SET name=:name;
INSERT INTO " . $this->table_name_2 . " SET age=:age";
$stmt = $this->conn->prepare($query);
$this->name=$this->name;
$this->age=$this->age;
$stmt->bindParam(':name', $this->name);
$stmt->bindParam(':age', $this->age);
if($stmt->execute()){
return true;
}else{
return false;
}
}
}
?>
now I have another php form that create and add record, the code is something like this add_record.php
<?php
inlude_once 'db.php'
inlude_once 'record.php'
$database = new Database();
$db = $database->getConnection();
$record = new Record($db);
?>
<?php
if($_POST) {
$record->name=$_POST['name'];
$record->age=$_POST['age'];
if(record->record()){
header("Location:{$home_url}view_recently_added_record.php?action=record_saved");
}
else{
echo "Unable to save";
}
}
?>
The idea is to save the query to two different table and the same time automatically record the auto increment ID of table 1 to table 2 so that they have a relationship. I am thinking I can do that if I can store immediately the ID from table 1 and assigned it a variable maybe so it can be automatically saved to table two using a new query or function maybe. Is this possible? does it make sense? and another thing I wanted to display the recently recorded data immediately to the user. Thank you.
You can return $stmt->insert_id or -1 insteaf of boolean in the newRecord function:
function newRecord(){
...
if($stmt->execute()){
return $stmt->insert_id;
}else{
return -1;
}
}
and use the value to redirect like this:
$newrecord = record->newRecord();
if($newrecord != -1) {
header("Location:{$home_url}view_recently_added_record.php?action=record_saved&id=".$newrecord);
}
else{
echo "Unable to save";
}

Php PDO, class method return true while the column name in the param is wrong?

I have created class having the following two methods:
Connection method is as following:
private function connect(){
try{
$this->con = new PDO("mysql:host={$this->host};dbname={$this->db_name};charset=utf8", $this->db_user, $this->db_pass);
$this->con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$this->con->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
}catch(PDOException $ex){
return $ex->getMessage();
}
}
While here is the method for insert,update,delete and search record in table:
public function dbQuery($sql,$bindVars=array()){
try{
$this->connect();
$statement = $this->con->prepare($sql);
$statement->execute($bindVars);
if($statement->rowCount() > 0){
return true;
}
else{
return false;
}
}catch(PDOException $exc){
return $exc->getMessage();
}
$this->con = null;
Now i am using this way to (for example) insert record to my table:
1- fill array that could be bind to dbQuery method;
$bindInputInsert = array(
':empSno'=>$empSno,
':fromCourt'=>$fromCourt,
':toCourt'=>$toCourt,
':transferDate'=>$transferDate,
':transferFromDate'=>$transferFromDate,
':causeOfTransfer'=>$causeOfTransfer,
':departmentDetails'=>$departmentDetails,
':orderNo'=>$orderNo
);
2- Now prepare an insert query like this:
$sqlInsert = "INSERT INTO empoffices (
empSno,
fromCourt,
toCourt,
transferDate,
transferFromDate,
causeOfTransfer,
departmentDetails,
orderNo
)
VALUES(
:empSno,
:fromCourt,
:toCourt,
:transferDate,
:transferFromDate,
:causeOfTransfer,
:departmentDetails,
:orderNo
)";
3- Send the query to dbQuery method and test record inserted or not?
if($db->dbQuery($sqlInsert,$bindInputInsert)){
echo($method->sucMsg("Info: - "," Record saved successfully"));
}
else {
echo($method->errorMsg("Error: - ","Record not saved"));
}//end else insert()
Problem i am facing with this:
The above method is working fine for me but when if i misspelled a column name in the query it does not inserted record to table (Which is correct), but still it returns true??? while it should return false?
What i am doing wrong i am not understanding, please help me out in this. Thanks in Advance.
Your method dbQuery only returns false if a valid query results in a rowCount() <= 0. When you send an invalid query, such as one with a misspelled column name, it is returning an exception, specifically $exc->getMessage(); which is not false.
Try something like this instead:
if ($db->dbQuery($sqlInsert,$bindInputInsert) === true) {
// success
} ...
This will evaluate as true if and only if dbQuery returns exactly true - not false or $exc->getMessage();.

The lifecycle of an object in PHP

I have two PHP scripts that I included below. Both of them attempt to do the same thing, but one works and one does not. I'm looking for someone to explain what PHP is doing under the covers. I'm new to PHP and I suspect that my Java experience is poisoning my thought process when I work in PHP.
What I'm attempting to do is functionally very simple -- Insert a question into a mySQL database table, retrieve the primary key of the inserted row, and then insert five answers into another table with a foreign key relationship to the question.
My original logic looked like this:
ManageQuestions.php:
<?php
session_start();
include('query.php');
echo "begin <br>";
if (isset($_POST['submit'])) {
echo "manageQuestion <br>";
$query = new Query;
$query->createTransaction();
$query->executeCreateUpdateDelete("INSERT INTO question (question) VALUES ('".$_POST['question']."'); ");
$question_pid = $query->getLastInsertedId();
$query->commitTransaction(); // Need to figure out how to do dirty reads so I can remove this.
echo $question_pid."<br>";
$result = $query->executeRead("SELECT question_pid FROM question where question_pid = '".$question_pid."';");
echo count($result)."<br>";
//if (count($result) === 1) {
$query->createTransaction(); // Need to figure out how to do dirty reads so I can remove this.
foreach($_POST['answer'] as $answer) {
$correctAnswers = 0;
$query->executeCreateUpdateDelete("INSERT INTO answer (question_fid, answer, isCorrect) VALUES ('".$question_pid."','".$answer['answer']."','".$answer['isCorrect']."')");
if ($answer['isCorrect'] === 1) {
$correctAnswers = $correctAnaswers + 1;
if ($correctAnswers > 1){
echo "Failed to insert answers";
$query->rollBackTransaction();
break;
}
}
}
echo "Success";
$query->commitTransaction();
/* } else {
echo "Failed to insert question";
$query->rollBackTransaction();
} */
}
?>
Query.php:
<?php
session_start();
class Query
{
private $host="<censored>";
private $username="<censored>";
private $password="<censored>";
private $db_name="<censored>";
private $pdo;
private $pdo_statement;
private $pdo_exception;
public function executeCreateUpdateDelete($pQuery)
{
$this->pdo_statement = $this->pdo->prepare($pQuery);
return $this->pdo_statement->execute();
}
public function executeRead($pQuery)
{
try
{
$dbh = new PDO("mysql:host=$this->host;dbname=$this->db_name", $this->username, $this->password);
$result = $dbh->query($pQuery);
$dbh = null;
return $result->fetchAll();
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
public function createTransaction()
{
$this->pdo = new PDO("mysql:host=$this->host;dbname=$this->db_name", $this->username, $this->password);
$this->pdo->beginTransaction();
}
public function commitTransaction()
{
$this->pdo->commit();
}
public function rollBackTransaction()
{
$this->pdo->rollBack();
}
public function getLastInsertedId()
{
$this->pdo->lastInsertId();
}
}
?>
When I rewrote my logic to not use a separate query class, I was able to do what I wanted to do. The only thing I've been able to find online about the life cycle of a PHP object is that it begins at the start of a script and ends at the end of a script. Does that imply that my query object is instantiated every time I call one of its methods and garbage collected when that particular method ends? Moving the logic out of that class and into the script caused my logic to work. This is what it looks like now:
ManageQuestions.php:
<?php
session_start();
include('query.php');
echo "Begin <br>";
if (isset($_POST['submit'])) {
echo "manageQuestion <br>";
$host="<censored>";
$username="<censored>";
$password="<censored>";
$db_name="<censored>";
$pdo = new PDO("mysql:host=$host;dbname=$db_name", $username, $password);
$stmt = $pdo->prepare("INSERT INTO question (question) VALUES ('".$_POST['question']."'); ");
$stmt->execute();
$question_pid = $pdo->lastInsertId();
echo $question_pid."<br>";
$stmt = $pdo->query("SELECT question_pid FROM question where question_pid = '".$question_pid."';");
$result = $stmt->fetchAll();
echo count($result)."<br>";
foreach($_POST['answer'] as $answer) {
$correctAnswers = 0;
$stmt = $pdo->prepare("INSERT INTO answer (question_fid, answer, isCorrect) VALUES ('".$question_pid."','".$answer['answer']."','".$answer['isCorrect']."')");
$stmt->execute();
}
echo "Success";
}
?>
Even though this fixed my issue, I don't understand why. If someone could explain that, I would be extremely grateful.
Cheers!
Does that imply that my query object is instantiated every time I call one of its methods and garbage collected when that particular method ends?
No. It's per request, not per method call. So the query object is instantiated every time the script is called and it gets unset (and not necessarily garbage collected) when the script ends.
However you could better manage the resource of the PDO object inside your Query class because you create a new instance (which would mean that it connects again to the database server which is not that cheap). So some lazy loading does not seem bad:
class Query
{
...
/** #var PDO */
private $pdo;
...
private function getPdo() {
if (!$this->pdo) {
$this->pdo = new PDO("mysql:host=$this->host;dbname=$this->db_name", $this->username, $this->password);
}
return $this->pdo;
}
public function executeRead($pQuery)
{
try {
$dbh = $this->getPdo();
$result = $dbh->query($pQuery);
return $result->fetchAll();
} catch (PDOException $e) {
echo $e->getMessage();
}
}
public function createTransaction()
{
$this->getPdo()->beginTransaction();
}
...

Created class will not run query

I'm having issues running this code here:
<?php
include_once "global.php";
class Project{
//Variable declarations
public $name;
public $pm;
public $members;
public $details;
//Not sure if dates are necessary yet
//$startDate = $inStartDate;
//$endDate = $inEndDate;
//Define the constructor
function __construct($inName, $inPm, $inMembers, $inDetails){
$this->name = $inName;
$this->pm = $inPm;
$this->members = $inMembers;
$this->details = $inDetails;
//Not sure if dates are necessary yet
//$startDate = $inStartDate;
//$endDate = $inEndDate;
}
//Function to create the Project within the database
public function createProject(){
echo "starting";
//Initiate database connection, checking for errors
$con = dbConnect();
if ($con->connect_errno){
echo "Connect failed: %s\n". $mysqli->connect_error;
exit();
}
//Insert the project into the projects database using the information used to instantiate
if ($stmt = $con->prepare("INSERT INTO projects (name, details) VALUES (?,?)")){
echo "Not getting here.";
$stmt->bind_param('ss', $this->name, $this->details);
$stmt->execute();
if (strlen($stmt->error) > 0){
return "Error creating project. Contact system administrator.";
}
elseif(count($members) == 0) {
//ensure there are members to enter
$last_insert = mysqli_insert_id($con);
$con->query('INSERT INTO project_member (pid,uid,rid) VALUES ('.$last_insert.','.$this->pm.',1');
exit();
}
else{
//get last inserted ID
$last_insert = mysqli_insert_id($con);
echo $last_insert;
//loop through members and create sql array
$sql = array();
foreach ($members as $user){
$sql[]='("'.$last_insert.','.$user.',0")';
}
//Add members to join table with roles
$con->query('INSERT INTO project_member (pid,uid,rid) VALUES '.implode(',', $sql));
$con->query('INSERT INTO project_member (pid,uid,rid) VALUES ('.$last_insert.','.$this->pm.',1');
return true;
}
$stmt->close();
}
}
}
It will get to the mysqli code loop, but not initiate it. Trying to echo from it results in nothing being submitted, and the PHP error logs don't note anything going on. I don't see anything strange on the MySQL logs as well.
The $members variable is going to be an array composed of integers, which is why it loops at the bottom through the array.
I am not getting any connection errors, and echoing after trying to capture errors results in success.
dbConnect() is from the global.php file above, and returns a mysqli connection. This is the inside of the connection:
function dbConnect(){
$con = mysqli_connect($_HOST, $_DBUNAME, $_DBPASS,$_DBTABLE);
if ($con->connect_errno){
printf("Connect failed: %s\n", $mysqli->connect_error);
return false;
}
return $con;
}
I have used that elsewhere on my site, and in much of the same situation. Not sure what I am doing wrong here, but any advice would be greatly appreciated.
Edit: I did try again with all of the above suggestions, and still getting the weird issue with it not wanting to go past the if loop.
Edit2: Never mind, I am a moron.
Issue was that the 'details' row in my database didn't exist. Instead, in my infinite wisdom, I called it description and didn't update the code. Changing it fixed the issue.
Again, thank you all for a great first post on StackOverflow. I am going to be back, but I will get better!
This loop:
foreach ($members as $user){
$sql='("'.$last_insert.','.$user.',0")';
}
should be:
foreach ($members as $user){
$sql[]='("'.$last_insert.','.$user.',0")';
}
You're just overwriting the variable with a string, not appending to the array.
I'm surprise implode(',', $sql) on the next line isn't reporting an error.
You initialize $sql as an array, but then assign a string to it; you need to add items to an array differently:
$sql = array();
foreach ($members as $user){
$sql[] ='("'.$last_insert.','.$user.',0")';
}
Note the square brackets - that adds an extra item to the $sql array.
I'd also look at the two queries you're running at the end:
$con->query('INSERT INTO project_member (pid,uid,rid) VALUES '.implode(',', $sql));
$con->query('INSERT INTO project_member (pid,uid,rid) VALUES ('.$last_insert.','.$pm.',1');
You're adding two sets of values to the same table - $pm is never defined. You've got $pm as a class variable, but you don't set it as such. The syntax for that would be:
function __construct($inName, $inPm, $inMembers, $inDetails){
$this->name = $inName;
$this->pm = $inPm;
$this->members = $inMembers;
$this->details = $inDetails;
That means that the rest of the class functions can access those variables as $this->name, and so on.
The problem starts at your Project constructor:
The way you're initializing $name, $pm, $members, $details is keeping them local to the constructor. You want to initialize these variables using $this.
//Define the constructor
function __construct($inName, $inPm, $inMembers, $inDetails){
$this->name = $inName;
$this->pm = $inPm;
$this->members = $inMembers;
$this->details = $inDetails;
}
You can then safely get rid of all the global variables defined within functions, some of them including:
global $name;
global $details;
global $members;
Finally you need to access the member variables name, pm, members, details and any others using:
$this->name, $this->pm, and so on
Lastly as other answerers have already pointed out on the SQL string generation.
$sql = array();
foreach ($members as $user){
$sql[] = '("'.$last_insert.','.$user.',0")';
}

PHP/MySQL isolating database access in a class - how to handle multiple row result set in an OOP manner

PHP/MySQLisolating database access in class - how to handle multiple row Selects
Here’s a coding question.
I isolated all DB access functions in a class
<?php
class DB {
var $conn;
function DBClass () {
#$this-> conn = mysqli_connect (DB_SERVER, DB_USER, DB_PASS, DB_NAME);
}
function validateUser ($aUserid, $aPassword) {
… validation code – sql injection code etc..
$sql = "Select userid, name, level From users where userid = '$aUserid' and password = '$aPassword'";
$result = mysqli_query ( $this->conn, $sql );
if (!$result || (mysqli_num_rows ($result) < 1)) {
return false;
}
$dbarray = mysqli_fetch_assoc ($result); // get a row
return $dbarray;
}
function getProduct ($aProductid) {
return $dbarray;
}
function getProductList () {
// <----------- this would be the problem function
}
}
$DB = new DBClass();
?>
My calling routine:
<?php
$dbarray = $DB->validateUser ($_POST['userid'], $_POST['password']);
?>
No problem it works fine. I run into a problem with a result set of more than one row. Now I have to get back to the class object for each row. It’s no problem if I include the MySQL code in the calling routine, but I’d like to keep it isolated in my class and I’m not sure how to code it.
Any thoughts? Any examples?
If you use PHP 5.3.0 and mysqlnd, you can use the new function mysqli_fetch_all(). This returns an array of associative arrays.
If you use an earlier version of PHP, you could switch to using PDO, and use the function PDOStatement::fetchAll().
You ask in a comment what about a very large result set. It's true that an unbounded result set could cause the array to exceed your PHP memory limit and that would cause a fatal error and halt the script. But is this really a problem? How many products do you have? You could use LIMIT to make sure the query isn't unbounded.
Re the other part of your questions regarding going back to a class, I'd suggest making an Iterator class:
class DB implements IteratorAggregate
{
protected $_data = array();
public function getProductList() {
// fetch all results from SQL query, stuff them into $this->_data
return $this->getIterator();
}
public function getIterator() {
return new ArrayIterator($this->_data);
}
}
Now you can use the class in a foreach loop:
$db = new DB();
foreach ($db->getProductList() as $product) {
// do something with each product
}
The IteratorAggregate interface means you can even do this:
$db = new DB();
$db->getProductList();
// ...other steps...
foreach ($db as $product) {
// do something with each product
}
Of course you could only store one result set at a time with this method. If you used your DB class for any other queries in the meantime, it would complicate things. For this reason, most people don't try to write a single class to encapsulate all database operations. They write individual classes for each type of Domain Model they need to work with, decoupled from the database connection.
you could save the result in an array and return it:
function getProductList () {
$sql = "SELECT ...";
$result = mysqli_query ( $this->conn, $sql );
$myProducts = array();
while ($row = mysqli_fetch_assoc($result))
$myProducts[] = $row; // or array_push($myProducts, $row)
}
return $myProducts
}
As a result you'll have an array of arrays and each element of it will contain one row of the result.
You have a SQL injection right in your login page.
What happens if someone inputs that as password:
xxx' OR 'yyy' <> 'x

Categories