I have three tables in my database (MySQL/InnoDB): pupil, evening_activity and pupil_evening_activity. As you can guess there is an m:m relationship between pupil and evening_activity.
I have a HTML form to insert new pupils and associate to them evening activities. After submitting the form, I would like to insert pupils and their associated activities in the database. So I have written this code:
$db = new PDO('mysql:host=localhost;dbname=school;charset=utf8','root', '****');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$sql = "INSERT INTO pupil
(name, surname)
VALUES
('test_name', 'test_surname')";
$st = $db->prepare($sql);
$st->execute();
foreach ($eveningActivityIds as $eveningActivityId) {
$sql = "INSERT INTO pupil_evening_activity
(pupil_id, evening_activity_id)
VALUES
(?, ?)
";
$st = $db->prepare($sql);
$st->execute(array($db->lastInsertId(), $eveningActivityId));
}
} catch (PDOException $e) {
echo $e->getMessage();
return false;
}
I have written that code expecting PDO::lastInsertId() always returns the last inserted pupil's id, but I've found out that what it really returns is the last inserted id on the whole database. So if another row is inserted in a hypothetical teacher table just before calling to $db->lastInsertId() on the code above, $db->lastInsertId() would return the id of the inserted teacher instead of the id of the last pupil.
So how I can get 100% safety the last inserted id of pupil table after submitting the form?
I have thought about using a transaction and MAX() function to get last id inserted in the pupil table, but I'm not sure about it.
It's a mistake to have $db->lastInsertId() within the foreach loop, as it may return unpredictable values. Please consider the code below:
try {
$sql = "INSERT INTO pupil
(name, surname)
VALUES
('test_name', 'test_surname')";
$st = $db->prepare($sql);
$st->execute();
// save pupil id into variable here:
$pupil_id = $db->lastInsertId();
// also there's no need to prepare the statement on each iteration:
$sql = "INSERT INTO pupil_evening_activity
(pupil_id, evening_activity_id)
VALUES
(?, ?)";
$st = $db->prepare($sql);
foreach ($eveningActivityIds as $eveningActivityId) {
$st->execute(array($pupil_id, $eveningActivityId));
}
} catch (PDOException $e) {
echo $e->getMessage();
return false;
}
Optimization: you can insert all acivities in one statement. It saves you few requests
try {
$sql = "INSERT INTO pupil
(name, surname)
VALUES
('test_name', 'test_surname')";
$st = $db->prepare($sql);
$st->execute();
// save pupil id into variable here:
$pupil_id = $db->lastInsertId();
$sql = 'INSERT INTO pupil_evening_activity
(pupil_id, evening_activity_id)
VALUES ';
$values = array();
foreach ($eveningActivityIds as $eveningActivityId) {
$sql .= '(?, ?),';
array_push($values, $pupil_id, $eveningActivityId);
}
$st = $db->prepare(chop($sql, ','));
$st->execute($values);
} catch (PDOException $e) {
echo $e->getMessage();
return false;
}
Related
I know this has been asked before but can't find a answer to my specific instance of this error.
Catchable fatal error: Object of class PDOStatement could not be converted to string
Here is my code:
try
{
// create connection and set error attributes
$conn = new PDO("mysql:host=" . HOST . "; dbname=" . DATABASE, USER, PASS);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//define insert and query variables
$query = "SELECT * FROM cart WHERE user_id=? AND product_id=?;";
$insert = "INSERT INTO cart (user_id, product_id, qty, date)
VALUES ('$sessId', :i, :q, 'Now()');";
// execute a query to check if item all ready exist in cart
$r = $conn->prepare($query);
$r->execute(array(
session_id(),
$i
));
if ($r->rowCount() > 0) {
echo 'Item already exists in cart';
} else {
$q = $conn->prepare($insert);
$q->bindParam(':i', $i);
$q->bindParam(':q', $q);
$q->execute();
header('Location:../shopping_cart.php');
}
$r->closeCursor();
}
catch (PDOException $e)
{
echo $e->getMessage();
}
I cant find anything wrong with it, otherwise I just need to know what I need to do to get the values to convert, or should I look into another method.
Change the name of variable $q, you are changing type for nothing.
$q = $conn->prepare($insert);
$q->bindParam(':i', $i);
$q->bindParam(':q', $q); //binding a prepare statement itself?
Change to, and define the $qty with initial value of $q.
$q = $conn->prepare($insert);
$q->bindParam(':i', $i);
$q->bindParam(':q', $qty);
First you are testing if there is at least one record using
session_id()
If not then you are trying to insert with
$sessId
Assuming that they somehow have the same value...
I think you should insert with real prepared statement like:
$insert = "INSERT INTO cart (user_id, product_id, qty, date)
VALUES (:s, :i, :q, :n);";
...
...
...
$q = $conn->prepare($insert);
$q->bindParam(':s', $sessId);
$q->bindParam(':i', $i);
$q->bindParam(':q', $qty);
$q->bindParam(':n', Now());
$q->execute();
header('Location:../shopping_cart.php');
I can do it with php/mysqli with multiple step.
So, table have only two column.
ID, Name
Both of then will be unique.
I want to check if Name is available in the database, get the ID if it is available.
If it is not available, add Name on the database and get the ID.
I can do it with php/mysql which need multiple sql query.
Is there a way do it (checking database, if not exist add it and get the ID) only with one mysql query and get the ID?
Thanks in advance!
My code (MySQLi Procedural)
function abc($name) {
global $conn;
$checkName = mysqli_query($con,"SELECT * FROM category WHERE name=".mysql_real_escape_string($name));
if (mysqli_num_rows($checkName) > 0) {
$inputName = mysqli_query($con,"INSERT INTO category (name) VALUES ('".mysql_real_escape_string($name)."')");
if (!$inputName) { die(mysqli_error($conn)); }
$checkName2 = mysqli_query($con,"SELECT * FROM category WHERE name=".mysql_real_escape_string($name));
while($blahblah = mysqli_fetch_assoc($checkName2)) {
$returnData[] = $blahblah;
}
} else {
while($blahblah = mysqli_fetch_assoc($checkName)) {
$returnData[] = $blahblah;
}
}
return $blahblah;
}
This can be done with just one line. Use "INSERT IGNORE INTO.." or "REPLACE INTO....". This page refers.
If you use the Object-Oriented MySQLi, this is how you do it:
$mysqli = new mysqli(...);
$name = "Something";
$query = $mysqli->prepare("SELECT id, name FROM table WHERE name=?");
$query->bind_param('s', $something);
$query->execute();
$query->bind_result($id, $name);
$query->store_result();
if($query->num_rows == 1) {
return $id;
} else {
$queryTwo = $this->mysqli->prepare("INSERT INTO table VALUES('', ?);");
$queryTwo->bind_param('s', $name);
$queryTwo->execute();
$queryTwo->close();
$queryThree = $this->mysqli->prepare("SELECT id FROM table WHERE name=?");
$queryThree->bind_param('s', $name);
$queryThree->execute();
$queryThree->bind_result($id);
$queryThree->store_result();
while($queryThree->fetch()) {
return $id;
}
$queryThree->free_result();
$queryThree->close();
}
$query->free_result();
$query->close();
How can I allow the user submitting a form, to update his entry on "re-submission"
for example
12345678910 (unique id) , submitted the form with selections,
12345678910 , re-submitted with new selections
what's the function responsible for "automatically" updating such kind of form entries.
I know that I can use a check if the entry exists, but how do I update it if it exists and insert it in a new row if it doesn't ...
function checkstudentid($studentid)
{
$con = connectvar();
mysql_select_db("database1", $con);
$result = mysql_query(
"SELECT * FROM table WHERE studentid='$studentid' LIMIT 1");
if(mysql_fetch_array($result) !== false)
....
// I want to add the entry here since it doesn't exist...with checkboxes
// else , I want to update if it already exists
}
Now I'm also not completely positive if the above code will work...but this is what I have for starters, if there is any other way or if the method I'm using is "wrong" , I would appreciate the heads up...or if what I'm trying to is even possible (the way I'm doing it)...
NOTES
I only have one php file which the form submits to.
I am not using a login/registration system
I do not want to display all the data in a table using HTML, just an
"automatic" update if the studentid already exists in the table
If I were using a deprecated method to interact with a database, I would probably just do this:
<?php
function checkstudentid($studentid) {
$con = connectvar();
mysql_select_db("database1", $con);
$result = mysql_query(
"SELECT * FROM table WHERE studentid='$studentid' LIMIT 1");
$query = '';
if (mysql_num_rows($result) > 0) {
$query = "UPDATE table SET column1='$value_one', column2='$value_two' WHERE studentid='$studentid'";
} else {
$query = "INSERT INTO table VALUES('$new_id', '$value_one', '$value_two')";
}
if (mysql_query($query)) {
return true;
} else {
return false;
}
}
?>
But then again, I would use PDO to interact with the DB.
Here is a simple PDO example (you just have to write the function to return the connection):
<?php
function checkstudentid($studentid) {
$update = false;
$dbh = formPDOConnection();
$query = "SELECT studentid FROM table WHERE studentid=:id";
$stmt = $dbh->prepare($query);
$stmt->bindValue(':id', $studentid, PDO::PARAM_STR);
if ($stmt->execute()) {
if ($stmt->rowCount()) {
$update = true;
}
} else {
return 'failure to execute query';
}
// if we just need to update
if ($update) {
$update = "UPDATE table SET value1=:v1,
value2=:v2 WHERE studentid=:id";
$stmt = $dbh->prepare($update);
$stmt->bindValue(':id', $studentid, PDO::PARAM_STR);
$stmt->bindValue(':v1', $value_one, PDO::PARAM_STR);
$stmt->bindValue(':v2', $value_two, PDO::PARAM_STR);
} else {
$insert = "INSERT INTO table VALUES(:id,:v1,v2)";
$stmt = $dbh->prepare($insert);
$stmt->bindValue(':id', $new_id, PDO::PARAM_STR);
$stmt->bindValue(':v1', $value_one, PDO::PARAM_STR);
$stmt->bindValue(':v2', $value_two, PDO::PARAM_STR);
}
return $stmt->execute();
}
?>
Save yourself a headache and stop using mysql_*
You can use INSERT... ON DUPLICATE KEY UPDATE... on your mysql code instead use the logic in your PHP.
Here's a sample:
INSERT INTO `category` (`id`, `name`) VALUES (12, 'color')
ON DUPLICATE KEY UPDATE `name` = 'color';
Reference: http://dev.mysql.com/doc/refman/5.6/en/insert-on-duplicate.html
Hi here I have this code to insert data to mysql database into table radnici :
try {
$STH = $db->prepare("INSERT INTO radnici (ime_prezime, jmbg, grad, ulica, posta, telefon, uloga, email, user_id) VALUES (:1,:2,:3,:4,:5,:6,:7,:8,:9)");
$STH->bindParam(':1', $_POST['ime']);
$STH->bindParam(':2', $_POST['jmbg']);
$STH->bindParam(':3', $_POST['grad']);
$STH->bindParam(':4', $_POST['ulica']);
$STH->bindParam(':5', $_POST['posta']);
$STH->bindParam(':6', $_POST['telefon']);
$STH->bindParam(':7', $_POST['pozicija']);
$STH->bindParam(':8', $_POST['email']);
$STH->bindParam(':9', $user_id);
//HERE I NEED TO INSERT DATA TO TABLE WORKHOURS BUT CONNECTED WITH JUST ADDEDED ID
//SO here I NEED query something like this // INSERT INTO radnici (ID, ID_radnici, ime_prezime, jmbg,... here ID_radnici is =with ID from table radnici of added data
$STH->execute();
} catch (PDOException $e) {
echo $e->getMessage();
}
echo "<p>Data submitted successfully</p>";
So now when I add this I need to add into this table workhours data but connected with ID with just added data...
so in second table I have: ID, ID_radnici (here I must add ID_radnici same value as ID on table radnici), value, user_id
I write this but DONT work:
UPDATE:
try {
$STH = $db->prepare("INSERT INTO radnici (ime_prezime, jmbg, grad, ulica, posta, telefon, uloga, email, user_id) VALUES (:1,:2,:3,:4,:5,:6,:7,:8,:9)");
$STH->bindParam(':1', $_POST['ime']);
$STH->bindParam(':2', $_POST['jmbg']);
$STH->bindParam(':3', $_POST['grad']);
$STH->bindParam(':4', $_POST['ulica']);
$STH->bindParam(':5', $_POST['posta']);
$STH->bindParam(':6', $_POST['telefon']);
$STH->bindParam(':7', $_POST['pozicija']);
$STH->bindParam(':8', $_POST['email']);
$STH->bindParam(':9', $user_id);
$orderID = $db -> lastInsertId();
$STH1 = $db->prepare("INSERT INTO workhours (ID_radnika) values($orderID)"); // where $value is the value you want to insert...
$STH->execute();
$STH1->execute();
} catch (PDOException $e) {
echo $e->getMessage();
}
echo "<p>Data submitted successfully</p>";
Using lastInsertId will do what you want:
$orderID = $db -> lastInsertId();
Use lastInsertId() and insert your data into the other table using the value returned from it.
Something like this:
int $lastID = $db->lastInsertId();
if ( $lastID ) {
$STH = $db->prepare("INSERT INTO workhours (ID_radnici, value, userid) values($lastID, $value, $user_id)"); // where $value is the value you want to insert...
$STH->execute();
}
I have 3 tables which I have to add a record to them after registration of a new user:
List of Tables:
I. users
... ... ... id (auto_increment, primary)
... ... ... email (email address of new user)
II. blogs
... ... ... id (auto_increment, primary)
... ... ... owner_id (= 'id' in 'users')
III. events
... ... ... id (auto_increment, primary)
... ... ... owner_id (= 'id' in 'users')
... ... ... blog_id (= 'id' in 'blogs')
In this situation I found 2 solutions for adding sequential records:
Solution 1: Using lastInsertId
<?php
try {
// Step 1: add a record to 'users' table and get lastInsertId
$query = $conn->prepare("INSERT INTO users (email) VALUES (:email)");
$query->bindParam(':email', $email);
$query->execute();
$user_id = $conn->lastInsertId();
// Step 2: add a record to 'blogs' table and get lastInsertId
$query = $conn->prepare("INSERT INTO blogs (owner_id) VALUES (:owner)");
$query->bindParam(':owner', $user_id);
$query->execute();
$blog_id = $conn->lastInsertId();
// Step 3: add a record to 'events' table
$query = $conn->prepare("INSERT INTO events (owner_id, blog_id) VALUES (:owner, :blog)");
$query->bindParam(':owner', $user_id);
$query->bindParam(':blog', $blog_id);
$query->execute();
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
Solution 2: Using single execute()
<?php
try {
// Step 1
$query = $conn->prepare("INSERT INTO users (email) VALUES (:email);" .
"INSERT INTO blogs (owner_id) VALUES ((SELECT id FROM users WHERE email = :email));" .
"INSERT INTO events (owner_id, blog_id) VALUES ((SELECT id FROM users WHERE email = :email), (SELECT id FROM blogs WHERE owner_id = (SELECT id FROM users WHERE email = :email)));");
$query->bindParam(':email', $email);
$query->execute();
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
Which solution should I choose for a better performance and security? Is there a better solution for my purpose?
Note: the connection created using PDO:
<?php
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
);
try {
$conn = new PDO("mysql:host=" . App::DB_HOST . ";dbname=" . App::DB_NAME . ";charset=utf8", App::DB_USERNAME, App::DB_PASSWORD, $options);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
I would use transactions as a modification of 1st option.
$conn->beginTransaction();
try {
// Step 1: add a record to 'users' table and get lastInsertId
$query = $conn->prepare("INSERT INTO users (email) VALUES (:email)");
$query->bindParam(':email', $email);
$query->execute();
$user_id = $conn->lastInsertId();
// Step 2: add a record to 'blogs' table and get lastInsertId
$query = $conn->prepare("INSERT INTO blogs (owner_id) VALUES (:owner)");
$query->bindParam(':owner', $user_id);
$query->execute();
$blog_id = $conn->lastInsertId();
// Step 3: add a record to 'events' table
$query = $conn->prepare("INSERT INTO events (owner_id, blog_id) VALUES (:owner, :blog)");
$query->bindParam(':owner', $user_id);
$query->bindParam(':blog', $blog_id);
$query->execute();
$conn->commit();
}
catch (PDOException $e) {
// roll back transaction
$conn->rollback();
echo $e->getMessage();
die();
}
If you do some benchmarks you will see most time will be lost making the request.
From personal benchmarks on simple queries like this the execution time is very low.
The only thing that realy took time is the initialisation/prepare function.
There for making 3 requests will be slower then creating one large one.
EDIT:
Option 1 is the correct one because you do need to use id's, never link using a string or somethign else allways use id's.
Appart from that 1 (prepared) big query is better then 3x a prepare.
Edit. I misread the question at first, thought you are using exec(), not execute().
So, in fact you can combine both, as lastInsertId is just a PHP wrapper for Mysql's LAST_INSERT_ID()
But, as you need two ids, it will require additional mess with setting a variable. So, I doubt second option would worth, although feasible.
Just note that second would work only if PDO emulation mode is turned off
And surely there is no such question like "performance". Both will go perfectly.