PDO and rowcount - mysql

I'm trying to make the PDO query below working, but somehow it doesn't.
This is how far i mine brain exploded!
Is there somebody who can say what i'm doint wrong?
$dbh = new PDO("mysql:host=localhost;dbname=databasename", "user", "pass");
$stmt = $dbh->prepare("SELECT * FROM `something` WHERE `email`=':email' and `name`=':name'");
$stmt->bindParam(':email', 'mail#mail.nl',PDO::PARAM_STR);
$stmt->bindParam(':name', 'name',PDO::PARAM_INT);
$stmt->execute();
if($stmt->rowCount() == 1)
{
echo "row count 1";
}
else if ($stmt->rowCount() == 0)
{
echo "row count 0";
}
else if ($stmt->rowCount() > 1)
{
echo "row count greater then 1";
}

Single quotes (') denote string literals in SQL, so the prevent PDO from parsing inside them. If you want to use bind variables, you should remove the quotes (don't worry, PDO will properly handle them as strings):
$stmt = $dbh->prepare
("SELECT * FROM `something` WHERE `email`=:email and `name`=:name");

Related

Unique Profile Slug with PHP and PDO

I am using a class to generate a string name profile to slug and next use an SQL command to tell me whats the unique value to use in insert command, the problem is the command isn't working properly, sometimes it is possible to return a value which already exist...
Thats the class I am using to generate the slug: (composer require channaveer/slug)
And this the example code:
use Channaveer\Slug\Slug;
$string = "john doe";
$slug = Slug::create($string);
$profile_count_stmt = $pdo->prepare("
SELECT
COUNT(`id`) slug_count
FROM
`advogados_e_escritorios`
WHERE
`slug_perfil` LIKE :slug
");
$profile_count_stmt->execute([
":slug" => "%".$slug."%"
]);
$profile_count = $profile_count_stmt->fetchObject();
if ($profile_count && $profile_count->slug_count > 0) {
$profile_increment = $profile_count->slug_count + 1;
$slug = $slug . '-' . $profile_increment;
}
echo 'Your unique slug: '. $slug;
// Your unique slug: john-doe-5
This is the content of the table when the script run:
Do you know how can I improve the select command to prevent it to return existing slugs from DB?
Ok finally found a solution... Heres the code for who wants to generate unique profile slugs using PHP - PDO and MySQL
$string = "John Doe";
$string = mb_strtolower(preg_replace('/\s+/', '-', $string));
$slug = iconv('UTF-8', 'ASCII//TRANSLIT', $string);
$pdo = Conectar();
$sql = "
SELECT slug_perfil
FROM advogados_e_escritorios
WHERE slug_perfil
LIKE '$slug%'
";
$statement = $pdo->prepare($sql);
if($statement->execute())
{
$total_row = $statement->rowCount();
if($total_row > 0)
{
$result = $statement->fetchAll();
foreach($result as $row)
{
$data[] = $row['slug_perfil'];
}
if(in_array($slug, $data))
{
$count = 0;
while( in_array( ($slug . '-' . ++$count ), $data) );
$slug = $slug . '-' . $count;
}
}
}
echo $slug;
//john-doe-1
You should check if the slug exists or not from your database. If it already exists then you can append some random string like the following
$slug = Slug::create($string);
$slugExists = "DB query to check if the slug exists in your database then you may return the count of rows";
//If the count of rows is more than 0, then add some random string
if($slugExists) {
/** NOTE: you can use primary key - id to append after the slug, but that has to be done after you create the user record. This will help you to achieve the concurrency problem as #YourCommenSense was stating. */
$slug = $slug.time(); //time() function will return time in number of seconds
}
//DB query to insert into database
I have followed the same for my blog articles (StackCoder) too. Even LinkedIn follows the same fashion.
Following is screenshot from LinkedIn URL

What's the best way to fetch an array

Alright, so I believe that there is a better way that I can fetch an array from the database, here's the code right now that I have.
$id = 1;
$userquery = mysql_query("SELECT * FROM login WHERE id='$id'");
while($row = mysql_fetch_array($userquery, MYSQL_ASSOC)) {
$username = $row['username'];
$password = $row['password'];
$email = $row['email'];
}
So If I am not wrong, you want a better way to get all the returned rows from mysql in a single statement, instead of using the while loop.
If thats the case, then I must say mysql_ drivers do not provide any such functionality, which means that you have to manually loop through them using foreach or while.
BUT, since mysql_ is already depricated, you are in luck! you can actually switch to a much better and newer mysqli_ or the PDO drivers, both of which DO actually have functions to get all the returned rows.
For mysqli_: mysqli_result::fetch_all
For PDO : PDOStatement::fetchAll
Eg.
mysqli_fetch_all($result,MYSQLI_ASSOC);
// The second argument defines what type of array should be produced
// by the function. `MYSQLI_ASSOC`,`MYSQLI_NUM`,`MYSQLI_BOTH`.
Like the comments already told you: PHP's mysql driver is deprecated. And you should use prepared statements and parameters.
for example in PDO your code would look something like this:
//connection string:
$pdo= new PDO('mysql:host=localhost;dbname=my_db', 'my_user', 'my_password');
//don't emulate prepares, we want "real" ones:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
//use exception-mode if you want to use exception-handling:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$id = 1;
//it's always better to strictly use backticks for db-names (db, tables, fields):
$sql = "SELECT * FROM `login` WHERE `id` = :id";
try
{
//create your prepared statement:
$stmt = $pdo->prepare($sql);
//bind a parameter and explicitly use a parameter of the type integer (in this case):
$stmt->bindParam(":id", $id, PDO::PARAM_INT);
//execute the query
$stmt->execute();
}
catch(PDOException $e)
{
exit("PDO Exception caught: " . $e->getMessage());
}
while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
$username = $row['username'];
$password = $row['password'];
$email = $row['email'];
}
here you go: your PHP-MySQL routine is save against SQL-injections now and no longer uses deprecated PHP-functions! it's kinda state of the art ;)

Mysql dynamic sql statement with PDO and parameters

I want to create a dynamic sql statement and run it using PDO. My problem is that i have some parameters and i cannot think of a way to pass the parameters.
Ex :
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= " AND name = ?";
if (!empty($status)) $query .= " AND status = ?"
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->bindParam (1,$name);
$db_stmt->bindParam (2,$status);
My parameters does not get binded and i don't know how many parameters i have to bind, unless i write the same if statements but with bindParam instructions.
I tryed with mysql_real_escape_string instead bindParam to PDO but for some reason my parameters are added empty.
Any idea on how can i build a dynamic query and bind parameters to PDO ?
Edit 1 :
$arr = array();
if (!empty($name)){
$query .= " AND `name` like :NAME";
$arr['NAME'] = $name;
}
$db_stmt = new PDOStatement();
$db_stmt = $this->db->prepare($query);
$db_stmt->execute($arr);
How can i write a "like" statement ? I tried
$query .= " AND `name` like :NAME" . "%";
and is not working.
What I usually do is the following:
$query = "Select * from `tbl_task` where 1=1";
$arr = array();
if (!empty($name))
{
$query .= " AND `name` = :NAME";
$arr['NAME'] = $name;
}
if (!empty($status))
{
$query .= " AND `status` = :STATUS";
$arr['STATUS'] = $status;
}
$this->db->beginTransaction();
try
{
$tmp = $this->db->prepare($query);
$tmp->execute($arr);
$this->db->commit();
}
catch(PDOException $ex)
{
$this->db->rollBack();
$this->log->error($ex->getMessage());
}
You can't add SQL code as a parameter; only data will do. You'll have to force these bits into $query. They won't be escaped then so they shouldn't contain user-submitted data.
What I usually do is the following:
$query = "Select * from tbl_task where 1=1";
if (!empty($name)) $query .= $db->parse(" AND name = ?s", $name);
if (!empty($status)) $query .= $db->parse(" AND status = ?s",$status);
$data = $this->db->getAll($query);
the idea is in having a function to parse placeholders in arbitrary query part instead of whole query.
I don't bother with native prepared statements though. They pollute PHP scripts with heaps of useless code with not a single benefit.
To answer updated question
as you've been told, you can't bind arbitrary query part. But a literal only.
So, make your literal looks like foo% and then bind it usual way.

How to insert array into mysql using PDO and bindParam?

I'm using the following code. The code works, but I want to change it so that it uses bindparam
try {
$dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
$stqid=array();
for ($i=0; $i<$array_count; $i++){
$stqid[$i][0]=$lastInsertValue;
$stqid[$i][1]=$qid[$i][0];
$stqid[$i][2]=$qid[$i][1];
}
$values = array();
foreach ($stqid as $rowValues) {
foreach ($rowValues as $key => $rowValue) {
$rowValues[$key] = $rowValues[$key];
}
$values[] = "(" . implode(', ', $rowValues) . ")";
}
$count = $dbh->exec("INSERT INTO qresults(instance, qid, result) VALUES ".implode (', ', $values));
$dbh = null;
}
catch(PDOException $e){
echo $e->getMessage();
}
I replaced the following
$count = $dbh->exec("INSERT INTO qresults(instance, qid, result) VALUES ".implode (', ', $values));
with
$sql = "INSERT INTO qresults (instance, qid, result) VALUES (:an_array)";
$stmt = $dbh->prepare($sql);
$stmt->bindParam(':an_array', implode(',', $values),PDO::PARAM_STR);
$stmt->execute();
but the insert doesn't work anymore (I didn't get any error messages though).
QUESTION: What am I doing wrong? How can I rewrite the code to use bindParam?
You're trying to create a statement and bind a param.
Statement are great because it potentially nullify any kind of SQL injection. And it does it by removing the concept of a query being only seen as a string. The SQL query is seen as a string with a parameter list and an the associated data as binded variables.
So the query is not only text, but text + data.
I mean:
This simple query:
SELECT * FROM A WHERE val="$param"
It is not safe because the query is only viewed as a string. And if $param is not checked, it is a SQLi hole.
But when create a statement, your query becomes:
SELECT * FROM A WHERE val=:param
Then you use bindparam to specify the value a :param. Which mean the value is not appended to the query string, but the query is already parsed and the data is provided.
In your case, you bind to the param :array an imploded array (I assume "data1", "data2", etc..). Which is only one parameter with the value as a string ( "data1, data2, data3..." ), so it will only result in one insert and not multiple insertions.
You can change your statement generation by generating a query with enough parameters to handle your array
$sql = "INSERT INTO qresults (instance, qid, result) VALUES ( :val0, :val1, :val2, ...)";
Then loop on your array and call the bindparam method for each parameters.
$count = 0;
foreach($values as $val)
{
$stmt->bindParam(":val$count", $val,PDO::PARAM_STR);
$count++;
}
This will work.
Edit: This solution show how it works for a one dimensional array, but can be easily extended to your problem by tweaking the statement query generation and modify the bindparam loop.
Your statement should looks like:
$sql = "INSERT INTO qresults (instance, qid, result) VALUES (:val0, :val1, :val2) , (:val3, :val4, :val5), ...";
You just have to count the number of element in your base array.

Why am I getting error SQLSTATE[HY093]: Invalid parameter number: ? How can I fix it?

Based on this question How to insert array into mysql using PDO and bindParam?
I'm trying to insert values of an array into mysql via PDO.
I'm having a hard time of it, because I keep getting the following error.
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
for this line $stmt->execute();
I'm guessing the problem has something to do with this line
$stmt->bindParam(':val$count', $val,PDO::PARAM_STR); Specifically 'val$count', but I'm not sure exactly what is going wrong.
QUESTION: What am I doing wrong? How can I fix this?
Anyway here is the code I'm using along with the sample array.
$lastInsertValue=87;
$qid[0][0]=1;
$qid[0][1]=1;
$qid[1][0]=2;
$qid[1][1]="null";
$qid[2][0]=3;
$qid[2][1]=0;
$array_count = count($qid);
if (isset($lastInsertValue))
{
try
{
$dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
$stqid=array();
$a=0;
for ($i=0; $i<$array_count; $i++)
{
$stqid[$a]=$lastInsertValue;
$a++;
$stqid[$a]=$qid[$i][0];
$a++;
$stqid[$a]=$qid[$i][1];
$a++;
}
$sql = "INSERT INTO qresults (instance, qid, result) VALUES ( :val0, :val1, :val2)";
$count = 0;
$stmt = $dbh->prepare($sql);
foreach ($stqid as $val)
{
$stmt->bindParam(':val$count', $val,PDO::PARAM_STR);
$count++;
}
$stmt->execute();
$dbh = null;
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
Yikes, so many issues.
Your array building is so verbose. Try this
$stqid = array();
foreach ($qid as $qidArr) {
$stqid[] = $lastInsertValue; // no idea why you repeat this
$stqid[] = $qidArr[0];
$stqid[] = $qidArr[1];
}
Use positional placeholders if you're simply relying on number of arguments
$sql = 'INSERT INTO ... VALUES (?, ?, ?)';
bindParam uses references which you are overwriting with each loop iteration. You would want to use bindValue() instead
Your query only has 3 placeholders but your $stqid array has 9 items. This is the source of your error.
You have single quotes around a variable, which will be treated as the variable name $count (not the value), try concatenating the variable to the string. Give this a try:
$stmt->bindParam(':val' . $count, $val,PDO::PARAM_STR);
for ($i=0; $i<$array_count; $i++)
{
$stqid[$a]=$lastInsertValue;
$a++;
$stqid[$a]=$qid[$i][0];
$a++;
$stqid[$a]=$qid[$i][1];
$a++;
}
so in case $i = 2, it will add $stqid[6], $stqid[7], $stqid[8] so
foreach ($stqid as $val)
{
$stmt->bindParam(':val$count', $val,PDO::PARAM_STR);
$count++;
}
will give you :val0 to :val8
In your query you have only :val0 to :val2.
Also having multiple values in one field in database is bad. Don't do it. Try to redesign your DB differently
EDIT: bad math in the morning... sorry