I\'m using PDO to execute a statement with an IN
clause that uses an array for its values:
$in_array = array(1, 2, 3);
$in_values = implode(\',\'
As PDO doesn't seem to provide a good solution, you might as well consider using DBAL, which mostly follows PDO's API, but also adds some useful features http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion
$stmt = $conn->executeQuery('SELECT * FROM articles WHERE id IN (?)',
array(array(1, 2, 3, 4, 5, 6)),
array(\Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
);
There are probably some other packages out there that don't add complexity and don't obscure the interaction with the database (like most ORM do), but at the same time make small typical tasks bit easier.
PDO is not good with such things. You need to create a string with placeholders dynamically and insert it into the query, while binding array values the usual way. With positional placeholders it would be like this:
$in = str_repeat('?,', count($in_array) - 1) . '?';
$sql = "SELECT * FROM my_table WHERE my_value IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($in_array);
$data = $stm->fetchAll();
In case there are other placeholders in the query, you could use the following approach (the code is taken from my PDO tutorial):
You could use array_merge()
function to join all the variables into a single array, adding your other variables in the form of arrays, in the order they appear in your query:
$arr = [1,2,3];
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE foo=? AND column IN ($in) AND bar=? AND baz=?";
$stm = $db->prepare($sql);
$params = array_merge([$foo], $arr, [$bar, $baz]);
$stm->execute($params);
$data = $stm->fetchAll();
In case you are using named placeholders, the code would be a little more complex, as you have to create a sequence of the named placeholders, e.g. :id0,:id1,:id2
. So the code would be:
// other parameters that are going into query
$params = ["foo" => "foo", "bar" => "bar"];
$ids = [1,2,3];
$in = "";
$i = 0;// we are using an external counter
// because the actual array keys could be dangerous
foreach ($ids as $item)
{
$key = ":id".$i++;
$in .= "$key,";
$in_params[$key] = $item; // collecting values into a key-value array
}
$in = rtrim($in,","); // :id0,:id1,:id2
$sql = "SELECT * FROM table WHERE foo=:foo AND id IN ($in) AND bar=:bar";
$stm = $db->prepare($sql);
$stm->execute(array_merge($params,$in_params)); // just merge two arrays
$data = $stm->fetchAll();
Luckily, for the named placeholders we don't have to follow the strict order, so we can merge our arrays in any order.
An alternative version of PHP Delusions (@your-common-sense) using closures:
$filter = ["min_price" => "1.98"];
$editions = [1,2,10];
$editions = array_combine(
array_map(function($i){ return ':id'.$i; }, array_keys($editions)),
$editions
);
$in_placeholders = implode(',', array_keys($editions));
$sql = "SELECT * FROM books WHERE price >= :min_price AND edition IN ($in_placeholders)";
$stm = $pdo->prepare($sql);
$stm->execute(array_merge($filter,$editions));
$data = $stm->fetchAll();
Variable substitution in PDO prepared statements doesn't support arrays. It's one for one.
You can get around that problem by generating the number of placeholders you need based on the length of the array.
$variables = array ('1', '2', '3');
$placeholders = str_repeat ('?, ', count ($variables) - 1) . '?';
$query = $pdo -> prepare ("SELECT * FROM table WHERE column IN($placeholders)");
if ($query -> execute ($variables)) {
// ...
}
Here is a solution for unnamed placeholders (?). If you pass $sql with question mark like "A=? AND B IN(?) " and $args where some of the elements are arrays like [1, [1,2,3]] it will return SQL string with appropriate number of placeholders - "A=? AND B IN(?,?,?)". It needs $args parameter only to find which element is array and how many placeholders it needs. You can find the small PDO extension class with this method that will run your query: https://github.com/vicF/pdo/blob/master/src/PDO.php
public function replaceArrayPlaceholders($sql, $args)
{
$num = 0;
preg_match_all('/\?/', $sql, $matches, PREG_OFFSET_CAPTURE); // Captures positions of placeholders
//echo $matches[0][1][1];
$replacements = [];
foreach($args as $arg) {
if(is_array($arg)) {
$replacements[$matches[0][$num][1]] = implode(',',array_fill(0, count($arg), '?')); // Create placeholders string
}
$num++;
}
krsort($replacements);
foreach($replacements as $position => $placeholders) {
$sql = substr($sql, 0, $position).$placeholders.substr($sql, $position+1); // Replace single placeholder with multiple
}
return $sql;
}
Here is my full code, sorry for my stupid coding, bad structure and comment lines. Maybe someone recode my stupid code :)
sending to class:
$veri_sinifi = new DB_Connect;
$veri_sorgu_degerleri=array(
"main_user_id" => (int)$_SESSION['MM_UserId'],
"cari_grup_id" => [71,72,73],
"cari_grup_adi" => ['fatih','ahmet','ali']
);
$siteler =$veri_sinifi->query("Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id) and cari_grup_adi in (:cari_grup_adi)",$veri_sorgu_degerleri) ;
class get this sql :
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id) and cari_grup_adi in (:cari_grup_adi)
class convert this sql to this.
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id0,:cari_grup_id1,:cari_grup_id2) and cari_grup_adi in (:cari_grup_adi0,:cari_grup_adi1,:cari_grup_adi2)
class binding params:
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= 1 and cari_grup_id in (71,72,73) and cari_grup_adi in ('fatih','ahmet','ali')
code:
class DB_Connect{
var $dbh;
function __construct(){
$host = "";
$db = "";
$user = "";
$password = "";
$this -> dbh = $this -> db_connect($host, $db, $user, $password);
}
public function getDBConnection(){
return $this -> dbh;
}
protected function db_connect($host, $db, $user, $password){
//var_dump($host, $db, $user, $password);exit();
try {
$dbh = new PDO("mysql:host=$host;dbname=$db", $user, $password);
}
catch(PDOException $err) {
echo "Error: ".$err->getMessage()."<br/>";
die();
}
return $dbh;
}
public function query($statement,$bind_params){
$keyword = substr(strtoupper($statement), 0, strpos($statement, " ")); // sql in en başındaki kelimeye bakıyor SELECT UPDATE vs gibi ordaki ilk boşluğa kadar olan kelimeyi alıyor.
//echo $keyword;
$dbh = $this->getDBConnection();
if($dbh){
try{
$sql = $statement;
/*GELEN PARAMETRELERE BAKIP İÇİNDE ARRAY VAR İSE SQL STATEMENT KISMINI ONA GÖRE DEĞİŞTİRİYORUZ.
Alttaki döngünün yaptığı işlem şu. Eğer alttaki gibi bir sorgu değerleri gönderilirse
$veri_sorgu_degerleri=array(
"main_user_id" => (int)$_SESSION['MM_UserId'],
"cari_grup_id" => [71,72,73],
"cari_grup_adi" => ['fatih','ahmet','ali']
);
burada main_user_id tek bir değer diğerleri sise array olarak gönderiliyor. Where IN sorgusu birden fazla değer alabileceği için bunları PDO kabul ederken string olarak kabul ediyor yani yukardaki 71,72,73 değerini tırnak içine tek değermiş gib '71,72,73' şeklinde alıyor yapılması gereken sorgunun değiştirilmesi. bu döngü ile
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id) and cari_grup_adi in (:cari_grup_adi)
şeklindeki sorgu in kısımları değiştirilerek
Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id0,:cari_grup_id1,:cari_grup_id2) and cari_grup_adi in (:cari_grup_adi0,:cari_grup_adi1,:cari_grup_adi2)
halini alıyor bir sonraki foreach de ise yine benzer yapı ile arary olarak gelen değerler in için tek tek bind ediliyor, normal gelen değerler ise normal bind yapılıyor.
*/
foreach($bind_params as $paramkey => $param_value) {
//echo "Key=" . $paramkey ."-".($param_value)."-, Value=" . $param_value;
//echo "<br>";
//echo "is_numeric($param_value)>".is_numeric($param_value)."<br>";
//echo "is_string($param_value)>".is_string($param_value)."<br>";
//echo "is_array($param_value)>".is_array($param_value)."<br>";
$in_key="";
$in_parameters="";
if (is_array($param_value)) // Gelan parametre array ise yeniden yapılandır.
{
foreach ($param_value as $i => $item)
{
$in_key = ":$paramkey".$i;
//echo "<br>$in_key = ".$paramkey.".$i";
$in_parameters .= "$in_key,";
//echo "<br>$in_parameters = ".$in_key;
}
$in_parameters = rtrim($in_parameters,","); // :id0,:id1,:id2
//echo "<br>in_parameters>$in_parameters";
$sql = str_replace(":".$paramkey, $in_parameters,$sql);
//echo "<br>oluşan sql>".$sql."<br>";
}
}
$sql = $dbh->prepare($sql);
foreach($bind_params as $paramkey => $param_value) {
//echo "Key=" . $paramkey ."-".($param_value)."-, Value=" . $param_value;
//echo "<br>";
//echo "is_numeric($param_value)>".is_numeric($param_value)."<br>";
//echo "is_string($param_value)>".is_string($param_value)."<br>";
//echo "is_array($param_value)>".is_array($param_value)."<br>";
if (is_numeric($param_value)==1) // gelen veri numerik ise
{
$param_value = (int)$param_value;
$pdo_param_type = PDO::PARAM_INT;
}
elseif (is_string($param_value)==1)// gelen veri string ise
{$pdo_param_type = PDO::PARAM_STR; }
if (is_array($param_value)) // gelen veri array tipinde ise
{
foreach ($param_value as $i => $param_array_value) // bu döngünün açıklaması yukardaki döngü için yazılan açıklama içinde mevcut
{
$in_key = ":$paramkey".$i;
if (is_numeric($param_array_value)==1) // gelen veri numerik ise
{
$param_array_value = (int)$param_array_value;
$pdo_param_type = PDO::PARAM_INT;
}
elseif (is_string($param_array_value)==1)// gelen veri string ise
{$pdo_param_type = PDO::PARAM_STR; }
$sql->bindValue($in_key, $param_array_value, $pdo_param_type );
//echo "<br>oldu1";
//echo "<br> -$in_key-";
}
//$sql = str_replace(":".$paramkey, $in_parameters, $sql);
//echo "oluşan sql>".$sql."<br>";
}
else // array değilse aşağıdaki şekilde bind yap.
{
//echo "<br>oldu2";
$sql->bindValue(":$paramkey", $param_value, $pdo_param_type ); // bindparam foreach içinde kullanılmaz çünkü execute esnasında bind yaptığı için yani anlık olarak değerleri atamaddığı için for döngüsünde en sonda value değişkeni neyse tüm parametrelere onu atıyor, bu sebeple bindvalue kullanıyoruz.PDO::PARAM_INT
}
} // foreach
$exe = $sql->execute();
$sql->debugDumpParams();
//echo $exe;
}
catch(PDOException $err){
return $err->getMessage();
}
//BU KISMA AİT AÇIKLAMA AŞAĞIDAIR.
switch($keyword){ // sorgu çalıştıktan sonra sorgu sonucuna göre gerekli işlemler yapılıyor.
case "SELECT": // Sorgu select sorgusu ise
$result = array(); //sonuçları diziye aktaracak.
while($row = $sql->fetch(PDO::FETCH_ASSOC)){ // sonuç satırlarını tek tek okuyup
//echo $row;
$result[] = $row; // her bir satırı dizinin bir elemanına aktarıyor.bu değer diziden nasıl okunur açıklaması aşağıda
}
return $result; // sorgudan dönen diziyi doğrudan ana programa aktarıyor orada dizi olarak okunabilir.
break;
default:
return $exe;
break;
}
}
else{
return false;
}
}
}