Collect "where" clause array with "join" using wpdb prepare safely - mysql

I need to secure a big SQL statement has a lot of conditions in its WHERE clause so I made an array for WHERE clause using $wpdb->prepare properly BUT an error happened while join this array together as a string in the final statement.
Here is some of my code .. Is that secure enough or it may cause an SQL injection?
P.S. I try to make another $wpdb->prepare in the last get_row function but the join function made quotes before and after WHERE clause so the statement generates an error.
foreach( $args as $field => $field_value ) {
if( ! is_null( $field_value ) ) {
switch( $field ) {
case 'id': {
if( is_numeric( $field_value ) && ( intval( $field_value ) > 0 ) ) {
$where[] = $wpdb->prepare( 'tbl_names.id = %d', $field_value );
}
} break;
case 'name': {
$where[] = $wpdb->prepare( 'tbl_names.name = %s', $field_value );
} break;
}
}
}
// NOT Working
return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$tbl_names} tbl_names WHERE %s", join( ' AND ', $where ) ), ARRAY_A );
// Working Good .. BUT Is it Safe??
return $wpdb->get_row( ( "SELECT * FROM {$tbl_names} tbl_names WHERE " . join( ' AND ', $where ) ), ARRAY_A );

Unfortunately, I think this will be the only answer for a while.
$count = 0;
$query = "SELECT * FROM {$tbl_names} tbl_names";
foreach( $args as $field => $field_value ) {
if( ! is_null( $field_value ) ) {
$count++;
$query .= ( 1 == $count ) ? ' WHERE ' : ' AND ';
switch( $field ) {
case 'id': {
$query .= $wpdb->prepare( 'tbl_names.id = %d', $field_value );
} break;
case 'name': {
$query .= $wpdb->prepare( 'tbl_names.name = %s', $field_value );
} break;
}
}
}
return $wpdb->get_row( $query, ARRAY_A );

Related

DataTables warning: table id=seo_editor_product - Invalid JSON response

I use Xampp with MariaDB 10.2.7
I installed a seo module then using json and it shows me the following error.
Error: Uncaught Exception: Error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'rows' at line 1<br />Error No: 1064<br />SELECT FOUND_ROWS() as rows in C:\xampp\htdocs\store\system\library\db\mysqli.php:40
<?php
class ModelToolSeoPackageEditor extends Model {
public function __construct($registry) {
parent::__construct($registry);
if (version_compare(VERSION, '3', '>=')) {
$this->url_alias = 'seo_url';
} else {
$this->url_alias = 'url_alias';
}
}
/**
* Create the data output array for the DataTables rows
*
* #param array $columns Column information array
* #param array $data Data from the SQL get
* #return array Formatted data in a row based format
*/
public function data_output ( $columns, $data, $type = '')
{
$out = array();
for ( $i=0, $ien=count($data) ; $i<$ien ; $i++ ) {
$row = array();
for ( $j=0, $jen=count($columns) ; $j<$jen ; $j++ ) {
$column = $columns[$j];
// Is there a formatter?
if($column['db'] == 'related') {
$related = $this->db->query("SELECT pr.related_id, pd.name FROM " . DB_PREFIX . "product_related pr LEFT JOIN " . DB_PREFIX . "product_description pd ON pd.product_id = pr.related_id WHERE pr.product_id='" . $data[$i]['product_id'] . "' AND pd.language_id=" . $this->config->get('config_language_id') . " ORDER BY pd.name")->rows;
$old_related = $old_related_id = array();
foreach ($related as $rel) {
$old_related[] = $rel['name'];
$old_related_id[] = $rel['related_id'];
}
$data[$i]['related']['text'] = implode(', ', $old_related);
$data[$i]['related']['rows'] = $related;
}
if ( isset( $column['formatter'] ) ) {
$row[ $column['dt'] ] = $column['formatter']( $data[$i][ $column['db'] ], $data[$i], $type, $this );
}
else {
$row[ $column['dt'] ] = $data[$i][ $columns[$j]['db'] ];
}
}
$out[] = $row;
}
return $out;
}
/**
* Paging
*
* Construct the LIMIT clause for server-side processing SQL query
*
* #param array $request Data sent to server by DataTables
* #param array $columns Column information array
* #return string SQL limit clause
*/
public function limit ( $request, $columns )
{
$limit = '';
if ( isset($request['start']) && $request['length'] != -1 ) {
$limit = "LIMIT ".intval($request['start']).", ".intval($request['length']);
}
return $limit;
}
/**
* Ordering
*
* Construct the ORDER BY clause for server-side processing SQL query
*
* #param array $request Data sent to server by DataTables
* #param array $columns Column information array
* #return string SQL order by clause
*/
public function order ( $request, $columns, $default )
{
$order = '';
if ( isset($request['order']) && count($request['order']) ) {
$orderBy = array();
$dtColumns = self::pluck( $columns, 'dt' );
for ( $i=0, $ien=count($request['order']) ; $i<$ien ; $i++ ) {
// Convert the column index into the column data property
$columnIdx = intval($request['order'][$i]['column']);
$requestColumn = $request['columns'][$columnIdx];
$columnIdx = array_search( $requestColumn['data'], $dtColumns );
$column = $columns[ $columnIdx ];
if ( $requestColumn['orderable'] == 'true' ) {
$dir = $request['order'][$i]['dir'] === 'asc' ?
'ASC' :
'DESC';
$orderBy[] = '`'.$column['db'].'` '.$dir;
}
}
if(!implode(', ', $orderBy)) return 'ORDER BY ' . $default;
$order = 'ORDER BY '.implode(', ', $orderBy);
}
return $order;
}
/**
* Searching / Filtering
*
* Construct the WHERE clause for server-side processing SQL query.
*
* NOTE this does not match the built-in DataTables filtering which does it
* word by word on any field. It's possible to do here performance on large
* databases would be very poor
*
* #param array $request Data sent to server by DataTables
* #param array $columns Column information array
* #param array $bindings Array of values for PDO bindings, used in the
* sql_exec() function
* #return string SQL where clause
*/
public function filter ( $request, $columns, &$bindings )
{
$globalSearch = array();
$columnSearch = array();
$dtColumns = self::pluck( $columns, 'dt' );
if ( isset($request['search']) && $request['search']['value'] != '' ) {
$str = $request['search']['value'];
for ( $i=0, $ien=count($request['columns']) ; $i<$ien ; $i++ ) {
$requestColumn = $request['columns'][$i];
$columnIdx = array_search( $requestColumn['data'], $dtColumns );
$column = $columns[ $columnIdx ];
if ( $requestColumn['searchable'] == 'true' ) {
$binding = '\'%'. $this->db->escape($str) .'%\'';
$globalSearch[] = "`".$column['db']."` LIKE ".$binding;
}
}
}
// Individual column filtering
for ( $i=0, $ien=count($request['columns']) ; $i<$ien ; $i++ ) {
$requestColumn = $request['columns'][$i];
$columnIdx = array_search( $requestColumn['data'], $dtColumns );
$column = $columns[ $columnIdx ];
$str = $requestColumn['search']['value'];
if ( $requestColumn['searchable'] == 'true' &&
$str != '' ) {
$binding = '\'%'. $this->db->escape($str) .'%\'';
$columnSearch[] = "`".$column['db']."` LIKE ".$binding;
}
}
// Combine the filters into a single string
$where = '';
if ( count( $globalSearch ) ) {
$where = '('.implode(' OR ', $globalSearch).')';
}
if ( count( $columnSearch ) ) {
$where = $where === '' ?
implode(' AND ', $columnSearch) :
$where .' AND '. implode(' AND ', $columnSearch);
}
if ( $where !== '' ) {
$where = 'WHERE ' . $where;
if($this->filter_language !== false) {
$where .= " AND ";
}
} else if($this->filter_language !== false) {
$where .= " WHERE ";
}
if($this->filter_language !== false) {
$query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "language` WHERE status = '1'");
foreach ($query->rows as $result) {
if ($result['code'] == $this->config->get('config_language')) $default_lang = $result['language_id'];
}
if ($this->filter_language == $default_lang) {
$where .= " ( language_id = '" . $this->filter_language . "' OR language_id = '0' )";
} else {
$where .= " language_id = '" . $this->filter_language . "'";
}
}
return $where;
}
/**
* Perform the SQL queries needed for an server-side processing requested,
* utilising the helper functions of this class, limit(), order() and
* filter() among others. The returned array is ready to be encoded as JSON
* in response to an SSP request, or can be modified if needed before
* sending back to the client.
*
* #param array $request Data sent to server by DataTables
* #param array $sql_details SQL connection details - see sql_connect()
* #param string $table SQL table to query
* #param string $primaryKey Primary key of the table
* #param array $columns Column information array
* #return array Server-side processing response array
*/
public function simple ( $request, $type, $lang, $store, $columns )
{
$bindings = array();
$extra_select = '';
$extra_where = '';
$primaryKey = $type . '_id';
if (in_array($type, array('product', 'category', 'information'))) {
$select_cols = "`".implode("`, `", self::pluck_fields($columns, 'db'))."`";
$table = '`'.DB_PREFIX . $type . "_description` INNER JOIN `" . DB_PREFIX . $type . "` USING(" . $primaryKey . ")";
$this->filter_language = $lang;
$default_order = 'name';
// #perf: high impact
if (version_compare(VERSION, '3', '>=') || ($this->config->get('mlseo_multistore') && $this->config->get('mlseo_ml_mode'))) {
$extra_select .= ",(SELECT keyword FROM " . DB_PREFIX . $this->url_alias . " u WHERE query = CONCAT('".$type."_id=' , ".$type."_id) AND (language_id = '".(int) $lang."' OR language_id = 0) AND store_id = ".$store." LIMIT 1) AS seo_keyword";
} else if ($this->config->get('mlseo_multistore')) {
$extra_select .= ",(SELECT keyword FROM " . DB_PREFIX . $this->url_alias . " u WHERE query = CONCAT('".$type."_id=' , ".$type."_id) AND store_id = ".$store." LIMIT 1) AS seo_keyword";
} else if ($this->config->get('mlseo_ml_mode')) {
$extra_select .= ",(SELECT keyword FROM " . DB_PREFIX . $this->url_alias . " u WHERE query = CONCAT('".$type."_id=' , ".$type."_id) AND (language_id = '".(int) $lang."' OR language_id = 0) LIMIT 1) AS seo_keyword";
} else {
$extra_select .= ",(SELECT keyword FROM " . DB_PREFIX . $this->url_alias . " WHERE query = CONCAT('".$type."_id=' , ".$type."_id) LIMIT 1) AS seo_keyword";
}
if($type == 'information') {
$default_order = 'title';
}
} elseif (in_array($type, array('manufacturer'))) {
$select_cols = "`".implode("`, `", self::pluck_fields($columns, 'db'))."`";
$this->filter_language = false;
$table = DB_PREFIX . $type . ' m';
$default_order = 'name';
$extra_select = ",(SELECT keyword FROM " . DB_PREFIX . $this->url_alias . " WHERE query = CONCAT('".$type."_id=' , m.".$type."_id) LIMIT 1) AS keyword";
} elseif (in_array($type, array('common', 'special'))) {
$select_cols = "`query`, `keyword`, `".$this->url_alias."_id`";
$primaryKey = $this->url_alias.'_id';
$table = DB_PREFIX . $this->url_alias;
$default_order = 'query';
if ($this->config->get('mlseo_ml_mode')) {
$this->filter_language = $lang;
if ($type == 'common') {
$extra_where = "AND query LIKE 'route=%'";
} elseif ($type == 'special') {
$extra_where = "AND query NOT LIKE 'route=%'
AND query NOT LIKE 'product_id=%'
AND query NOT LIKE 'category_id=%'
AND query NOT LIKE 'information_id=%'
AND query NOT LIKE 'manufacturer_id=%'";
}
} else {
$this->filter_language = false;
if ($type == 'common') {
$extra_where = "AND query LIKE 'route=%'";
} elseif ($type == 'special') {
$extra_where = "AND query NOT LIKE 'route=%'
AND query NOT LIKE 'product_id=%'
AND query NOT LIKE 'category_id=%'
AND query NOT LIKE 'information_id=%'
AND query NOT LIKE 'manufacturer_id=%'";
}
}
$type = 'url_alias';
} elseif ($type == 'absolute') {
$select_cols = "`query`, `redirect`, `url_absolute_id`";
$primaryKey = 'url_absolute_id';
$table = DB_PREFIX . 'url_absolute';
$default_order = 'query';
$type = 'url_absolute';
if ($this->config->get('mlseo_ml_mode')) {
$this->filter_language = $lang;
} else {
$this->filter_language = false;
}
} elseif ($type == 'redirect') {
$select_cols = "`query`, `redirect`, `url_redirect_id`";
$this->filter_language = false;
$primaryKey = 'url_redirect_id';
$table = DB_PREFIX . 'url_redirect';
$default_order = 'query';
$type = 'url_redirect';
} elseif ($type == '404') {
$select_cols = "u.`query`, u.`count`, u.`url_404_id`, (r.query IS NOT NULL) AS has_redirect";
$this->filter_language = false;
$primaryKey = 'url_404_id';
$table = DB_PREFIX . "url_404 u LEFT JOIN " . DB_PREFIX . "url_redirect r ON (u.query = r.query OR REPLACE(u.query, '".HTTP_CATALOG."', '/') = r.query)";
$default_order = 'query';
$type = 'url_404';
} else {
$this->filter_language = false;
$table = DB_PREFIX . $type;
}
// Build the SQL query string from the request
$limit = self::limit( $request, $columns );
$order = self::order( $request, $columns, $default_order );
if ($type == 'url_404') {
$where = self::filter( $request, array(array('db' => 'u`.`query', 'dt' => 0)), $bindings );
} else {
$where = self::filter( $request, $columns, $bindings );
}
if (!$where) {
$where = 'WHERE 1';
}
// Main query to actually get the data
$data = $this->db->query("SELECT SQL_CALC_FOUND_ROWS ". $select_cols . "
" . $extra_select ."
FROM " . $table . ' '
. $where . ' '
. $extra_where . ' '
. $order . ' '
. $limit)->rows;
// Data set length after filtering
$resFilterLength = $this->db->query(
"SELECT FOUND_ROWS() as rows"
)->row;
$recordsFiltered = $resFilterLength['rows'];
// Total data set length
$resTotalLength = $this->db->query(
"SELECT COUNT(`{$primaryKey}`) AS total
FROM $table"
)->row;
$recordsTotal = $resTotalLength['total'];
/*
* Output
*/
return array(
"draw" => intval( $request['draw'] ),
"recordsTotal" => intval( $recordsTotal ),
"recordsFiltered" => intval( $recordsFiltered ),
"data" => self::data_output( $columns, $data, $type )
);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Internal methods
*/
/**
* Throw a fatal error.
*
* This writes out an error message in a JSON string which DataTables will
* see and show to the user in the browser.
*
* #param string $msg Message to send to the client
*/
public function fatal ( $msg )
{
echo json_encode( array(
"error" => $msg
) );
exit(0);
}
/**
* Create a PDO binding key which can be used for escaping variables safely
* when executing a query with sql_exec()
*
* #param array &$a Array of bindings
* #param * $val Value to bind
* #param int $type PDO field type
* #return string Bound key to be used in the SQL where this parameter
* would be used.
*/
public function bind ( &$a, $val, $type )
{
$key = ':binding_'.count( $a );
$a[] = array(
'key' => $key,
'val' => $val,
'type' => $type
);
return $key;
}
/**
* Pull a particular property from each assoc. array in a numeric array,
* returning and array of the property values from each item.
*
* #param array $a Array to get data from
* #param string $prop Property to read
* #return array Array of property values
*/
public function pluck ( $a, $prop )
{
$out = array();
for ( $i=0, $len=count($a) ; $i<$len ; $i++ ) {
$out[] = $a[$i][$prop];
}
return $out;
}
public function pluck_fields ( $a, $prop )
{
$out = array();
for ( $i=0, $len=count($a) ; $i<$len ; $i++ ) {
if($a[$i][$prop] != 'keyword' && $a[$i][$prop] != 'related')
$out[] = $a[$i][$prop];
}
return $out;
}
}

How to replace labels of json array and add information prior to the array

I'm fetching data from mysql like ...
$query = "SELECT legal_country, count(*) FROM accounts GROUP BY legal_country ORDER BY count(*) DESC";
$result = mysql_query( $query );
$data = array();
while ( $row = mysql_fetch_assoc( $result ) ) {
$data[] = $row;
}
return json_encode( $data );
Current result is:
[{"legal_country":"US","count(*)":"107865"},{"legal_country":"DE","count(*)":"44711"},{"legal_country":"SL","count(*)":"1"}]
Yet, my goal is to have the json string like:
{"map": "worldLow", "areas": [{"id": "US","value": "107865"},{"id": "DE","value": "44711"},{"id": "SL","value": "1"}]}
How can I add {"map": "worldLow", "areas": .... } prior to the array and how can I exchange the labels legal_country -> id and count(*) -> value ??
In addition, this is a working example with 'echo' ... yet I need it with the 'return' function ...
$prefix = '';
echo '{"map": "worldLow", "areas": [';
while ( $row = mysql_fetch_assoc( $result ) ) {
echo $prefix . " {\n";
echo ' "id": "' . $row['legal_country'] . '",' . "\n";
echo ' "value": "' . $row['count(*)'] . '"' . "\n";
echo " }";
$prefix = ",\n";
}
echo "\n]}";
The working example with echo looks like this:
{"map": "worldLow", "areas": [ {
"id": "US",
"value": "107865"
},
{
"id": "DE",
"value": "44711"
},
{
"id": "SL",
"value": "1"
}
]}
Create an array with the result according to your requirement.
$query = "SELECT legal_country AS `id`, count(*) AS `value`
FROM accounts
GROUP BY legal_country
ORDER BY count(*) DESC";
$dataArr = $areasArr = array();
if ($result = mysql_query( $query )) {
while ( $row = mysql_fetch_assoc( $result ) ) {
$areasArr[] = $row;
}
$dataArr = array(
"map" => "worldLow",
"areas" => $areasArr
);
}
return json_encode( $dataArr, true );
Also use mysqli_* functions instead of mysql_*
You have to change slightly your query and $data before using json_encode
$query = "SELECT legal_country as `id`, count(*) as `value` FROM accounts GROUP BY legal_country ORDER BY count(*) DESC";
$result = mysql_query( $query );
$json= array();
$area = array();
while ( $row = mysql_fetch_assoc( $result ) ) {
$area[] = $row;
}
$data = array(
'map' => 'worldlow',
'area' => $area,
);
return json_encode( $data );

Database Query - Order by two values (Nested Order)

I want to order results by two database values: rating_full followed by rating_count
Currently, Im ordering by the highest rating_full. it works fine.
$sql .= " LEFT JOIN {$wpdb->postmeta} rating ON ({$wpdb->posts}.ID = rating.post_id AND rating.meta_key IN ('rating_full'))";
…
…
$sql = "cast(rating.meta_value as decimal(10,2)) {$order}";
……
The first line of code, part of the SELECT statement, retrieves the rating_full part
The second line of code is the ORDER BY part, which currently just uses the rating_count
As far as I can tell rating.meta_value referred to in the second line of code is the rating_full value
I'm trying to get it to ORDER BY rating_full, rating_count
I'm not sure how to modify the first line so that I can achieve this.
Thanks
FULL CODE:
<?php
// Sorting
add_filter('posts_join', 'directorySortingJoin',10,2);
function directorySortingJoin($join, $query) {
global $wpdb, $aThemeOptions;
if ($query->is_main_query() && !$query->is_admin && ((isset($_GET['dir-search'])) || (isset($query->query_vars["a-dir-item-category"])) || (isset($query->query_vars["a-dir-item-location"])))) {
$sql = "";
// default ordering
$orderby = (isset($aThemeOptions->directory->defaultOrderby)) ? $aThemeOptions->directory->defaultOrderby : 'post_date';
// get from get parameters
if (!empty($_GET['orderby'])) {
$orderby = $_GET['orderby'];
}
if ($orderby == 'rating') {
$sql .= " LEFT JOIN {$wpdb->postmeta} rating ON ({$wpdb->posts}.ID = rating.post_id AND rating.meta_key IN ('rating_full'))";
//$sql .= " LEFT JOIN {$wpdb->postmeta} rating ON (wp_posts.ID = rating.post_id AND rating.meta_key IN ('rating_full')) LEFT JOIN {$wpdb->postmeta} count ON ({$wpdb->posts} = count.post_id AND count.meta_key IN ('rating_count'))";
}
if ($orderby == 'packages') {
directorySaveUserPackagesToDb();
$sql .= " LEFT JOIN {$wpdb->usermeta} packages ON ({$wpdb->posts}.post_author = packages.user_id AND packages.meta_key IN ('dir_package'))";
}
if (isset($aThemeOptions->directory->showFeaturedItemsFirst)) {
$sql .= " LEFT JOIN {$wpdb->postmeta} featured ON ({$wpdb->posts}.ID = featured.post_id AND featured.meta_key IN ('dir_featured'))";
}
$join .= $sql;
//echo $join;
}
return $join;
}
add_filter('posts_orderby', 'directorySortingOrderby',10,2);
function directorySortingOrderby($orderby, $query) {
global $wpdb, $aThemeOptions;
if ($query->is_main_query() && !$query->is_admin && ((isset($_GET['dir-search'])) || (isset($query->query_vars["a-dir-item-category"])) || (isset($query->query_vars["a-dir-item-location"])))) {
$sql = "";
// default ordering
$orderby = (isset($aThemeOptions->directory->defaultOrderby)) ? $aThemeOptions->directory->defaultOrderby : 'post_date';
$order = (isset($aThemeOptions->directory->defaultOrder)) ? $aThemeOptions->directory->defaultOrder : 'DESC';
// get from get parameters
if (!empty($_GET['orderby'])) {
$orderby = $_GET['orderby'];
}
if (!empty($_GET['order'])) {
$order = $_GET['order'];
}
if ($orderby == 'rating') {
if (isset($aThemeOptions->directory->showFeaturedItemsFirst)) {
$sql = "featured.meta_value DESC, convert(rating.meta_value, decimal) {$order}";
} else {
//$sql = "convert(rating.meta_value, decimal) {$order}";
$sql = "cast(rating.meta_value as decimal(10,2)) {$order}";
//$sql = "cast(rating.meta_value as decimal(10,2)) {$order}, count.meta_value {$order}";
}
} else if ($orderby == 'packages') {
if (isset($aThemeOptions->directory->showFeaturedItemsFirst)) {
$sql = "featured.meta_value DESC, packages.meta_value {$order}";
} else {
$sql = "packages.meta_value {$order}";
}
} else {
if (isset($aThemeOptions->directory->showFeaturedItemsFirst)) {
$sql = "featured.meta_value DESC, {$wpdb->posts}.{$orderby} {$order}";
}
}
$orderby = $sql;
//echo $orderby;
}
return $orderby;
}
// Save directory packages for sorting
function directorySaveUserPackagesToDb() {
$users = get_users();
// capabilities list
$roles = array(
'administrator' => 10,
'directory_5' => 9,
'directory_4' => 8,
'directory_3' => 7,
'directory_2' => 6,
'directory_1' => 5,
'editor' => 4,
'author' => 3,
'contributor' => 2,
'subscriber' => 1
);
foreach ($users as $user) {
if (isset($user->roles[0])) {
if (array_key_exists($user->roles[0], $roles)) {
update_user_meta($user->ID, 'dir_package', $roles[$user->roles[0]]);
} else {
update_user_meta($user->ID, 'dir_package', 0);
}
}
}
}
The MySQL documentation for ORDER BY should be helpful here, particularly the last paragraph on sorting multiple columns.
Your ORDER BY should be something like
ORDER BY rating_full DESC, rating_count DESC

In woocommerce, what determines the order_item_id?

I've got a webshop running with woocommerce. The webshop has products with variations. When I add the same product with the same variation, I want it to generate a new order_item_id, in stead of combining the two under the same order_item_id.
I can't seem to find the bit of code that is responsible for generating the order_item_id.
Could anyone tell me where this bit of code is located?
My goal is to make each product unique in stead of stacking items that are the same.
-EDIT
Ok, so I've narrowed it down to class-wc-cart.php
On line 782 the product ID is generated with the following code:
public function generate_cart_id( $product_id, $variation_id = '', $variation = '', $cart_item_data = array() ) {
$id_parts = array( $product_id );
if ( $variation_id ) $id_parts[] = $variation_id;
if ( is_array( $variation ) ) {
$variation_key = '';
foreach ( $variation as $key => $value ) {
$variation_key .= trim( $key ) . trim( $value );
}
$id_parts[] = $variation_key;
}
if ( is_array( $cart_item_data ) && ! empty( $cart_item_data ) ) {
$cart_item_data_key = '';
foreach ( $cart_item_data as $key => $value ) {
if ( is_array( $value ) ) $value = http_build_query( $value );
$cart_item_data_key .= trim($key) . trim($value);
}
$id_parts[] = $cart_item_data_key;
}
return md5( implode( '_', $id_parts ) );
}
Now all that is left is to edit in a unique ID via a timestamp or something.
Will keep you guys posted.
Solved it.
Open class-wc-cart.php, and add a timestamp, or anything else that is unique to $id_parts.
Just like in the following code:
public function generate_cart_id( $product_id, $variation_id = '', $variation = '', $cart_item_data = array() ) {
$id_parts = array( $product_id );
if ( $variation_id ) $id_parts[] = $variation_id.time();
if ( is_array( $variation ) ) {
$variation_key = '';
foreach ( $variation as $key => $value ) {
$variation_key .= trim( $key ) . trim( $value );
}
$id_parts[] = $variation_key.time();
}
if ( is_array( $cart_item_data ) && ! empty( $cart_item_data ) ) {
$cart_item_data_key = '';
foreach ( $cart_item_data as $key => $value ) {
if ( is_array( $value ) ) $value = http_build_query( $value );
$cart_item_data_key .= trim($key) . trim($value);
}
$id_parts[] = $cart_item_data_key.time();
}
return md5( implode( '_', $id_parts ) );
}

A duplicate key is inserted instead of updated in a SQL INSERT / UPDATE query

I have the below function in Wordpress.
However, the meta key product_price is duplicated each time a post is updated.
Like this image
Is there any way to prevent this?
function do_my_stuff($post_ID) {
global $post,$wpdb;
$tablename="wp_cart66_products";
if($post->post_type == "post" && strlen( get_post_meta($post_ID, 'price', true))>0 )
{
$id = $wpdb->get_var("SELECT id FROM ".$tablename." WHERE id=".$post_ID);
$cny = get_post_meta($post->ID, 'price', true);
/*Shipping rate */
if( $cny < 50 )
$shipping = 14.97;
else if( $cny >= 50 && $cny < 200 )
$shipping = 22.59;
else if( $cny >= 200 && $cny < 250 )
$shipping = 24.59;
else if( $cny >= 250 && $cny < 300 )
$shipping = 26.60;
else if( $cny >= 300 )
$shipping = 29.27;
/*Exchange rate CNY to EURO */
$cny_to_euro = 0.124;
$euro = $cny * $cny_to_euro ;
$price = $euro + $shipping;
$price = number_format($price,2);
$data=array(
'id'=>$post_ID,
'item_number'=>get_post_meta($post->ID, 'scode', true),
'name'=>$post->post_title,
'price'=>$price,
'options_1'=>get_post_meta($post->ID, 'variations', true),
'shipped'=>'1',
);
$where = array("id" => $post_ID);
// Possible format values: %s as string; %d as decimal number; and %f as float.
$format=array( '%d', '%s', '%s', '%s', '%s', '%d');
$where_format = array( '%d' );
if($id>0){
// update
$wpdb->update( $tablename,$data, $where, $format, $where_format);
}else{
// insert
$wpdb->insert( $tablename,$data,$format);
}
AddMetaPrice ( $post_ID ) ;
}
return $post_ID;
}
function AddMetaPrice ( $post_ID )
{
// init
global $post,$wpdb;
// We add the wordpress post ID and price to a meta tag
$metaKey = "product_price" ;
$tableName = "wp_cart66_products" ;
$metaQuery = "SELECT price FROM $tableName WHERE id='$post_ID'" ;
$metaValue = $wpdb->get_var( $metaQuery ) ;
$tableName = "wp_postmeta" ;
// Do we have this post already?
if ( $id > 0 )
{
// this already exists. we only need to update
// $metaQuery = "UPDATE $tableName SET meta_value='$metaValue' WHERE meta_key='$metaKey' AND post_id='$id'" ; // we use wordpress's method instead
$data = array ( "meta_value" => $metaValue ) ;
$where = array ( "meta_key" => $metaKey, "post_id" => $post_ID ) ;
$wpdb->update( $tableName, $data, $where ) ;
}
else
{
// this is not created, we need to create it now (insert)
// $metaQuery = "INSERT INTO $tableName (post_id, meta_key, meta_value) VALUES ('$id', '$metaKey', '$metaValue')" ; // we use wordpress's method instead
$data = array ( "post_id" => $post_ID, "meta_key" => $metaKey, "meta_value" => $metaValue ) ;
$wpdb->insert( $tableName, $data ) ;
}
}
add_action('publish_post', 'do_my_stuff');
I've read here that a ON DUPLICATE KEY statement exists. But i'm not sure if it can be implemented in the above code.
Try replacing that piece of code:
if ( $metaValue !== NULL )
{
update_post_meta($post_ID, $metaKey, $metaValue); // use built-in function instead
}
where it was:
// Do we have this post already?
if ( $id > 0 )
{
// this already exists. we only need to update
// $metaQuery = "UPDATE $tableName SET meta_value='$metaValue' WHERE meta_key='$metaKey' AND post_id='$id'" ; // we use wordpress's method instead
$data = array ( "meta_value" => $metaValue ) ;
$where = array ( "meta_key" => $metaKey, "post_id" => $post_ID ) ;
$wpdb->update( $tableName, $data, $where ) ;
}