I have two tables which are linked with many many relationship,Normally I work with Mysqli for querying,but now for some reasons I have to use PDO,So my problem is 1st to check if a value doesn't exists, to add it in a table tag,and if not to take the id of the value and add the related data in the join data.
tag-tagmap-post a tag can belong to many posts,and a post can have many tags I try this but when a tag name already exists I can't retrieve the id of the tag.
$sql = "
INSERT INTO tag (name)
SELECT * FROM (SELECT :name) AS tmp
WHERE NOT EXISTS (
SELECT name FROM tag WHERE name =:name
) LIMIT 1";
try {
$db = getConnection();
$stmt = $db->prepare($sql);
$stmt->bindParam(":name", $post->name);
$stmt->execute();
$test=$post->id = $db->lastInsertId();
$db = null;
//echo json_encode($post);
} catch(PDOException $e) {
//error_log($e->getMessage(), 3, '/tmp/php.log');
echo '{"error":{"text":'. $e->getMessage() .'}}';
}
$sql2="INSERT INTO tagmap (tag_id,post_id,user_id) VALUES(:id2,:post_id,:id)";//
try {
$db = getConnection();
$stmt = $db->prepare($sql2);
$stmt->bindParam("id2", $test);//tag id
$stmt->bindParam("post_id", $post->post_id);//post_id
$stmt->bindParam("id", $id);
$stmt->execute();
//$post2->id = $db->lastInsertId();
$db = null;
echo json_encode(array("result"=>$test));
} catch(PDOException $e) {
//error_log($e->getMessage(), 3, '/tmp/php.log');
echo '{"error":{"text":'. $e->getMessage() .'}}';
}
Thank you for your help!!!
As documented under PDO::prepare():
You cannot use a named parameter marker of the same name more than once in a prepared statement, unless emulation mode is on.
That said, the insertion query itself is over-complicated (and contains syntax errors: there is no LIMIT clause in the INSERT syntax). You could instead:
define a UNIQUE key over (name) in the tag table:
ALTER TABLE tag ADD UNIQUE (name);
use INSERT ... ON DUPLICATE KEY UPDATE:
INSERT INTO tag (name) VALUES (?)
ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id);
This will ensure that $db->lastInsertId() returns the ID of the relevant record whether it has been newly inserted or it already existed.
Related
I have the table my_table and have field id,name (varchar), properties (jsonb) on Postgres.
The name field already inserted and now i want to add some data to the properties field.
<?php
global $dbh;
$queryUpdate = "UPDATE my_table set
properties=jsonb_set(coalesce(properties,'{}'),'{token_approval}','{$token}',true) where id=:id";
$stmt = $dbh->prepare($queryUpdate);
$stmt->bindValue(':id', $idPengajuanUtama);
try {
$stmt->execute();
} catch (PDOException $e) {
throw new RuntimeException($e->getMessage() . "\r\n Function " . __METHOD__);
return false;
}
return true;
?>
When I run the code I always get the below error.
SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for type json\nLINE 1: ..._set(coalesce(properties,'{}'),'{token_approval}','asdfadsna...\n
How can I solve this problem?
Thanks in advance.
You could write the query like this:
update my_table
set properties = jsonb_set(
coalesce(properties, '{}'),
'{token_approval}',
to_jsonb(:token::text)
)
where id = :id
Note that this allows passing the token as a query parameter rather than concatenating it in the query string.
You might find it easir to use built-in jsonb operators:
update my_table
set properties =
coalesce(properties, '{}')
- 'token_approval'
|| jsonb_build_object('token_approval', :token)
where id = :id
Demo on DB Fiddle
I executing the following code which creates a company_id as a UUID_SHORT in a temporary table.
This company_id will then be used to insert records in multiple tables with the UUID as the primary key. My issue is when I try retrieve the company_id that is $company_id in my code it is null. However if I json_encode ($tempResult) the company_id value is there. What am I doing wrong?
Any help is much appreciated, thank you!
try {
$conn = new PDO("mysql:host=localhost;dbname=$dbname", $db->id, $db->pass); //connect to db
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //error modes
$temp = $conn->prepare('CREATE TEMPORARY TABLE tempId (user_id VARCHAR(17) PRIMARY KEY, company_id VARCHAR(17))');
$temp->execute();
$temp = $conn->prepare('INSERT INTO tempId(user_id, company_id) VALUES(:user_id, UUID_SHORT())');
$temp->bindParam(':user_id', $_SESSION['username'], PDO::PARAM_INT);
$temp->execute();
$temp = $conn->prepare('SELECT company_id FROM tempId WHERE user_id = :user_id ');
$temp->bindParam(':user_id', $_SESSION['username'], PDO::PARAM_INT);
$temp->execute();
$tempResult= $temp->fetchAll(PDO::FETCH_ASSOC);
$company_id = $tempResult->company_id;
// $result[1] =$_SESSION('username');
} catch(PDOException $e) {
$result = $e->getMessage();
}
print json_encode($company_id);
Here:
$tempResult= $temp->fetchAll(PDO::FETCH_ASSOC);
If the fetchAll is successful, then $tempResult will be an array. For debugging, we can verify this using the convenient var_dump, e.g.
var_dump($tempResult);
If $tempResult is an array, I'm wondering about this expression:
$tempResult->company_id
What does that return? What do you expect that to return? Why?
EDIT: I know better than to answer a question with a question, or three questions.
However, I can't (in good conscience) bring myself to giving an "answer" to the problem with OP code...
at least not without (figuratively) scratching my head wondering about the actual SQL being used in the code.
What is the purpose of the TEMPORARY TABLE? Why is there an INSERT to it? Why is the UNSIGNED BIGINT datatype (returned by UUID_SHORT() function) being cast to a VARCHAR(17)? Is there some reason we want to lop off 1 or 2 digits when the function returns 18 or 19 decimal digits?
If the intent of this block of code is to return a value from MySQL UUID_SHORT() function, I'm not understanding why we need more than one statement. Obviously, there's something I'm missing, why this wouldn't suffice:
try {
$conn = new PDO("mysql:host=localhost;dbname=$dbname", $db->id, $db->pass);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sth = $conn->prepare('SELECT UUID_SHORT() AS company_id');
$sth->execute();
$company_id = $sth->fetchColumn();
} catch(PDOException $e) {
//var_dump($e->getMessage);
} finally {
if(isset($sth)){ $sth->close(); }
if(isset($conn)){ $conn->close(); }
}
(An application wouldn't churn database connections like this; there would either be a connection pool, or the connection would be passed in to this routine.)
Not sure, but as soon as fetchAll returns array, your code:
$company_id = $tempResult->company_id;
is invalid, you should:
$company_id = $tempResult[0]['company_id'];
or
$tempResult= $temp->fetch(PDO::FETCH_ASSOC);
$company_id = $tempResult['company_id'];
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.
I'm trying to generate a unique username that is not already in my database and then add it as a primary key into my InnoDB database table plus some other field entries.
I get the error code:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'ft6888' for key 'PRIMARY'
Checking the database table manually I can see that it does not already contain the value I'm passing and by echoing the username values I'm binding I can see each is only bound once. The field is not set to auto_increment as in some of these questions but it used as a foreign key in some other tables (but the values I'm binding don't exist in those either).
If I echo out the variables I'm binding just before I bind them I get two sets of correct data. When I insert this same data (copy and pasted) into the table using phpmyadmin it works fine no errors. I can only assume my code itself is somehow trying to insert twice?
$query = "INSERT INTO user_login (username, usertype, hashedpassword) VALUES";
$qPart = array_fill(0, count($excelData), "(?, ?, ?)");
$query .= implode(",",$qPart);
$sth = $dbh->prepare($query);
$i = 1;
$sql = "SELECT username FROM user_login WHERE username = :username";
$sthUser = $dbh->prepare($sql);
Foreach($excelData As $Row){
Do{
//Create unique userID
$finitial = substr(addslashes(str_replace(" ","",$Row['0']['2'])),0,1);
$sinitial = substr(addslashes(str_replace(" ","",$Row['0']['3'])),0,1);
$username = strtolower($finitial).strtolower($sinitial).rand(999,9999);
try {
$sthUser->bindParam(':username', $username);
$sthUser->execute();
$Row = $sthUser->fetch(PDO::FETCH_ASSOC);
}catch (PDOException $e) {
print $e->getMessage();
}
}while(!empty($Row));
$hashedPassword = create_hash($Row['0']['1']);
$usertype = 'Student';
$sth->bindParam($i++, $username);
$sth->bindParam($i++, $usertype);
$sth->bindParam($i++, $hashedPassword);
}
try {
$sth->execute();
}catch (PDOException $e) {
print $e->getMessage();
}
Found the answer here - It seems that bindParam inside the loop binds by reference and is only evaluated at execute statement so it always contains the last bound value for each field.
Changing it to bindValue worked.
All queries execute successfully, when I check table in MySQL row inserted successfully without any error, but lastInsertId() returns 0. why?
My code:
// queries executes successfully, but lastInsetId() returns 0
// the menus table has `id` column with primary auto_increment index
// why lastInsertId return 0 and doesn't return actual id?
$insertMenuQuery = "
SELECT #rght:=`rght`+2,#lft:=`rght`+1 FROM `menus` ORDER BY `rght` DESC limit 1;
INSERT INTO `menus`(`parent_id`, `title`, `options`, `lang`, `lft`, `rght`)
values
(:parent_id, :title, :options, :lang, #lft, #rght);";
try {
// menu sql query
$dbSmt = $db->prepare($insertMenuQuery);
// execute sql query
$dbSmt->execute($arrayOfParameterOfMenu);
// menu id
$menuId = $db->lastInsertId();
// return
return $menuId;
} catch (Exception $e) {
throw new ForbiddenException('Database error.' . $e->getMessage());
}
With PDO_MySQL we must use
$DB->setAttribute(PDO::ATTR_EMULATE_PREPARES,TRUE); // there are other ways to set attributes. this is one
so that we can run multiple queries like:
$foo = $DB->prepare("SELECT * FROM var_lst;INSERT INTO var_lst (value) VALUES ('durjdn')");
but sadly, doing so relieves the $DB from returning the correct insert id. You would have to run them separately to be able to retrieve the insert id. This returns the correct insert id:
$DB->setAttribute(PDO::ATTR_EMULATE_PREPARES,TRUE);
$foo = $DB->prepare("INSERT INTO var_lst (value) VALUES ('durjdn')");
$foo->execute();
echo $DB->lastInsertId();
but this won't:
$DB->setAttribute(PDO::ATTR_EMULATE_PREPARES,TRUE);
$foo = $DB->prepare("SELECT * FROM var_lst;INSERT INTO var_lst (value) VALUES ('durjdn')");
$foo->execute();
echo $DB->lastInsertId();
and this won't even run the two queries:
$DB->setAttribute(PDO::ATTR_EMULATE_PREPARES,FALSE); // When false, prepare() returns an error
$foo = $DB->prepare("SELECT * FROM var_lst;INSERT INTO var_lst (value) VALUES ('durjdn')");
$foo->execute();
echo $DB->lastInsertId();
Place $dbh->lastInsertId(); Before $dbh->commit() and After $stmt->execute();