Parsing Dictionary Object and Creating MySQL Table through PHP - php

Currently, I have this dictionary object in my PHP script that was posted to my PHP implemented server through my iOS app:
{"command":"save","classname":"GameScore","cheatMode":"0","playerName":"SeanPlott","score":"1337"}
Now I need to create a MySQL table based off the data...
Here is my attempt to parse the data but all i get are the objects, not the keys...
foreach( $text as $stuff ) {
if( is_array( $stuff ) ) {
echo json_encode($stuff);
foreach( $stuff as $thing ) {
echo json_encode($thing);
}
} else {
echo json_encode($stuff);
}
}
Heres the result... "save""GameScore""0""SeanPlott""1337"
This is what I plan on making as a query
$result = query("INSERT INTO '%s'('%s', '%s', '%s', '%s')
VALUES('%s','%s','%s','%s')", $classname, $key1, $key2, $key3, $key4,
$object1, $object2, $$object3, $object4);
But the query should be dynamic, since the dictionary object can have 1 or many keys/objects...
So I figure I need to implement a parsing for loop that generates the query within the array and outputs a string which is the query...
Anyone have any ideas on how to fix my parser?
EDIT: heres my query function to handle MYSQL
function query() {
global $link;
$debug = false;
$args = func_get_args();
$sql = array_shift($args);
for ($i = 0; $i < count($args); $i++) {
$args[$i] = urldecode($args[$i]);
$args[$i] = mysqli_real_escape_string($link, $args[$i]);
}
$sql = vsprintf($sql, $args);
if ($debug) print $sql;
$result = mysqli_query($link, $sql);
if (mysqli_errno($link) == 0 && $result) {
$rows = array();
if ($result !== true)
while ($d = mysqli_fetch_assoc($result))
array_push($rows,$d);
return array('result' => $rows);
} else {
return array('error' => 'Database error');
}
}
EDIT: Took me a while and a lot of help but I completed it... It takes care of the case of spaces in values and adds '`' characters around the keys if there corresponding value contains a space...
$raw_data = '{"command":"save","classname":"GameScore","cheatMode":"0","playerName":"Sean Plott","score":"1337"}';
$data = json_decode($raw_data, true);
$columns = array_slice(array_keys($data), 2);
array_shift($data);
$table_title = array_shift($data);
$values = array();
$num = 0;
foreach($data as $key => $value) {
if(strpos($value, " ") !== false) $columns[$num] = "`".$columns[$num]."`";
$num = $num + 1;
$values[] = (!is_numeric($value)) ? "'".$value."'" : $value;
}
$final_statement = "INSERT INTO " . $table_title . " (".implode(', ', $columns).") VALUES (".implode(', ', $values).")";
echo $final_statement;
If anyone sees any way to optimize this or make it cleaner... please feel free to post something!
Thanks again for everyones input!

You could just use a simple json_decode() and implode() to format it and prepare to insertion. Consider this example:
$raw_data = '{"command":"save","classname":"GameScore","cheatMode":"0","playerName":"SeanPlott","score":"1337"}';
$data = json_decode($raw_data, true);
$columns = array_keys($data); // get the columns
$final_statement = "INSERT INTO `table` (".implode(', ', $columns).") VALUES ('".implode("','", $data)."')";
echo $final_statement;
// outputs: INSERT INTO `table` (command, classname, cheatMode, playerName, score) VALUES ('save','GameScore','0','SeanPlott','1337')
EDIT: Suppose your columns has int types (especially the ones that has numeric values). For whatever reason it doesn't work, because of a mismatch, if so, you could probably do this. Consider this example:
$raw_data = '{"command":"save","classname":"GameScore","cheatMode":"0","playerName":"SeanPlott","score":"1337"}';
$data = json_decode($raw_data, true);
$columns = array_keys($data); // get the columns
$values = array();
foreach($data as $key => $value) {
// if not numeric, add quotes, if not, leave it as it is
$values[] = (!is_numeric($value)) ? "'".$value."'" : $value;
}
$final_statement = "INSERT INTO `table` (".implode(', ', $columns).") VALUES (".implode(', ', $values).")";
echo $final_statement;
// outputs: INSERT INTO `table` (command, classname, cheatMode, playerName, score) VALUES ('save', 'GameScore', 0, 'SeanPlott', 1337)
// Note: numbers has no qoutes

Related

ON DUPLICATE KEY UPDATE [duplicate]

I have a CodeIgniter/PHP Model and I want to insert some data into the database.
However, I have this set in my 'raw' SQL query:
ON DUPLICATE KEY UPDATE duplicate=duplicate+1
I am using CodeIgniter and am converting all my previous in-controller SQL queries to ActiveRecord. Is there any way to do this from within the ActiveRecord-based model?
Thanks!
Jack
You can add the "ON DUPLICATE" statement without modifying any core files.
$sql = $this->db->insert_string('table', $data) . ' ON DUPLICATE KEY UPDATE duplicate=LAST_INSERT_ID(duplicate)';
$this->db->query($sql);
$id = $this->db->insert_id();
I hope it's gonna help someone.
The below process work for me in Codeigniter 3.0.6
public function updateOnDuplicate($table, $data ) {
if (empty($table) || empty($data)) return false;
$duplicate_data = array();
foreach($data AS $key => $value) {
$duplicate_data[] = sprintf("%s='%s'", $key, $value);
}
$sql = sprintf("%s ON DUPLICATE KEY UPDATE %s", $this->db->insert_string($table, $data), implode(',', $duplicate_data));
$this->db->query($sql);
return $this->db->insert_id();
}
Following the snippet linked by Pickett, I made a few modifications to:
1) Update it to use the new Query Builder (CI 3), formerly known as Active Record.
2) You can now pass an associative array (key => value) or an array of associative arrays. In the second form, it performs a multi-update.
I only tested it with mysqli, and it works well.
This piece of code goes in system/database/drivers/mysqli/mysqli_driver.php
function _duplicate_insert($table, $values)
{
$updatestr = array();
$keystr = array();
$valstr = array();
foreach($values as $key => $val)
{
$updatestr[] = $key." = ".$val;
$keystr[] = $key;
$valstr[] = $val;
}
$sql = "INSERT INTO ".$table." (".implode(', ',$keystr).") ";
$sql .= "VALUES (".implode(', ',$valstr).") ";
$sql .= "ON DUPLICATE KEY UPDATE ".implode(', ',$updatestr);
return $sql;
}
function _multi_duplicate_insert($table, $values)
{
$updatestr = array();
$keystr = array();
$valstr = null;
$entries = array();
$temp = array_keys($values);
$first = $values[$temp[0]];
foreach($first as $key => $val)
{
$updatestr[] = $key." = VALUES(".$key.")";
$keystr[] = $key;
}
foreach($values as $entry)
{
$valstr = array();
foreach($entry as $key => $val)
{
$valstr[] = $val;
}
$entries[] = '('.implode(', ', $valstr).')';
}
$sql = "INSERT INTO ".$table." (".implode(', ',$keystr).") ";
$sql .= "VALUES ".implode(', ',$entries);
$sql .= "ON DUPLICATE KEY UPDATE ".implode(', ',$updatestr);
return $sql;
}
And this goes into the /system/database/DB_query_builder.php file:
function on_duplicate($table = '', $set = NULL )
{
if ( ! is_null($set))
{
$this->set($set);
}
if (count($this->qb_set) == 0)
{
if ($this->db_debug)
{
return $this->display_error('db_must_use_set');
}
return FALSE;
}
if ($table == '')
{
if ( ! isset($this->qb_from[0]))
{
if ($this->db_debug)
{
return $this->display_error('db_must_set_table');
}
return FALSE;
}
$table = $this->qb_from[0];
}
$is_multi = false;
foreach (array_keys($set) as $k => $v) {
if ($k === $v) {
$is_multi = true; //is not assoc
break;
}
}
if($is_multi)
{
$sql = $this->_multi_duplicate_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $this->qb_set );
}
else
{
$sql = $this->_duplicate_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $this->qb_set );
}
$this->_reset_write();
return $this->query($sql);
}
Then you can do this for a single row insert/update:
$this->db->on_duplicate('table', array('column1' => 'value', 'column2' => 'value'));
Or this for a multi insert/update:
$this->db->on_duplicate('table', array(
array('column1' => 'value', 'column2' => 'value'),
array('column1' => 'value', 'column2' => 'value')
));
You can tweak the active record function with minimal addition:
DB_driver.php add inside the class:
protected $OnlyReturnQuery = false;
public function onlyReturnQuery($return = true)
{
$this->OnlyReturnQuery = $return;
}
find function query( ...and add at the very beginning:
if ($this->OnlyReturnQuery) {
$this->OnlyReturnQuery = false;
return $sql;
}
and finally in DB_active_rec.php add function:
public function insert_or_update($table='', $data=array())
{
$this->onlyReturnQuery();
$this->set($data);
$insert = $this->insert($table);
$this->onlyReturnQuery();
$this->set($data);
$update = $this->update($table);
$update = preg_replace('/UPDATE.*?SET/',' ON DUPLICATE KEY UPDATE',$update);
return $this->query($insert.$update);
}
Now you can use it as:
$this->db->insert_or_update('table',array $data);
Pros:
uses all the active record validation
Cons:
it is not the best (the most proper) way of extending the function, because if you are planning to update these files, you will have to redo the procedure.
The link to the forum thread above is broken. I don't know of a better way than just using db->query for the call, if someone has a better solution, please post that.
$result = $this->CI->db->query(
"INSERT INTO tablename (id, name, duplicate) VALUES (1, 'Joe', 1) ".
"ON DUPLICATE KEY UPDATE duplicate=duplicate+1");
I hope this helps someone looking for a solution to this.
Below is a code snippet I use everytime for update on duplicates:
$sql = "insert into table_name (id, name) values(".$id.",'".$name."') on duplicate key update id=".$id.",name='".$name."'";
//Executing queries
$result = $this->db->query($sql, array($id, $name));
Note: column id must be primary or unique
Using $this->db->query() and parameters, this is how I do it. the first 4 parameters are for the insert part and the last three parameters are repeated for the on duplicate key update part.
$sql = "insert into application_committee ".
"(application_id, member1_id, member2_id, member3_id) values (?, ?, ?, ?)".
" on duplicate key update member1_id = ?, member2_id = ?, member3_id = ?";
$this->db->query($sql, array($appid, $member_1, $member_2, $member_3,
$member_1, $member_2, $member_3);
Simply done -
$updt_str = '';
foreach ($insert_array as $k => $v) {
$updt_str = $updt_str.' '.$k.' = '.$v.',';
}
$updt_str = substr_replace($updt_str,";", -1);
$this->db->query($this->db->insert_string('table_name', $insert_array).' ON DUPLICATE KEY UPDATE '.$updt_str);

How to insert an array into a Mysql table

I want to insert an array ($array) into a Mysql table (notification), I tried this but nothing is entering. How do I solve this?
$select = "SELECT * FROM addclique WHERE adder_id = :session_id";
$param1 = array ('session_id' => $_SESSION['id']);
$cliques = $db->query($select, $param1);
foreach($cliques as $key)
{
$array[] = $key['clique_id'];
}
$array[] = $key['clique_id'];
$notijfy = new Notification();
$notijfy->addCircle($array);
function addCircle($id_involve){
$escaped_values = array_map('mysql_real_escape_string', array_values($array));
$sql2 = "INSERT INTO notification(id_involve) VALUES (:id_involve)";
$param2 = array ('id_involve' => implode(", ", $escaped_values));
$result2 = $this->db->query ($sql2, $param2);
}
There are several ways.
I would just do something simple like this:
foreach($cliques as $key){
$array[] = $key['clique_id'];
}
$notijfy = new Notification();
$notijfy->addCircle($array);
function addCircle($array){
$insert_string = '';
$count = 0;
foreach ($array as $k => $v){
$count++;
${$k} = mysqli_real_escape_string($this->db, $v);
$insert_string .= "(" . ${$k} . ")";
if ($count < sizeof($array)){
$insert_string .= ",";
}
}
$sql2 = "INSERT INTO notification(id_involve) VALUES $insert_string;";
$result2= $this->db->query ($sql2, $param2);
}
Your major error is that you are trying to pass ($passing an array when calling the function, but in the function itself your argument is listed as $id_involve, when you obviously need an $array variable that you are using in the function itself. I also can't understand why are you trying to add an element to the $array variable outside of foreach loop in the beginning. Doesn't make any sense.
Instead of $this->db just use your connection variable.

How to insert this array into a MYSQL database and why mine won't work?

I am trying to insert this array into my database, using an implode statement. But i am struggling to find where I am going wrong: I basically am looking to convert the two functions below to return strings:
function Titles($link) {
$str = file_get_contents($link);
if( strlen( $str )>0 ) {
preg_match_all( "/\<title\>(.*)\<\/title\>/", $str, $titles );
if (count($titles) > 1) {
return $titles[1];
}
}
return '';
}
function getMetas($link) {
$str1 = file_get_contents($link);
if (strlen($str1)>0) {
preg_match_all( '/<meta.*?name=("|\')description("|\').*?content=("|\')(.*?)("|\')/i', $str1, $description);
if (count($description) > 1) {
return $description[4];
}
}
}
$data = array();
foreach ($links as $link) {
$output = array(
"title" => Titles($link),
"link" => $link,
"description" => getMetas($link),
"keywords" => getKeywords($link)
);
if (empty($output["description"]))
{$output["description"] = getWord($link);
}
$data[] = $output;
}
print_r($data);
mysql_query("INSERT INTO scan (title, url, description, keywords) VALUES ('".implode("'),('",$data)."')");
if (!mysql_query()) {
echo "woops";
}
mysql_close($connect);
The problem is that $data is a multi-dimensional array, so after you implode, you still have arrays instead of strings in your query.
You can solve that by using implode earlier on $output as well:
$data[] = implode(',', $output);
Apart from that you should use mysql_real_escape_string on your original variables but as the mysql_* functions are being deprecated, you really should switch to prepared statements in mysqli or PDO.
Edit: Actually you need to modify both implodes:
$data[] = '"' . implode('","', $output) . '"';
...
mysql_query( "INSERT INTO scan (title, url, description, keywords)
VALUES (" . implode('),(',$data) . ")" );
Try this
$fields = array_keys($data);
$values = array_values($data);
$formatedValues = array();
foreach($values as $val)
{ $val = "'".addslashes($val)."'";
$formatedValues[] = $val;
}
$table="scan";
$sql = "INSERT INTO ".$table." (";
$sql .= implode(", ",$fields).") ";
$sql .= "VALUES( ";
$sql .= implode(", ",$formatedValues);
$sql .= ")";
mysql_query($sql) or die(mysql_error());

Concatenation of string with a specific array elements

the given code below insert data from an array to the mysql table.as its not the full code but what i want to know is available in this code. my question is that there is a field in table named "image_url" but the data in that field only have image name and i want to append http://www.xxxxxx.com at the start of every image name and the replace it with the image name in the field but i dont know how to do that plz help me out
thanks in advance
function putTest($t) {
//$c = connect();
foreach ($t as $k => $v) {
$query = "INSERT INTO test (".implode(',',array_keys($v)).") VALUES ('".implode("','",$v)."')";
//echo "<pre>";
// echo $query;
$r = mysql_query($query);
}
//mysql_close($c);
}
This snippet should do what you want:
if (isset($v['image_url'])) {
$v['image_url'] = 'http://www.xxxxxx.com/' . $v['image_url'];
}
You can concatenate strings with the dot "."!
At first... Is your application protected against SQL injection? If not you should build two methods/functions like this using mysql_real_escape_string():
function sqlSafeKey( $key){
return '`' . mysql_real_escape_string( $key) . `'`;
}
function sqlSafeValue( $value){
return "'" . mysql_real_escape_string( $value) . "'";
}
And than use array_map() to escape your values like this:
$keys = array_map( 'sqlSafeKey', array_keys( $v));
$values = array_map( 'sqlSafeValue', $v);
About your question... The matzino's answer is correct and whole loop should look like this:
function putTest($t) {
//$c = connect();
foreach ($t as $k => $v) {
$v['image_url'] = 'http://www.xxxxxx.com/' . $v['image_url'];
$keys = array_map( 'sqlSafeKey', array_keys( $v));
$values = array_map( 'sqlSafeValue', $v);
$query = "INSERT INTO test (".implode(',', $keys).
") VALUES ('".implode("','",$values)."')";
//echo "<pre>";
// echo $query;
$r = mysql_query($query);
}
//mysql_close($c);
}

PHP Implode Associative Array

So I'm trying to create a function that generates a SQL query string based on a multi dimensional array.
Example:
function createQueryString($arrayToSelect, $table, $conditionalArray) {
$queryStr = "SELECT ".implode(", ", $arrayToSelect)." FROM ".$table." WHERE ";
$queryStr = $queryStr.implode(" AND ",$conditionalArray); /*NEED HELP HERE*/
return $queryStr;
}
$columnsToSelect = array('ID','username');
$table = 'table';
$conditions = array('lastname'=>'doe','zipcode'=>'12345');
echo createQueryString($columnsToSelect, $table, $conditions); /*will result in incorrect SQL syntax*/
as you can see I need help with the 3rd line as it's currently printing
SELECT ID, username FROM table WHERE
lastname AND zipcode
but it should be printing
SELECT ID, username FROM table WHERE
lastname = 'doe' AND zipcode = '12345'
You're not actually imploding a multidimensional array. $conditions is an associative array.
Just use a foreach loop inside your function createQueryString(). Something like this should work, note it's untested.:
$terms = count($conditionalArray);
foreach ($conditionalArray as $field => $value)
{
$terms--;
$queryStr .= $field . ' = ' . $value;
if ($terms)
{
$queryStr .= ' AND ';
}
}
Note: To prevent SQL injection, the values should be escaped and/or quoted as appropriate/necessary for the DB employed. Don't just copy and paste; think!
function implodeItem(&$item, $key) // Note the &$item
{
$item = $key . "=" . $item;
}
[...]
$conditionals = array(
"foo" => "bar"
);
array_walk($conditionals, "implodeItem");
implode(' AND ', $conditionals);
Untested, but something like this should work. This way you can also check if $item is an array and use IN for those cases.
You will have to write another function to process the $conditionalArray, i.e. processing the $key => $value and handling the types, e.g. applying quotes if they're string.
Are you just dealing with = condition? What about LIKE, <, >?
Forgive me if its not too sexy !
$data = array('name'=>'xzy',
'zip'=>'3432',
'city'=>'NYK',
'state'=>'Alaska');
$x=preg_replace('/^(.*)$/e', ' "$1=\'". $data["$1"]."\'" ',array_flip($data));
$x=implode(' AND ' , $x);
So the output will be sth like :
name='xzy' AND zip='3432' AND city='NYK' AND state='Alaska'
I'd advise against automated conditionals creation.
Your case is too local, while there can be many other operators - LIKE, IN, BETWEEN, <, > etc.
Some logic including several ANDs and ORs.
The best way is manual way.
I am always doing such things this way
if (!empty($_GET['rooms'])) $w[]="rooms='".mesc($_GET['rooms'])."'";
if (!empty($_GET['space'])) $w[]="space='".mesc($_GET['space'])."'";
if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'";
Though if you still want it with this simple array, just iterate it using
foreach ($conditions as $fieldname => $value)...
and then combine these variables in the way you need. you have 2 options: make another array of this with field='value' pairs and then implode it, or just concatenate, and substr trailing AND at the end.
I use a variation of this:
function implode_assoc($glue,$sep,$arr)
{
if (empty($glue)) {$glue='; ';}
if (empty($sep)) {$sep=' = ';}
if (is_array($arr))
{
foreach ($arr as $k=>$v)
{
$str .= $k.$sep.$v.$glue;
}
return $str;
} else {
return false;
}
};
It's rough but works.
Here is a working version:
//use: implode_assoc($v,"="," / ")
//changed: argument order, when passing to function, and in function
//output: $_FILES array ... name=order_btn.jpg / type=image/jpeg / tmp_name=G:\wamp\tmp\phpBDC9.tmp / error=0 / size=0 /
function implode_assoc($arr,$glue,$sep){
$str = '';
if (empty($glue)) {$glue='; ';}
if (empty($sep)) {$sep=' = ';}
if (is_array($arr))
{
foreach ($arr as $key=>$value)
{
$str .= $key.$glue.$value.$sep;
}
return $str;
} else {
return false;
}
}
I know this is for the case of a pdo mysql type.. but what i do is build pdo wrapper methods, and in this case i do this function that helps to build the string, since we work with keys, there is no possible way to mysql inject, since i know the keys i define / accept manually.
imagine this data:
$data=array(
"name"=>$_GET["name"],
"email"=>$_GET["email"]
);
you defined utils methods...
public static function serialize_type($obj,$mode){
$d2="";
if($mode=="insert"){
$d2.=" (".implode(",",array_keys($obj)).") ";
$d2.=" VALUES(";
foreach ($obj as $key=>$item){$d2.=":".$key.",";}
$d2=rtrim($d2,",").")";}
if($mode=="update"){
foreach ($obj as $key=>$item){$d2.=$key."=:".$key.",";}
}
return rtrim($d2,",");
}
then the query bind array builder ( i could use direct array reference but lets simplify):
public static function bind_build($array){
$query_array=$array;
foreach ($query_array as $key => $value) { $query_array[":".$key] = $query_array[$key]; unset($query_array[$key]); } //auto prepair array for PDO
return $query_array; }
then you execute...
$query ="insert into table_x ".self::serialize_type( $data, "insert" );
$me->statement = #$me->dbh->prepare( $query );
$me->result=$me->statement->execute( self::bind_build($data) );
You could also go for an update easy with...
$query ="update table_x set ".self::serialize_type( $data, "update" )." where id=:id";
$me->statement = #$me->dbh->prepare( $query );
$data["id"]="123"; //add the id
$me->result=$me->statement->execute( self::bind_build($data) );
But the most important part here is the serialize_type function
Try this
function GeraSQL($funcao, $tabela, $chave, $valor, $campos) {
$SQL = '';
if ($funcao == 'UPDATE') :
//Formata SQL UPDATE
$SQL = "UPDATE $tabela SET ";
foreach ($campos as $campo => $valor) :
$SQL .= "$campo = '$valor', ";
endforeach;
$SQL = substr($SQL, 0, -2);
$SQL .= " WHERE $chave = '$valor' ";
elseif ($funcao == 'INSERT') :
//Formata SQL INSERT
$SQL = "INSERT INTO $tabela ";
$SQL .= "(" . implode(", ", array_keys($campos) ) . ")";
$SQL .= " VALUES ('" . implode("', '", $campos) . "')";
endif;
return $SQL;
}
//Use
$data = array('NAME' => 'JOHN', 'EMAIL' => 'J#GMAIL.COM');
GeraSQL('INSERT', 'Customers', 'CustID', 1000, $data);

Categories