Automatically add parameters to get_results() - php

I want to call the method $wpdb->get_results(),
and I need to add parameters (WHERE clauses) to the SQL query.
The thing is, I want to build a generic function that will receive a parameters object and will generate an SQL query which includes my parameters.
So, for example, if this is my base query:
$taxis = $wpdb->get_results("SELECT * FROM $table", ARRAY_A); // base query
And these are my parameters:
$params = array(
'color' => 'blue',
'model' => 'ford'
);
It will call get_results with the following query:
SELECT * FROM $table WHERE color='blue' AND model='ford';
Is there a way to build a function that will have this behaviour?
*NOTES:
I don't know how many parameters are going to be in the parameters object nor which parameters they will be (that is why it has to be generic).
I do know, (in my case) that the only SQL CLAUSES I am going to need are WHERE =.

See the code comments for an explanation on what is happening in each step:
<?php
// make sure this doesn't come from user input, as it is inserted into the query directly
$tableName = $wpdb->prefix . "yourtable";
$columnWhitelist = array( 'color', 'model' );
$params = array(
'color' => 'blue',
'model' => 'ford'
);
// filter param keys by allowed column names defined in $columnWhitelist
$params = array_filter(
$params,
function ( $key ) use ( $columnWhitelist ) {
return in_array( $key, $columnWhitelist );
},
ARRAY_FILTER_USE_KEY
);
$whereClauseParts = array();
// put column/value pairs of $params into $placeholderValues array
$placeholderValues = array();
foreach ( $params as $column => $value) {
$whereClauseParts[] = "`$column` = %s";
$placeholderValues[] = $value;
}
// put together the WHERE clause with placeholders
$whereClause = implode(' AND ', $whereClauseParts );
// put together the whole query tring
$queryString = "SELECT * FROM `$tableName` WHERE " . $whereClause;
// you can use this to see what your prepared query will roughly look like,
// but with missing single quotes around the values
// die( vsprintf(
// $queryString,
// $placeholderValues
// ) );
$wpdb->get_results(
$wpdb->prepare(
$queryString,
$placeholderValues
)
);

Related

Update column of current row

I'm looping through contacts, pulling out specific data then want to update a particular column on the current row. How can I do that?
The line is at the very bottom, I just want to change it from a 0 to 1.
require('../mailin.php');
$mailin = new Mailin("https://api.sendinblue.com/v2.0","Your access key");
$customer_data = $wpdb->get_results("SELECT * FROM imp_customer_log WHERE updated_in_sib = '0'");
foreach( $customer_data as $customer ) {
$customer_id = $customer[0];
$customer_event = $customer[1];
$customer_data = $customer[2];
$customer_sib = $customer[3];
$user = get_user_by( 'id', $customer_id );
$user_email = $user->user_email;
$data = array(
"email" => $user_email,
"attributes" => array(
$customer_event => $customer_data,
),
);
var_dump($mailin->create_update_user($data));
// Change updated_in_sib to 1 for current row
}
i assume that this is a wordpress. you can use the update function in wpdb something look like this.
$wpdb->update('imp_customer_log', ['customer_sib' => 1], ['id' => $customer_id]);
you can check the it on documentation. https://codex.wordpress.org/Class_Reference/wpdb#UPDATE_rows
for php (not wordpress)
$sql = "UPDATE imp_customer_log SET customer_sib=1 WHERE id=" . $customer_id;
$connection->query($sql);
I think you're using Wordpress. Take a look at these wordpress docs for update, used just like get_results() in your question.
https://developer.wordpress.org/reference/classes/wpdb/update/
$wpdb->update( string $table, array $data, array $where, array|string $format = null, array|string $where_format = null )

Laravel mysql json data selecting

I got a database running on mysql 5.7.11 and has a lot of data into a table with JSON column. I got a raw query like this one.
select * from `note`
where `note_structure_id` = 3
and JSON_EXTRACT(LCASE(data), '$._letter') = 'a'
and JSON_EXTRACT(data, '$._number') = 1
Running in sequel pro or phpmyadmin it gives me a result what i expect;
{"_letter": "A", "_number": 1}
The same query i build with laravel it gives me a result of nothing. An empty array. My code to building the same query i got this kind of code.
$result = \DB::table( 'note' )->where( 'note_structure_id', $structureId );
if( is_array( $query ) ) {
if( isset( $query['where'] ) ) {
foreach( $query['where'] AS $field => $value ) {
if( is_numeric( $value ) ) {
$result = $result->where( \DB::raw( "JSON_EXTRACT(data, '$._{$field}')" ), '=', $value );
}
else {
$result = $result->where( \DB::raw( "JSON_EXTRACT(LCASE(data), '$._{$field}')" ), '=', strtolower( $value ) );
}
}
}
}
dd($result->get());
Did anyone knows what i did wrong with my code or something. I try everything what's possible for fixing it but without result.
Thanks!
Saw you fixed it, but why not just do as a raw query, in a more traditional way. Your code is not very readable at the moment.
$results = DB::select('select * from `note` where `note_structure_id` = :id and JSON_EXTRACT(LCASE(data), `$._letter`) = :a and JSON_EXTRACT(data, `$._number`) = :number', ['id' => 3, 'a' => 'a', 'number' => 1]);

Get the next element in an array (PHP)

I have two models set up for an array. Basically, what I want to achieve is to get the first next entry from the database based on the order ID I have set up.
So, I send the ID 4, I find the entry with the ID 4 which has the order ID 15. Then I want to get the first next item after it, which should have the order ID 16. I tried incrementing with +1 after the order ID, but it just doesn't work.
With my current code I get the same array twice.
function first($id_domain) {
$this->db->select ( '*' );
$this->db->from ( 'domains' );
$this->db->where ( 'id_domain', $id_domain);
$result = $this->db->get ();
return $result->result_array ();
}
function second ($result) {
$this->db->select ( '*' );
$this->db->from ( 'domains' );
$this->db->where ( 'order', $result[0]['order'] + 1 ); //code that should get me the next entry, but doesn't work...
$this->db->where ( 'parent', $result[0]['parent'] );
$result2 = $this->db->get ();
return $result2->result_array ();
}
The problem is not due to your code, but it may be due to the records in the database: either they are non-existing for that specific condition or your matching is not entirely correct.
If you are using CodeIgniter I suggest you to alter your second function to this:
function second($result) {
$whereConds = array(
'order' => intval($result[0]['order'] + 1),
'parent'=> $result[0]['parent']
);
//if you don't have firephp print/echo/var_dump as you normally would
$this->firephp->log($whereConds);
$query = $this->db->get_where('domains', $whereConds);
$this->firephp->log($this->db->last_query());
if ($query->num_rows() <= 0) {
$this->firephp->log('No results');
return array();
} else {
return $query->result_array();
}
}
This way you can track the problem accurately.
Btw, you can also use the $offset parameter in the get_where to get the next id (perhaps just with the parent condition, since you know they are ordered sequentially):
$limit=1;
$offset=intval($result[0]['order'] + 1);//adjust depending on the parent condition below: it can be just an integer such as 2
$whereConds = array(
'parent'=> $result[0]['parent']
);
$query = $this->db->get_where('domains', $whereConds, $limit, $offset);

Checking for Unique Key and updating row or creating a new one WordPress

I have a draggable div in which the position is being saved to database with user_id set to unique. How would I check if user_id exists.. and if it does, update the other 3 columns.. If it does not exist, insert new row. I have read to use "ON DUPLICATE KEY UPDATE", but having a hard time implementing it. Any thoughts?
As explained by #N.B. - Working solution
global $wpdb;
$_POST['user_id'];
$_POST['div_id'];
$_POST['x_pos'];
$_POST['y_pos'];
$user_id = $_POST['user_id'];
$div_id = $_POST['div_id'];
$x_pos = $_POST['x_pos'];
$y_pos = $_POST['y_pos'];
$wpdb->query($wpdb->prepare(" INSERT INTO coords
(user_id, div_id, x_pos, y_pos)
VALUES (%d, %s, %d, %d)
ON DUPLICATE KEY UPDATE
x_pos = VALUES(x_pos), y_pos = VALUES(y_pos)",
$user_id,
$div_id,
$x_pos,
$y_pos
));
As #N.B. pointed out in the comments, while the first method I submitted works, it is open to race conditions. I'd like to thank him for pointing that out. Here is that first solution with race conditions:
$user_exists = $wpdb->get_var(
$wpdb->prepare("SELECT user_id FROM coords WHERE user_id = %d", $user_id)
);
if($user_exists) {
// Do Update
}
else {
// Do Insert
}
These race conditions are astronomical, as an insert must finish executing in the time between the first query returning and the next insert query. But the race condition exists none-the-less, and therefore could happen at some point in time.
However your database is still safe. When it occurs it won't duplicate the data, but rather it will throw a wpdb error that a unique key already exists and the insert will silently fail (it won't terminate the script and it won't output the error unless error reporting is turned on).
WordPress database error: [Duplicate entry '3' for key 'PRIMARY']
Amazingly, the above technique is used in the Wordpress core and by countless plugin and theme authors, and I could only find two instances of 'ON DUPLICATE' being used correctly in the Wordpress core. So a large chunk of the internet runs with multiple instances of this race condition seemingly just fine, just to give you an idea of the astronomical chance we're talking about.
Regardless of the chance, to use it is bad practice. As N.B. commented, the database should worry about the data and not PHP.
The Real Solution
Wordpress, for whatever reason, does not have an 'INSERT ON DUPLICATE UPDATE' function, which means you have to either write up a query each time with $wpdb->query or build your own function to handle it. I went with writing a function because writing wpdb->query each time is a pain and brings the user one layer closer to accidental mysql injection. Also development speed.
/**
* Insert on Duplicate Key Update.
*
* Wordpress does not have an 'insert on duplicate key update' function, which
* forces user's to create their own or write standard queries. As writing
* queries is annoying and open to mysql injection via human error, this function
* automates this custom query in an indentical fashion to the core wpdb functions.
* Source: http://stackoverflow.com/a/31150317/4248167
*
* #global wpdb $wpdb
* #param string $table The table you wish to update.
* #param array $data The row you wish to update or insert, using a field => value associative array.
* #param array $where The unique keys that you want to update at or have inserted, also a field => value array.
* #param array $data_formats Wordpress formatting array for the data. Will default to %s for every field.
* #param array $where_formats Wordpress formatting array for the where. Will default to %s for every field.
* #return boolean True if successfully inserted or updated the row.
*/
function insertOrUpdate($table, $data, $where, $data_formats = array(), $where_formats = array())
{
if(!empty($data) && !empty($where)) {
global $wpdb;
// Data Formats - making sure they match up with the data.
$default_data_format = (isset($data_formats[0])) ? $data_formats[0] : '%s';
$data_formats = array_pad($data_formats, count($data), $default_data_format);
$data_formats = array_splice($data_formats, 0, count($data));
// Where Formats - making sure they match up with the where data.
$default_where_format = (isset($where_formats[0])) ? $where_formats[0] : '%s';
$where_formats = array_pad($where_formats, count($where), $default_where_format);
$where_formats = array_splice($where_formats, 0, count($where));
// Get Fields
$data_fields = array_keys($data);
$where_fields = array_keys($where);
// Create Query
$query =
"INSERT INTO $table" .
" (" . implode(', ', array_merge($data_fields, $where_fields)) . ")" .
" VALUES(" . implode(', ', array_merge($data_formats, $where_formats)) . ")" .
" ON DUPLICATE KEY UPDATE";
// Compile update fields and add to query
$field_strings = array();
foreach($data_fields as $index => $data_field) {
$field_strings[] = " $data_field = " . $data_formats[$index];
}
$query .= implode(', ', $field_strings);
// Put it all together - returns true on successful update or insert
// and false on failure or if the row already matches the data.
return !!$wpdb->query(
$wpdb->prepare(
$query,
array_merge(
array_merge(
array_values($data),
array_values($where)
),
array_values($data)
)
)
);
}
return false;
}
To use it, you simply enter parameters just like you would with a $wpdb->update function call.
insertOrUpdate(
'testing_table',
array('column_a' => 'hello', 'column_b' => 'world'),
array('id' => 3),
array('%s', '%s'),
array('%d')
);
In your case, it would be:
insertOrUpdate(
'coords',
array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos),
array('user_id' => $user_id),
array('%d', '%d', '%d'),
array('%d')
);
Or you can just use a default formatting:
insertOrUpdate(
'coords',
array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos),
array('user_id' => $user_id),
array('%d')
);
Or you can ignore the formatting which will default to formatting as strings:
insertOrUpdate(
'coords',
array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos),
array('user_id' => $user_id)
);
If you find any issues just let me know.

Add to a multi-dimensional array php

I have an array like this:
$where = array(
'product_id' => $product_id,
'item_id' => $item_id
);
I want to add to this array, based on a condition, so I could do
if($condition){
$where = array()
}else{
$where = array()
}
And repeat the original contents twice, but ideally, I'd like to do like an array_push(array('id' => $id), $where);
Thanks!
you may add something to your array in the following way:
$where['mykey'] ='myvalue';
Simply add it to your array by specifying the index and the value.
if($condition){
$where['id'] = $id;
}else{
$where['other'] = $other;
}

Categories