wpdb::prepare() with use multi placeholders (args) - php

I want to query all user roles "subscriber" or "administrator"
$ARRAY = array('%subscriber%','%administrator%');
$query = $wpdb->prepare( "
SELECT *
FROM {$wpdb->usermeta}
WHERE meta_value IN %s
ORDER BY user_id
DESC LIMIT {$limit} OFFSET {$offset}
", $ARRAY );
Errors: Notice: wpdb::prepare was called incorrectly. The query only expected one placeholder, but an array of multiple placeholders was sent.
I want to use multi placeholders, how to fix? thank you!

Using LIKE operator and AND condition in place of IN() function.
You can try:
$ARRAY = array('%subscriber%','%administrator%');
$query = $wpdb->prepare( "
SELECT *
FROM {$wpdb->usermeta}
WHERE meta_value LIKE %s OR meta_value LIKE %s
ORDER BY user_id
DESC LIMIT {$limit} OFFSET {$offset}
", $ARRAY );

You need to expand the array into a string by using the implode function as it's expecting a string to be passed.
$ARRAY = array('%subscriber%','%administrator%');
$array_placeholders = implode( ', ', array_fill( 0, count( $ARRAY ), '%s' ) );
$query = $wpdb->prepare( "
SELECT *
FROM {$wpdb->usermeta}
WHERE meta_value IN ( $array_placeholders )
ORDER BY user_id
DESC LIMIT {$limit} OFFSET {$offset}
", $ARRAY );

First, I should point out a security problem with your query. You are looking for any meta value with the words "subscriber" and "administrator" in them. So with any user account on your site, I could just set my first name or bio to contain "administrator" and your query will pick me up as one. If you are looking for roles, make sure you are explicit about it:
$prefix = $wpdb->get_blog_prefix();
... "WHERE meta_key = '{$prefix}_capabilities' AND " ...
Second, although incorrect, Andrew's answer looks better, because you're not hard-coding the number of roles you're going to look for in the query itself, like in Karlo's answer. Imagine you have to query for 15 roles. The reason it's incorrect though, is because the capabilities meta value is a serialized PHP array, so you can't really use IN() there, unfortunately, but using the same idea, you could do something like this:
$prefix = $wpdb->get_blog_prefix();
$roles = array( 'subscriber', 'administrator' );
$parts = array();
foreach ( $roles as $role ) {
$parts []= $wpdb->prepare( "meta_value LIKE %s",
'%' . $wpdb->esc_like( $role ) . '%' );
}
$parts_sql = implode( " OR ", $parts );
$sql = "SELECT * FROM {$wpdb->usermeta}
WHERE meta_key = '{$prefix}capabilities'
AND ({$parts_sql})";
$wpdb->get_results( $sql, ARRAY_A );
Finally, even if you do get site administrators this way, make sure you don't allow them any actions just based on their roles, and run capability checks wherever applicable with current_user_can() or user_can().
Hope that helps.

Related

How to create multiple word search? SQL

We have made a search field where you can search for ingredients and find recipes.
For now you can only type in 1 ingredient:
if (isset($_POST['search'])) {
$searchquery = $_POST['search'];
$query = mysql_query("SELECT * FROM opskrifter WHERE id IN
(SELECT opskrifterid FROM ingredienser WHERE ing_name IN ('$searchquery'))") or die("search failed");
We want to be able to search for multiple ingredients in the same search field by seperating the ingredients with a "," or something like this.
Is there a simple way to make that happen ?
EDIT:
We tried to use explode like this without succes.
$searchTerms = explode(' ', $searchquery);
$searchTermBits = array();
foreach ($searchTerms as $term) {
if (!empty($term)) {
$searchTermBits[] = "ing_name '$term'";
}}
...
$result = mysql_query("SELECT * FROM opskrifter WHERE id IN (SELECT * FROM WHERE ".implode(' AND ', $searchTermBits)));
Thanks! :)
You could simply get the user to type in his values comma-separated, the the input would be almost in the right syntax for the query. You just have to add semicolons around the values because you search for a string in your table.
You can use PHP's str_replace()-Function:
$vals = $_POST['search'];
$valsFormatted = "'" . str_replace(",", "','", $vals) . "'";
In this code, you replace all the commas of the input with the comma plus semicolons before and behind them in orderto wrap all values of the input with semicolons. You also have to add one at the beginning and at the end of the string. Replace the first comma in the function above with the char you want your users to separate the values with.
After that, you can simply change your query to the following:
$query = "SELECT * FROM opskrifter WHERE id IN
(SELECT opskrifterid FROM ingredienser WHERE ing_name IN ('$valsFormatted'))";
Please also be informed, that your code like this is vulnerable for SQL Injections! Check out this link to learn how to prevent this.
A simple statement like this would work:
$array = implode("','",explode($_POST['search'], ","));
$query = mysql_query("SELECT * FROM opskrifter WHERE id IN (SELECT opskrifterid FROM ingredienser WHERE ing_name IN ({$array}))") or die("search failed");
First explode your search, then implode it (might not even need to do so). After that make sure the array gets used as the 'in' operator as a string/array.
For more information about this, you could read this question: PHP/MySQL using an array in WHERE clause
The working copy from my local machine was this;
$_POST['search'] = "0, 1, 2";
$array = implode ( "','", explode ( ",", $_POST['search'] ) );
$query = mysql_query("SELECT * FROM users WHERE id IN ('$array')") or die(mysql_error());
var_dump ( $array );
var_dump ( $query );
var_dump ( "SELECT * FROM users WHERE id IN ('$array')" );
var_dump ( mysql_fetch_array ( $query ) );
which actually did return users, so if we would take this example and change it to your code, it would be (the query, at least):
$query = mysql_query("SELECT * FROM opskrifter WHERE id IN (SELECT opskrifterid FROM ingredienser WHERE ing_name IN ('$array'))") or die(mysql_error());
Do take note of the changed $array variable too.
First you need to convert the text coming from the search field to array with:
$string = $_POST['search'];
$array = explode( '"' , $string);
So if you put in the search: test"hello"hi
the array will be:
1 => test,
2 => hello,
3 => hi
After that, you need to use the SQL format:
WHERE column_name IN (value1,value2,...)
So you need to change the array we have created to a string with this format:
$string = implode(',',$array);
So the echo of $string will be:
test,hello,hi
and SQL will be :
WHERE column_name IN ($string)

Warning: Missing argument 2 for wpdb::prepare() on theme functions

I upgraded my old site from wordpress version 3.4 to 4.4 recently. Suddenly I am getting a PHP warning as per below:
Warning: Missing argument 2 for wpdb::prepare(), called in ...../wp-content/themes/...functions/admin-functions.php on line 1543 and defined in .......public/wp-includes/wp-db.php on line 1246
Below are the codes for admin-functions.php:
global $wpdb;
$query = "SELECT *,count(*) AS used FROM $wpdb->postmeta WHERE meta_key = '_wp_page_template' AND meta_value = '$filename' GROUP BY meta_value";
$results = $wpdb->get_row($wpdb->prepare($query),'ARRAY_A'); // Select thrid coloumn accross
if(empty($results))
return false;
and wp-db.php
public function prepare( $query, $args ) {
if ( is_null( $query ) )
return;
You need to use placeholders and add the variable as an argument to prepare,%s is a placeholder for string value,assuming it is string from your quotes
$query = "SELECT *,count(*) AS used FROM $wpdb->postmeta
WHERE meta_key = '_wp_page_template' AND meta_value = %s
GROUP BY meta_value";
$results = $wpdb->get_row($wpdb->prepare($query,$filename),'ARRAY_A'); // Select thrid coloumn accross
I would recommend reading the documentation for $wpdb->prepare.
Prepare requires at least two arguments to be passed in. The first is the query, using placeholders (%s for a string and %d for a number), and the second is the list of variables / values to place into the query instead of the placeholders.
For your specific case, I'm demonstrating in a bit longer format so you can see clearly:
global $wpdb;
// Old query. Let's get it ready for prepare...
// $query = "SELECT *,count(*) AS used FROM $wpdb->postmeta WHERE meta_key = '_wp_page_template' AND meta_value = '$filename' GROUP BY meta_value";
// New query, ready for prepare:
$query = "SELECT *,count(*) AS used FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s GROUP BY meta_value";
// Now we pass that into prepare, along with the two values we want replaced
$wpdb->prepare($query, '_wp_page_template', $filename);
// And we execute the query...
$results = $wpdb->get_row($query,'ARRAY_A');
Note that you can use the placeholders for as many values as you want, but the idea is that you use them for any value that is user input. If the value may be posted through a form, for example, you absolutely want to use prepare on that value. Example:
$post_id = $_POST['post_id'];
$query = 'SELECT * FROM $wpdb->postmeta WHERE post_id = %d';
$query = $wpdb->prepare($query, $post_id);
Hopefully this helps!
You need to pass $meta_key as second argument.
eg:-
<?php
// set the meta_key to the appropriate custom field meta key
$meta_key = 'miles';
$allmiles = $wpdb->get_var( $wpdb->prepare(
"
SELECT sum(meta_value)
FROM $wpdb->postmeta
WHERE meta_key = %s
",
$meta_key
) );
echo "<p>Total miles is {$allmiles}</p>";
?>
so here your second second argument is $filename.
Reference :- Class Reference / wpdb

Getting a single MYSQL result where the field is an array

Wordpress database, bit stuck on this one.
I'm using the following to get the ID of the current user.
$user_ID = get_current_user_id();
This returns something like this :
15
Now I try to find the matching value of $user_ID in the field show_user_list The data in this field is stored in an array.
Which looks something like this :
a:2:{i:0;s:2:"29";i:1;s:2:"15";}
This is the query i'm running (along with a set of conditions) :
global $wpdb; $result = $wpdb->get_results( "SELECT post_id FROM wp_postmeta WHERE show_user_list IN (' . implode(',', $user_ID) . ' AND post_type = 'show' AND post_status = 'publish'" );
And then I'm trying to echo the value of the matching post_id with this :
foreach ( $result as $unique ) {
echo $unique->post_id;
}
But it's not returning anything. I know I must be making a mistake while dealing with the array but I don't know where I'm going wrong?
looks like you have stored a serialized array in the show_user_list field, so it will be a hustle to search for values into using a db query.
In the model you described, you have to select all the rows from wp_postmeta that match "post_type = 'show' AND post_status = 'publish'", then manually filter results that do not have the user id in the unserialized show_user_list field.
You might try something like :
in_array($user_ID, unserialize($row->show_user_list))
Also, I noticed multiple errors in your query: string not properly concatenated with PHP code and the right parenthesis of the IN clause not closed.
Regards,
same
EDIT
Here is how I would solve your problem providing info you have given :
$user_ID = get_current_user_id();
global $wpdb;
$results = $wpdb->get_results("SELECT post_id, show_user_list FROM wp_postmeta WHERE post_type = 'show' AND post_status = 'publish'");
$user_post_ids = array();
foreach ($results as $post) {
if (in_array($user_ID, unserialize($post->show_user_list))) {
$user_post_ids[] = $post->post_id;
}
}
Hope this helps !

When mysql WHERE clause is empty, return all rows

$randomvariable=$_GET['randomvariable'];
$search="SELECT * from objects
WHERE transactiontype='$randomvariable'
order by id DESC";
Now if $randomvariable is empty (nothing), I would like it to return all rows. Currently if it's empty it returns nothing, because it basically searches for nothing from all of the rows.
$randomvariable = ESACPE_MYSQL_STRING($_GET['randomvariable']);
$search =
"SELECT * FROM objects " .
(empty($randomvariable) ? "" : "WHERE transactiontype='$randomvariable' ") .
"ORDER BY id DESC";
Where ESCAPE_MYSQL_STRING is the relevant function for escaping strings for whatever MySQL driver you're using.
Another, more modular way:
$search = array(
"select" => "SELECT * FROM objects",
"where" => "WHERE transactiontype='$randomvariable'",
"order" => "ORDER BY id DESC"
);
if (empty($randomvariable)) {
unset($search["where"]);
}
$search = implode(' ', $search);
The nice thing about this is that you can add, remove or alter the query for any situation easily, having easy access to any part of the query.
You could also do this with CASE() in SQL, but it's somewhat cumbersome and you shouldn't expect good performance either:
SELECT * FROM objects
WHERE transactiontype LIKE
CASE WHEN '$randomvariable' = '' THEN
'%'
ELSE
'$randomvariable'
END CASE
ORDER BY id DESC
Another approach:
if ($_GET['randomvariable'] != "") {
$where = "transactiontype = " . $randomvariable;
} else {
$where = "1";
}
$search = "SELECT * from objects WHERE " . $where . " ORDER BY id DESC";
Try as below
$randomvariable=mysql_real_escape_string($_GET['randomvariable']);
$where = '';
if($randomvariable){
$where .= "WHERE transactiontype='{$randomvariable}'";
}
$search="SELECT * from objects ".$where." order by id DESC";
There are some great answers here. I have one to add for a simple solution.
I sometimes run into this issue when I don't need a WHERE clause since none of my conditions to build my WHERE clause are met. A simple trick I like to use is something like this:
$sql_statement = "SELECT * FROM ".$table_names." WHERE 1 = 1 ";
if ($array) {
foreach ($array as $key => $value){
$sql_statement .= " AND ".$key." = '".$value."' ";
}
}
This way, you don't need any tricky logic or string manipulation because 1 always equals 1 in your WHERE clause and you can keep your looped string concats the same format. You can obvious extend this concept to do more, but for the purposes of this question, this psuedocode is just a simple way to achieve the goal.
You can use the IN operator to indicate many values for the WHERE clause and include all possible values for transactiontype as the default parameter.
Split it in 2 queries:
$randomvariable = $_GET['randomvariable'];
if($randomvariable)
$search="SELECT * from objects WHERE transactiontype='$randomvariable' order by id DESC";
else
$search="SELECT * from objects order by id DESC";
If you really need to do it in SQL and not in your language, you can do this:
$search="SELECT * from objects WHERE ("" = '$randomvariable' or transactiontype='$randomvariable') order by id DESC";
This will not perform well, however, and an IF/ELSE in your language should be preferred.
Add a simple if statement checking if $randomvariable is null. If it is then change the query to return all rows.

Select using a php variable with more than one ID

I have a PHP variable that looks like this for example:
$list = "12|421|466|501|1042|"
What I wanna do is to match each number with a field in my database table.
SELECT * FROM tableName WHERE id = any of the numbers in $list
Which is the simplest way to do this?
Use this SQL:
SELECT * FROM tableName WHERE id IN (12, 431, 466, 501, 1042)
Use explode(), implode() to convert your list to a comma separated list.
Use IN like this:
"SELECT * FROM tableName WHERE id IN (".str_replace('|', ',', substr($list, 0, -1)).")"
use replace to replace | into comma and remove the last | so that you could get the string in
correct format
$list = str_replace('|',',',$list);
$query = "select * from table where ID in ($list)";
SELECT * FROM table WHERE id LIKE '%$list%'
not so sure | is a good seperator. maybe u should try setting it into an array.
guy below got it!!!
This should work:
<?php
$list = "12|421|466|501|1042|";
$items = array_filter(
array_map( 'trim', explode( "|", $list ) ),
function( $item ) {
return $item != ''; // filter empty values.
}
);
$query = 'SELECT foo FROM bar WHERE id IN ( ' . implode( ', ', $items ) . ' );';
echo $query;
if you wan the combined result you can use IN statement like:
SELECT * FROM tableName WHERE id IN ( $cat = str_replace("|", "," , $list) )
Or if you want the separate results you have to iterate through all the values.
$array = preg_split( "\\|" , $list );
foreach ($array as $value) {
SELECT * FROM tableName WHERE id = $value
}
SELECT * FROM tableName WHERE id IN ('.str_replace('|',',',$list).');
it will repalce all | with , and then you can run query with IN condition to get desired records..

Categories