Data is not being inserted into second table (MYSQLi) - php

I am using the code below that uploads a file and inserts data into the "Image" table using mysqli:
<?php
session_start();
$username="xxx";
$password="xxx";
$database="mobile_app";
$mysqli = new mysqli("localhost", $username, $password, $database);
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
die();
}
$result = 0;
//UPLOAD IMAGE FILE
move_uploaded_file($_FILES["fileImage"]["tmp_name"], "ImageFiles/" . $_FILES["fileImage"]["name"]);
$result = 1;
//INSERT INTO IMAGE DATABASE TABLE
$imagesql = "INSERT INTO Image (ImageFile) VALUES (?)";
if (!$insert = $mysqli->prepare($imagesql)) {
// Handle errors with prepare operation here
}
//Dont pass data directly to bind_param store it in a variable
$insert->bind_param("s", $img);
//Assign the variable
$img = 'ImageFiles/' . $_FILES['fileImage']['name'];
$insert->execute();
//RETRIEVE IMAGEID FROM IMAGE TABLE
$lastID = $mysqli->insert_id;
//INSERT INTO IMAGE_QUESTION DATABASE TABLE
$imagequestionsql = "INSERT INTO Image_Question (ImageId, SessionId, QuestionId) VALUES (?, ?, ?)";
if (!$insertimagequestion = $mysqli->prepare($imagequestionsql)) {
// Handle errors with prepare operation here
}
$sessid = $_SESSION['id'] . ($_SESSION['initial_count'] > 1 ? $_SESSION['sessionCount'] : '');
$insertimagequestion->bind_param("sss", $lastID, $sessid, $_POST['numQuestion'][$i]);
$insertimagequestion->execute();
//IF ANY ERROR WHILE INSERTING DATA INTO EITHER OF THE TABLES
if ($insert->errno) {
// Handle query error here
}
$insert->close();
if ($insertimagequestion->errno) {
// Handle query error here
}
$insertimagequestion->close();
}
}
?>
So for example if I insert 2 images "cat.png" and "dog.png" into "Image" Database table, it will insert it like this:
ImageId ImageFile
220 cat.png
221 dog.png
(ImageId is an auto increment)
Anyway what I want to do is that when a file is uploaded, not only is the data inserted into the table above, but I want to also be able to retrieve the ImageId that was inserted above and place it in the "Image_Question" table below so it would be like this:
ImageId SessionId QuestionId
220 cat.png 1
221 dog.png 4
The problem is that it is not inserting any data into the second table "Image_Question", does anyone know why it is not inserting any data? There is no errors in the php file.
To upload a file, the user selects a file for the ajax uploader in the "QandATable.php" page, when the user clicks on upload, using AJAX it will go onto the imageupload.php page and does the uploading there. So the problem I have is that no errors will appear as they are on seperate pages.

First, save the insert ID gained from your record addition (after the $insert->execute):
$lastID = $mysqli->insert_id;
Then reference $lastID later.
To pull up my comment from below:
$lastID = $insert->insert_id;
I think it's to do with swapping the handle names around - $mysqli, $insert etc.
Hope I read the question correctly...

Check for 500 Error responses in Firebug -> Net tab/Chrome Developer tools -> Network tab . Even if nothing is returned as text, this will help you debug a syntax/semantic error as opposed to a logical error.

Firstly, what happens when you echo $lastID? Do you get a value output to the screen?
If not, we need to fix that first so that $lastID is returning the correct value.
Your insert code appears to be correct.

You should get the Last inserted ID from first table and insert into your 2nd table (Image_Question) .
I Don't know the PHP coding, but this task is simple as well.Because this operation will be executed inside DAO class.So, No matter whether it is PHP or JAVA.

If the second insertion fails, then
if ($insertimagequestion->error) {
// Handle query error here
echo $insertimagequestion->error;
}
This should tell you what the Error being thrown from the execution of the statement is.
Your PHP code seems fine, the error could be due to a Foreign key constraints or any other constraints on your DB Tables.
PS: I think you should validate the type of files you allow to be uploaded so people can't upload *.php or *.js files, this can lead to catastrophic XSS attacks.
Also try to avoid using the same filename as uploaded by the user, you may want to prefix with some random variable, so you can now have
//notice uniqid(time()) for randomness, also move the declaration of $img higher
//Assign the variable
$img = "ImageFiles/" . uniqid(time()) . $_FILES["fileImage"]["name"];
move_uploaded_file($_FILES["fileImage"]["tmp_name"], $img);
...
//Dont pass data directly to bind_param store it in a variable
$insert->bind_param("s", $img);

Bind with mysqli works with references to variables. I dont think your last argument in the second bind command references the way you expect it to.
Assign the the last argument $_POST['numQuestion'][$i] to a variable and use this variable in the bind method call. I am guessing this is either not defined, evaluating to null, and the bind is failing since you can't bind a null as a string or bind cannot use a multidimensional array since itexpects a variable passed as reference.
Try this:
//Below will set a default value of empty string if the POST variable is not set
$postVar = isset($_POST['numQuestion'][$i])?$_POST['numQuestion'][$i]:'';
$insertimagequestion->bind_param("sss", $lastID, $sessid, $postVar);
After doing this, if you see entries in the DB with a '' in the QuestionId column, $_POST['numQuestion'][$i] isn't being set and you have something wrong elsewhere in your code having nothing to do with DB access.

Tried to figure out where could be the failure.
There is no problem with second query and you get successfully last insert id. I used static values for the variables for second query it worked fine. Even you can hardcode values n check out.
Take care of the foll:
Does bind params get the all the values?
print_r() $lastID, $sessid, $_POST['numQuestion'][$i]
This Will not create problem unless database has contraints of not accepting empty or null values.
Make use of the check condition to find where its going wrong.
if (!$insertimagequestion = $mysqli->prepare("$imagequestionsql")) {
// Handle errors with prepare operation here
echo "Prepare statement err";
}
if ($insert->errno) {
// Handle query error here
echo "insert execution error";
}
Though its an ajax you can use Developer Tool of Chome to debug ajax requests.
Press F12 to open the Developer Tool in Chrome
Go to Network Tab >> Perform action for ajax requests to be sent on your form >> you can find the ajax requests sent >> click on it >> Click on the "Response" Tab you will find the error if you have
echoed or the response. So, echo error and print_r() to help debugging

Related

PHP pdo lastInsertId() always returns a 0

Here is the database and PHP information:
Database vendor and version : 10.2.32-MariaDB
PHP Version : PHP 7.3
I am running into an issue when trying to retrieve the last inserted id to use in another insert statement using PHP PDO and MariaDB...
Sorry for the vague pseudo-code below but trying to mask proprietary data:
try {
include_once $pdo connection stuff here;
$pdo->beginTransaction();
$sql = 'AN INSERT STATEMENT HERE';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':some_value', $some_value);
$stmt->bindValue(':another_one', $another_one);
$stmt->bindValue(':additional_value', $additional_value);
$stmt->execute();
// have tried to call $pdo->commit(): here to no avail.
//should get the last inserted id here on the AUTO_INCREMENT column in the target table from above prepared statement
// the AI column is not included in the insert statement above nor any value specified in the VALUES clause so should
// set to the next available value (and does so according to peeking at row over in phpMyAdmin).
$last_insert_id = $pdo->lastInsertId();
// don't really want to commit the above insert here just yet in case something goes wrong below and can rollback
// a file could be uploaded but it's not mandatory
if (!empty($_FILES['some_file'])) { // file has been attached.
// some file operations here
// some file operations here
// some file operations here
// some file operations here
$extensions = array("extension I am expecting");
if (in_array($file_ext, $extensions) === false) {
//Uh-oh not the correct extension so rolling back
$pdo->rollback();
die('message here...');
} else {
// file type is ok so proceeding
// if the file already exists, get rid of it so we don't have 2 copies on the server
if (file_exists($file_dir.$file_name)) {
unlink($file_dir.$file_name);
}
// storing the attached file in designated directory
move_uploaded_file($file_tmp, $file_dir.$file_name);
// going to parse the file...
$xml = simplexml_load_file('xml file to parse');
// have tried to call $pdo->commit(): here to no avail.
foreach ($xml->children() as $row) {
foreach ($row as $obj) {
if (some checking things with the obj here yada yada yada) {
$insert_sql = "INSERT INTO another table(columns.....) //there is no AUTO_INCREMENT column attribute on any column in this table just FYI
VALUES(column values...)";
$stmt = $pdo->prepare($insert_sql);
// want the AI value here from the very first insert above but it's always zero (0)
$stmt->bindValue(':last_insert_id', intval($last_insert_id), PDO::PARAM_INT);
$stmt->bindValue(':some_column', strval($some_column));
$stmt->bindValue(':another_one', strval($another_one));
$stmt->execute();
}
}
}
// all is good so committing the first insert
$pdo->commit();
}
} else {
// the file was not uploaded and it is not mandatory so committing the first insert here and the second insert never happens
$pdo->commit();
}
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollback();
}
throw $e;
echo 'An error occurred.';
echo 'Database Error '. $e->getMessage(). ' in '. $e->getFile().
': '. $e->getLine();
}
}
My goal is that the first insert always gets inserted (should nothing fail in it). The second insert is optional depending if a file is attached.
If the file is attached and all the file operations are good, then I'll insert some values in another table and use the auto_increment value from the first insert in this second table ( the idea is as a foreign key).
But for whatever reason, the value inserted is always zero (0).
When the code executes successfully both table inserts complete (granted a file is present and the second insert even fires)...
The row in the first table is created and 1 or more rows in the second insert's table are created but they have a value of 0 in the designated column, where I would expect them to contain the AI value from the first insert...
I've tried to call $pdo->commit() in several other places that "make sense" to me thinking that the first insert must be committed for an AI value to even exist on that table but no luck with any of them...
I even tried this I saw in another Stackoverflow post as a test to make sure PDO isn't doing anything wonky, but PDO is fine...
$conn = new PDO(connection info here);
$conn->exec('CREATE TABLE testIncrement ' .
'(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50))');
$sth = $conn->prepare('INSERT INTO testIncrement (name) VALUES (:name)');
$sth->execute([':name' => 'foo']);
var_dump($conn->lastInsertId());
And the above does return: string(1) "1"
So I think PDO is ok (granted the above was not wrapped in a transaction and I haven't tried that yet)
Hope I have provided enough clear details...
Does anyone know why I am getting 0 and not the last insert id?
Any help is greatly appreciated and thank you!
You need to check the result of $stmt->execute. Read the docs on PDOStatement::execute and you'll see that it returns a boolean value:
Returns TRUE on success or FALSE on failure.
Then read the docs on PDOStatement::errorInfo. Check this if execute returns FALSE.
$stmt->execute();
echo "\nPDOStatement::errorInfo():\n";
$arr = $stmt->errorInfo();
print_r($arr);
EDIT: it's not generally a good idea to output errors to the screen, I did so in this case for convenience. A better approach would be to write a log file:
$arr = $stmt->errorInfo();
file_put_contents("/path/to/file.log", print_r($arr, TRUE));

Update db tables of only updated fields of form- JQuery, PhP

I have a huge multistep form with data for multiple tables in mysql db. For every field my html is like-
input type="text" name="names" value="" // value set using php echo
On submit at php I am doing this for all the fields of my form-
$name=$_POST['names'] ?? ' '
to avoid unidentified index and unidentified variable
Then i update my first table and write log that its updated.
$query=mysqli_query($con,"UPDATE teacherpersonal set name='$name' ... where id=$id");
write_mysql_log("teacherpersonal updated", "facultydetails", $id).
I have defined write_mysql_log.
And similarly i update all the remaining tables with either the updated values or blank ("") values.
Since you can see that update query always executes even if the fields are not changed. Hence it is always logged that the tables are updated. But that's not what I want. I want to update only those fields in the table which are changed and remaining stay intact and log only those tables which are thus updated. Many tables won't be updated this way as the user might change only few details.
Using jquery and php.
My write_mysql_log is
function write_mysql_log($message, $db, $faculty_id)
{
$con=mysqli_connect("localhost","root","");
mysqli_select_db($con,"facultydetails");
// Construct query
$sql = "INSERT INTO my_log (message, faculty_id) VALUES('$message', '$faculty_id')";
$query=mysqli_query($con, $sql);
// Execute query and save data
if($query) {
echo 'written to the database';
}
else {
echo 'Unable to write to the database';
}
}
This you can achieve in 2 different ways.
1) With the help of jQuery check the values which are updated, post only those values to the php script
2)At the time of updating the check the current values with the updated one based on that criteria update the db tables.
solution 1 is less time taking process compare to the other.
You need to update only the user edited value, by doing this you can achieve it;
$oldvalue = array("username" => "green", "email" => "green#mail.com","dob" => "111");
$newvalue = array( "email" => "green#mail.com","dob" => "111","username" => "blue");
$updates = array_diff($newvalue, $oldvalue);
$implodeArray = implode(', ', $updates);
$sql = ("UPDATE user WHERE userID=$userID SET $implodeArray");
mysql_query($sql,$this->_db) or die(mysql_error());
mysql_close();
Output:
$updates = array_diff($newvalue, $oldvalue);
will have:
Array ( [username] => blue )
which is changed one
Ok after considering many options like-
create json object for old and new data and then compare and check which values changed and update that table and log it.
Or create a php array with old and new data and check diff then do the same (as suggested by Ram Karuppaiah)
Or a bad idea to have a flag on every input and then mark which ones have changed using onkeyup jquery event then try to update only those fields tables.
So finally what i did is that i let the form get submitted with all the data. As earlier i am taking the data in php as $name=$_POST['names'] ?? ' ' (blank if nothing is submitted or if something submitted then its value).
Before update statement in php, i am querying the table and comparing the database values with the values i got, if all same i dont do anything. If not then i update the table with the new values and log the change.

Seemingly identical sql queries in php, but one inserts an extra row

I generate the below query in two ways, but use the same function to insert into the database:
INSERT INTO person VALUES('','john', 'smith','new york', 'NY', '123456');
The below method results in CORRECT inserts, with no extra blank row in the sql database
foreach($_POST as $item)
$statement .= "'$item', ";
$size = count($statement);
$statement = substr($statement, 0, $size-3);
$statement .= ");";
The code below should be generating an identical query to the one above (they echo identically), but when I use it, an extra blank row (with an id) is inserted into the database, after the correct row with data. so two rows are inserted each time.
$mytest = "INSERT INTO person VALUES('','$_POST[name]', '$_POST[address]','$_POST[city]', '$_POST[state]', '$_POST[zip]');";
Because I need to run validations on posted items from the form, and need to do some manipulations before storing it into the database, I need to be able to use the second query method.
I can't understand how the two could be different. I'm using the exact same functions to connect and insert into the database, so the problem can't be there.
below is my insert function for reference:
function do_insertion($query) {
$db = get_db_connection();
if(!($result = mysqli_query($db, $query))) {
#die('SQL ERROR: '. mysqli_error($db));
write_error_page(mysqli_error($db));
} #end if
}
Thank you for any insite/help on this.
Using your $_POST directly in your query is opening you up to a lot of bad things, it's just bad practice. You should at least do something to clean your data before going to your database.
The $_POST variable often times can contain additional values depending on the browser, form submit. Have you tried doing a null/empty check in your foreach?
!~ Pseudo Code DO NOT USE IN PRODUCTION ~!
foreach($_POST as $item)
{
if(isset($item) && $item != "")
{
$statement .= "'$item', ";
$size = count($statement);
$statement = substr($statement, 0, $size-3);
$statement .= ");";
}
}
Please read #tadman's comment about using bind_param and protecting yourself against SQL injection. For the sake of answering your question it's likely your $_POST contains empty data that is being put into your query and resulting in the added row.
as #yycdev stated, you are in risk of SQL injection. Start by reading this and rewrite your code by proper use of protecting your database. SQL injection is not fun and will produce many bugs.

Updating table using string mysql

I have this string response coming from the server :
string '{"code":1,"status":200,"data":
[{"connect_id":"3","equipment_id":"1","sample_id":"33","test_id":"44","message_type":"test_ordered","sent_date":"0000-00-00"},
{"connect_id":"12","equipment_id":"34","sample_id":"234","test_id":"234","message_type":"asdasd","sent_date":null}]}'
I have to update my local table's fields using the values found in "data".
In case a value coming from the response is NULL (on a particular field from "data"), there should be no changes to that field when updating the local table.
The table to be updated has many fields but let's say I want to update only the following three fields: equipment_id,sample_id,test_id.
After the update is succesfull I have to send back a response to the server telling that the transaction was succesfull and to update it's status (which is a field of the table's server from where the data has been collected to send the response) so the server won't send the response twice.
I'm assuming you're using PDO and a valid pdo-connection in variable $dbc. I assume further that you identify your rows via the connect_id. You should change this according your needs.
Following code should give you an outline how to tackle this. Please regard my comments. I doesn't have done all for you, especially omitted the error handling.
$result = json_decode($response, true);
// You should validate that the decoding was successful and
// that the result contains all the data you expect.
// You find your data as in $result['data']
// This is an (numbered) array of associative arrays with your "columns" as key
// and the data as value
// prepare your update statement
$q = $dbc->prepare('
UPDATE mytable
SET
equipment_id = ?,
sample_id = ?,
test_id = ?
WHERE
connect_id = ?
');
foreach ($result['data'] as $row) {
// and execute it while looping through the data-array with
// the appropriate data. Please note that the order of the
// parameters got to be the same as in the prepared statement
$params = array(
$row['equipment_id'],
$row['sample_id'],
$row['test_id'],
$row['connect_id']
);
$q->execute($params);
// check success of the execute too.
}

Update Only Changed Values to Database

I have a form that I am trying to use to track batches of beer. Because the process of brewing takes several weeks or even months, I cannot complete the form all at once. When I first create a record for a beer, most of my values are set as NULL. When I retrieve the record and attempt to update it, it also updates all my NULL values to zeros. How can I send only changed values to the database from my form so the rest will be left as NULL?
Below is a sample of my update code (please forgive any PDO transgressions - it is my first foray into using PDO).
<?php
//Connect to Database
try {
$DBH = new PDO('mysql:host=localhost; dbname=dbname', 'user', 'password');
}
catch (PDOException $e) {
echo $e->getMessage();
exit();
}
//Build Update SQL Query
$update = "UPDATE brewlog
SET
BrewDate = :BrewDate,
EndOfPrimary = :EndOfPrimary,
EndOfSecondary = :EndOfSecondary,
PackagingDate = :PackagingDate,
AnticipatedOG = :AnticipatedOG,
WHERE ID = :ID";
//Prepare Query, Bind Parameters, Excute Query
$STH = $DBH->prepare($update);
$STH->bindParam(':ID', $_POST['ID'],PDO::PARAM_INT);
$STH->bindParam(':BrewDate', $_POST['BrewDate'],PDO::PARAM_STR,10);
$STH->bindParam(':EndOfPrimary', $_POST['EndOfPrimary'],PDO::PARAM_STR,10);
$STH->bindParam(':EndOfSecondary', $_POST['EndOfSecondary'],PDO::PARAM_STR,10);
$STH->bindParam(':PackagingDate', $_POST['PackagingDate'],PDO::PARAM_STR,10);
$STH->bindParam(':AnticipatedOG', $_POST['AnticipatedOG'],PDO::PARAM_INT);
$STH->execute();
?>
You would want to validate your data before you bind it. Say something like
if(!empty($_POST['EndOfPrimary'])) {
$eop = $_POST['EndOfPrimary'];
} else {
$eop = NULL;
}
Then bind
$STH->bindParam(':EndOfPrimary', $eop,PDO::PARAM_STR,10);
Edit:
You would also use this validation to check more than if the field was left blank. It looks like you probably want a date to be entered, so perhaps your would check if the user actually entered a date, and if not then send them back to the form with some type of helpful message about where they made the mistake. This is the regexp I use to validate a date.
function pcre_date($subject) {
return preg_match('/^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4}$/', $subject);
/*
* matches 01/01/1984, 1/1/1984, but NOT 1/1/84
* wants mm/dd/yyyy
*/
} // returns 0 for non valid, 1 for valid
Then I would use this for the validation
if(!empty($_POST['EndOfPrimary'])) {
if(pcre_date($_POST['EndOfPrimary'])) {
$eop = $_POST['EndOfPrimary'];
} else {
$form_errors[] = "Please format date as mm/dd/yyyy.";
}
} else {
$eop = NULL;
}
To accomplish this cleanly, use two steps:
In the form presented to the user, maintain a list of changed fields. For example, when a user modifies the data in an input field, use Javascript to copy the contents of that field into a hidden form to be submitted. Then when the user clicks "submit", send only the contents of the hidden form, not the contents of the original form with all fields.
In your PHP script, build your query based on the fields provided. Your query will now include only the fields that were modified. This way, when you perform your UPDATE statement, the unchanged fields will be untouched.
Sorry george, I guess you are way far complicated of what he is trying to do.
Actually, when you use _POST['somevar'], if the field is blank, you get and EMPTY string.
and the empty string is saved to the database so the field is not NULL anymore
The simplest way to ensure the fields stay NULL in the database if there is no value captured is:
$STH->bindParam(':EndOfPrimary', isset($_POST['EndOfPrimary'])?$_POST['EndOfPrimary']:null ,PDO::PARAM_STR,10);

Categories