You could use a PDO abstraction layer for this
I have made a class for this in the past
It uses: PDO, bound parameters, prepared statements
and it inserts everything in one sql query and the insert looks like this:
$db->insertRows('test_table', $default_row, $rows);
The full code
(which might seem a bit long, but makes sense if you read it) including the code for the connection would look like:
<?php
// Establish connection (on demand)
$db = new PdoHelper(function(){
$db_server = 'localhost';
$db_port= '3306';
$db_name = 'your_database';
$db_user = 'your_username';
$db_pass = 'your_password';
$dsn = 'mysql:host='.$db_server.';dbname='.$db_name.';port='.$db_port;
$driver_options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'",
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
);
$dbh = new PDO( $dsn, $db_user, $db_pass, $driver_options );
return $dbh;
});
// Make a blank sample to have default values for row keys
$default_row = array(
'a'=>null,
'b'=>null,
'c'=>null,
);
// The rows that we want to insert, with columns in the wrong order and nonsense
$rows = array(
array(
'b'=>'a2',
'c'=>'a3',
),
array(
'c'=>'b3',
'b'=>'b2',
),
array(
'b'=>'c2',
'c'=>'c3',
'nonsense'=>'boo',
),
);
// The actual insert query
// INSERT INTO `test_table` (`a`,`b`,`c`) VALUES (null,'a2','a3'), (null,'b2','b3'), (null,'c2','c3')
$db->insertRows('test_table', $default_row, $rows);
// The class that does it all
class PdoHelper {
private $db, $factory;
public function __construct($factory)
{
$this->factory = $factory;
}
public function connect()
{
$cb = $this->factory;
$this->db = $cb();
}
public function release()
{
$this->db = null;
}
public function implyConnect()
{
if(!$this->db){
$this->connect();
}
}
public function begin()
{
$this->implyConnect();
if($this->db instanceof PDO){
$this->db->beginTransaction();
}
}
public function commit()
{
$this->implyConnect();
if($this->db instanceof PDO){
$this->db->commit();
}
}
public function prepare($sql, $data=null, $callback=null)
{
$err = null;
$flat_data = array();
if($data){
$flat_data = self::flatten($data);
$sql = preg_replace_callback('/\?/isu', function($v) use (&$data) {
$val = array_shift($data);
if(is_array($val)){
return self::arrayToPlaceholder($val);
}
return '?';
}, $sql);
}
$this->implyConnect();
if($this->db instanceof PDO){
$stmt = $this->db->prepare($sql);
if($stmt instanceof PDOStatement){
$i = 1;
foreach($flat_data as $v) {
if(is_int($v)){
// workaround for a PDO bug with LIMIT ?,?
$stmt->bindValue($i++, $v, PDO::PARAM_INT);
}else{
$stmt->bindValue($i++, $v, PDO::PARAM_STR);
}
}
}
}
if($callback){
return call_user_func_array($callback, array($stmt));
}
return $stmt;
}
public function query($sql)
{
$res = false;
$args = func_get_args();
$data = array();
$callback = null;
if(isset($args[2])){
$data = $args[1];
$callback = $args[2];
}else
if(isset($args[1])){
if(is_callable($args[1])){
$callback = $args[1];
}else{
$data = $args[1];
}
}
$this->implyConnect();
$stmt = $this->prepare($sql, $data);
$res = $stmt->execute();
if($res && $callback && is_callable($callback)){
return call_user_func_array($callback, array($stmt, $this->db));
}
return $stmt;
}
// Helper functions
public function insertRows($table, $default, $rows=array(), $flag=null, $chunk_size=500)
{
if(empty($rows)){
return null;
}
$chunks = array_chunk($rows, $chunk_size);
foreach($chunks as $rows){
$data = array();
$data[] = $this->extend($default, $rows);
// http://stackoverflow.com/questions/1542627/escaping-column-names-in-pdo-statements
$flag = strtolower($flag);
$flags = array(
'ignore'=>'INSERT IGNORE INTO ',
'replace'=>'REPLACE INTO ',
);
$cols = array();
foreach($default as $k=>$v){
$k = str_replace('`', '``', $k);
$cols[] = '`'.$k.'`';
}
$sql = (isset($flags[$flag])?$flags[$flag]:'INSERT INTO ').$table.' ('.implode(',', $cols).') VALUES ?';
if($flag==='update'){
$cols = array();
foreach($default as $k=>$v){
$k = str_replace('`', '``', $k);
$cols[] = '`'.$k.'`=VALUE('.$k.')';
}
$sql .= ' ON DUPLICATE KEY UPDATE '.implode(', ', $cols);
}
$res = $this->query($sql, $data);
if(!$res){
return $res;
}
}
return $res;
}
public function insertRow($table, $default, $row, $flag=null)
{
$rows = array($row);
return $this->insertRows($table, $default, $rows, $flag);
}
// Helper functions
public static function extend($set, $rows)
{
foreach($rows as $k=>$v){
$v = array_intersect_key($v, $set);
$rows[$k] = array_replace($set, $v);
}
return $rows;
}
public static function flatten($x)
{
$d = array();
if(is_array($x)){
foreach($x as $k=>$v){
$d = array_merge($d, self::flatten($v));
}
}else{
$d[] = $x;
}
return $d;
}
public static function arrayToPlaceholder($array, $timeZone=null) {
return implode(',', array_map(function($v) use($timeZone){
if(is_array($v)){
return '('.self::arrayToPlaceholder($v, $timeZone).')';
}
return '?';
}, $array));
}
public function arrayToList($array, $timeZone=null) {
return implode(',',array_map(function($v) use($timeZone){
if(is_array($v)){
return '('.self::arrayToList($v, $timeZone).')';
}
$this->implyConnect();
return $this->escape($v);
},$array));
}
public function escape($val, $stringifyObjects=false, $timeZone=false) {
if(is_null($val)) return 'NULL';
if(is_bool($val)) return ($val) ? 'true' : 'false';
if(is_int($val)) return (string)$val;
if(is_float($val)) return (string)$val;
if (is_array($val)) {
return $this->arrayToList($val, $timeZone);
}
if(is_callable($val)){ return null; } // TODO
$val = preg_replace_callback('/[\0\n\r\b\t\\\'\"\x1a]/um', function($s) {
switch($s) {
case "\0": return "\\0";
case "\n": return "\\n";
case "\r": return "\\r";
case "\b": return "\\b";
case "\t": return "\\t";
case "\x1a": return "\\Z";
default: return "\\".$s;
}
}, $val);
return $this->db->Quote($val);
}
// Debug functions
public function getSQL($sql, $data){
foreach($data as $k=>$v){
if(is_array($v)){
$data[$k] = self::arrayToList($v);
}else{
$this->implyConnect();
$data[$k] = $this->escape($v);
}
}
$sql = preg_replace_callback('/\?/', function($match) use(&$data)
{
return array_shift($data);
}, $sql);
return $sql;
}
}