I write my own query to wordpress database and I'm stuck. I have many (5) custom fields like:
town
price
size
... etc
and in search.php I have:
$querystr = "
SELECT wposts.*
FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta
WHERE wposts.ID = wpostmeta.post_id ";
$town = addslashes($_GET['town']);
if($town!=''){
$querystr .= " AND wpostmeta.meta_key = 'town' AND wpostmeta.meta_value = '".$town."'";
}
$mo = addslashes($_GET['mo']);
if($mo!='' && preg_match("/^\d+$/", $mo)){
$querystr .= " AND wpostmeta.meta_key = 'price' AND wpostmeta.meta_value > '".$mo."'";
}
$md = addslashes($_GET['md']);
if($md!='' && preg_match("/^\d+$/", $md)){
$querystr .= " AND wpostmeta.meta_key = 'price' AND wpostmeta.meta_value < '".$md."'";
}
$querystr .= " AND wposts.post_status = 'publish' AND wposts.post_type = 'post'";
$pageposts = $wpdb->get_results($querystr, OBJECT);
but this does not work. If I use only one condition:
$querystr = "SELECT wposts.* FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta WHERE wposts.ID = wpostmeta.post_id ";
$town = addslashes($_GET['town']);
if($town!=''){
$querystr .= " AND wpostmeta.meta_key = 'town' AND wpostmeta.meta_value = '".$town."'";
}
$querystr .= " AND wposts.post_status = 'publish' AND wposts.post_type = 'post'";
$pageposts = $wpdb->get_results($querystr, OBJECT);
then it will work. What I'm doing wrong?
The select expression doesn't make sense as a whole because you have contradictive where conditions. It also doesn't make sense in relation to how relational databases work. You want to match two unique rows that share the same column names in one query, this is not possible without using techniques like subqueries.
Consider that all expression parts must be true and you got something like this:
SELECT wposts.*
FROM wp_posts wposts, wp_postmeta wpostmeta
WHERE wposts.ID = wpostmeta.post_id
AND wpostmeta.meta_key = 'town' AND wpostmeta.meta_value = 'My town'
AND wpostmeta.meta_key = 'price' AND wpostmeta.meta_value > 500
Here you say meta_key equals "town" AND meta_value equals "My town". That makes sense, but when you also say meta_key also equals "price" AND meta_value is also greater than 500. The expression will never be true and regardless the parser also has no way of grouping together the two different condition sets.
WP_Query
If possible I suggest that you use the WP_Query class instead of directly querying the database. This wrapper greatly simplifies your code and makes it easier to maintain. Note that the code requires WordPress >=3.1 as it uses the meta_query option.
Your query can be written like this:
<?php
// The arguments that defines the query
$args = array(
'post_status' => 'publish',
'post_type' => 'post'
);
// We define the meta/custom field conditions
$meta_query = array();
// PS: No need to slash the values, WordPress will do that for us
$town = $_GET['town'];
$mo = (int) $_GET['mo']; // Simple sanitizment, implement your own as see fit
$md = (int) $_GET['md']; // Simple sanitizment, implement your own as see fit
if ( $town ) {
$meta_query[] = array(
'key' => 'town',
'value' => $town
);
}
if ( $mo ) {
$meta_query[] = array(
'key' => 'price',
'value' => $mo,
'type' => 'NUMERIC',
'compare' => '>'
);
}
if ( $md ) {
$meta_query[] = array(
'key' => 'price',
'value' => $md,
'type' => 'NUMERIC',
'compare' => '<'
);
}
if ( $meta_query ) {
// Add the meta_query conditions to the arguments array
$meta_query['relation'] = 'AND';
$args['meta_query'] = $meta_query;
}
// Create WP_Query object with the arguments
$query = new WP_Query( $args );
// Fetch the posts
$posts = $query->get_posts();
// Voila!
?>
Related
I am trying to delete WordPress post after X Days.
And i am using this code.
$daystogo = 30;
$sql =
"UPDATE {$wpdb->posts}
SET post_status = 'trash'
WHERE (post_type = 'post' AND post_status = 'publish')
AND DATEDIFF(NOW(), post_date) > %d";
$wpdb->query($wpdb->prepare( $sql, $daystogo ));
But i want to exclude some post on meta key.
Like i don't want to delete that post which have FEATURD POST VALUE IS 1
'meta_query' => array(
array(
'key' => 'featured_post',
'value' => '1',
'compare' => '=='
)
),
Any way to add this condition in query??
Thanks
Here is mysql code for you, you can use MySQL "IN()" function for this:
$sql =
"UPDATE {$wpdb->posts}
SET post_status = 'trash'
WHERE (post_type = 'post' AND post_status = 'publish')
AND ID not in (select post_id from {$wpdb->postmeta} where
meta_key='featured_post' and meta_value='1' )
AND DATEDIFF(NOW(), post_date) > %d";
I only want to show posts which do not have the term 'brand-slug' for the taxonomy 'product-brand'.
My current query doesn't apply the filter:
SELECT DISTINCT * FROM $wpdb->posts AS p
LEFT JOIN $wpdb->postmeta AS meta ON p.ID = meta.post_id
LEFT JOIN $wpdb->term_relationships AS rel ON rel.object_id = p.ID
LEFT JOIN $wpdb->term_taxonomy AS tax ON tax.term_taxonomy_id = rel.term_taxonomy_id
LEFT JOIN $wpdb->terms AS term ON tax.term_id = term.term_id
WHERE 1=1
AND p.post_type = 'product'
AND p.post_status = 'publish'
AND p.post_title LIKE '%$trimmed%' OR (meta.meta_key = 'product_model' AND meta.meta_value LIKE '%$trimmed%')
AND (tax.taxonomy = 'product-brand' AND term.slug NOT IN ('$protected'))
Neither taxonomy or slug conditionals seem to be working in the above query.
Any help is appreciated!
Notes:
It looks like you're not using $wpdb->prepare(), so you risk SQL injections.
I also think you're missing parentheses around the relevant OR parts, so you don't end up displaying drafts, for example.
Alternative:
Instead of writing an hardcoded SQL query, we should be able to use the WP_Query class, with some modifications through hooks/filters.
Here's an example (PHP 5.4+):
$args = [
'_meta_or_like_title' => $trimmed, // Our new custom argument!
'post_type' => 'product',
'post_status' => 'publish',
'meta_query' => [
[
'key' => 'product_model',
'value' => $trimmed, // Your meta value
'compare' => 'LIKE'
]
],
'tax_query' => [
[
'taxonomy' => 'product-brand',
'field' => 'slug',
'terms' => $protected, // Your terms array
'operator' => 'NOT IN'
]
]
];
where the custom _meta_or_like_title argument is supported by a slightly modified plugin I wrote for another question here.
Plugin:
<?php
/**
* Plugin Name: Meta OR LIKE Title query in WP_Query
* Description: Activated through the '_meta_or_like_title' argument of WP_Query
* Plugin URI: http://stackoverflow.com/a/31241416/2078474
* Plugin Author: Birgir Erlendsson (birgire)
* Version: 0.0.1
*/
add_action( 'pre_get_posts', function( $q )
{
if( $title = $q->get( '_meta_or_like_title' ) )
{
add_filter( 'get_meta_sql', function( $sql ) use ( $title )
{
global $wpdb;
// Only run once:
static $nr = 0;
if( 0 != $nr++ ) return $sql;
// Modify WHERE part:
$sql['where'] = sprintf(
" AND ( %s OR %s ) ",
$wpdb->prepare(
"{$wpdb->posts}.post_title LIKE '%%%s%%'",
$wpdb->esc_like( $title )
),
mb_substr( $sql['where'], 5, mb_strlen( $sql['where'] ) )
);
return $sql;
});
}
}, PHP_INT_MAX );
I have a custom events post type in WordPress (with recurring events being saved as children to the main event) I have a few custom taxonomies set up which only saves the custom taxonomy data to the parent event post,
but what I am trying to figure out is how to filter the results (including the recurring children posts) by the custom taxonomies.
At current I have something similar to:
$args = array(
'post_type' => 'incsub_event',
'posts_per_page' => 50,
'post_status' => array( 'recurrent', 'publish'),
'meta_query' => array(
array(
'key' => 'incsub_event_start',
'value' => array( $date_1, $date_2 ),
'type' => 'DATETIME',
'compare' => 'BETWEEN'
),
array(
'key' => 'incsub_event_fee',
'value' => array( '10', '1000' ),
'type' => 'NUMERIC',
'compare' => 'BETWEEN'
),
array(
'key' => 'incsub_event_status',
'value' => 'open',
'type' => 'BINARY',
'compare' => '='
),
),
'tax_query' => array(
array(
'taxonomy' => 'location',
'field' => 'slug',
'terms' => 'uk',
),
),
'order' => 'ASC',
'orderby' => 'meta_value',
'meta_key' => 'incsub_event_start'
);
But it will only return the parent posts not the children, I would be grateful to anyone who could shed some help on the matter?
I had this issue a while ago and the solution isn't pretty. As far as I know, Wordpress's get_posts function doesn't support nested queries where 'parent has x-attribute'. To access the child posts you're going to have to roll your own get_posts-style function, using $wpdb to access the database. I've created a function for you that will work as the basis for this kind of query. There are probably some improvements and additions that need to be made to include other common search variables, but for your current query it will get the job done.
function customGetPosts($args) {
global $wpdb;
$wpdb->show_errors();
extract($args);
$limit = isset($posts_per_page) ? $posts_per_page : 50;
$post_type = isset($post_type) ? $post_type : "post";
$post_status = isset($post_status) ? $post_status : "publish";
$post_status = is_array($post_status) ? "IN ('" . implode("' ,'", $post_status) . "')" : "= '$post_status'";
$order = isset($order) && in_array($order, ["ASC", "DESC"]) ? $order : "DESC";
$order = isset($orderby) && isset($meta_key) ? "ORDER BY $meta_key $order" : "";
if (isset($meta_query) && is_array($meta_query) && count($meta_query) > 0) {
$meta_query_string = ", ";
$meta_query_where_string = "";
$is_first = true;
foreach($meta_query as $query) {
if ($is_first) {
$is_first = false;
$meta_query_where_string .= "WHERE ";
} else {
$meta_query_where_string .= "AND ";
}
$meta_query_string .= "MAX(IF(wpm.meta_key = '{$query['key']}', wpm.meta_value, NULL)) AS {$query['key']}, ";
switch($query['type']) {
case("NUMERIC"):
$delimiter = "";
break;
default:
$delimiter = "'";
}
switch ($query['compare']) {
case("BETWEEN"):
$meta_query_where_string .= "{$query['key']} {$query['compare']} $delimiter{$query['value'][0]}$delimiter AND $delimiter{$query['value'][1]}$delimiter ";
break;
default:
$meta_query_where_string .= "{$query['key']} {$query['compare']} $delimiter{$query['value']}$delimiter ";
}
}
$meta_query_where_string = substr($meta_query_where_string, 0, strlen($meta_query_where_string) - 1);
$meta_query_string = substr($meta_query_string, 0, strlen($meta_query_string) - 2);
}
if (isset($tax_query) && is_array($tax_query) && count($tax_query) > 0) {
foreach($tax_query as $query) {
$terms = is_array($query['terms']) ? "'" . implode("', '", $query['terms']) . "'" : "'{$query['terms']}'";
$tax_query_string .= "AND wtt.taxonomy = '{$query['taxonomy']}' AND wt.{$query['field']} IN ({$terms}) ";
}
}
return $wpdb->get_results("
SELECT * FROM
(
SELECT wp.*$meta_query_string
FROM wp_posts wp
JOIN wp_term_relationships wtr ON wp.ID = wtr.object_id
JOIN wp_term_taxonomy wtt ON wtt.term_taxonomy_id = wtr.term_taxonomy_id
JOIN wp_terms wt ON wtt.term_id = wt.term_id
JOIN wp_postmeta wpm ON wpm.post_id = wp.ID
WHERE wp.post_status $post_status
$tax_query_string
AND wp.post_type = '$post_type'
GROUP BY wp.ID
UNION ALL
SELECT wp.*$meta_query_string
FROM wp_posts wp
JOIN wp_postmeta wpm ON wpm.post_id = wp.post_parent
WHERE wp.post_parent IN (
SELECT wp.ID FROM wp_posts wp
JOIN wp_term_relationships wtr ON wp.ID = wtr.object_id
JOIN wp_term_taxonomy wtt ON wtt.term_taxonomy_id = wtr.term_taxonomy_id
JOIN wp_terms wt ON wtt.term_id = wt.term_id
JOIN wp_postmeta wpm ON wpm.post_id = wp.ID
WHERE wp.post_status $post_status
$tax_query_string
AND wp.post_type = '$post_type'
)
GROUP BY wp.ID
) as main
$meta_query_where_string
$order
LIMIT $limit"
);
}
As a sidenote, your metavalues can be accessed through their metakey in the getCustomPosts output, e.g.: while iterating through the results, $post->incsub_event_start
I filtered the post's in my sidebar by using this code,
$mostlikequerystr = "
SELECT $wpdb->posts.*
FROM $wpdb->posts, $wpdb->postmeta
WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id
AND $wpdb->postmeta.meta_key = 'most_liked'
AND $wpdb->posts.post_status = 'publish'
AND $wpdb->posts.post_type = 'post'
ORDER BY $wpdb->postmeta.meta_value DESC
LIMIT 0 , 10";
This code working perfect , but now i want to add a category filter too..
for this i used $term_id
global $wpdb;
$term_id = get_term_by('slug','trailers');
$term_id->term_id;
echo $term_id;//Prints 12
$mostlikequerystr="SELECT $wpdb->posts.* FROM $wpdb->posts,$wpdb->postmeta
INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id)
INNER JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.$wpdb->taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
WHERE ($wpdb->term_taxonomy.term_id = $term_id
AND $wpdb->posts.ID = $wpdb->postmeta.post_id
AND $wpdb->postmeta.meta_key = 'most_liked'
AND $wpdb->term_taxonomy.taxonomy = 'categories'
AND $wpdb->posts.post_type = 'post'
AND $wpdb->posts.post_status = 'publish')
LIMIT 0 , 10";
$tariler_post = $wpdb->get_results($mostlikequerystr, 'OBJECT');
echo $wpdb->show_errors();
but its not working for me and no error too ...
You are missing with conditional operator in your WHERE clause
WHERE ($wpdb->term_taxonomy.term_id = $term_id here
^^^^^
$wpdb->posts.ID = $wpdb->postmeta.post_id
Try this one by adding AND
$mostlikequerystr = "
SELECT $wpdb->posts.* FROM $wpdb->posts,$wpdb->postmeta
INNER JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id)
INNER JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.$wpdb->taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
WHERE ($wpdb->term_taxonomy.term_id = $term_id
AND $wpdb->posts.ID = $wpdb->postmeta.post_id
AND $wpdb->postmeta.meta_key = 'most_liked'
AND $wpdb->term_taxonomy.taxonomy = 'categories'
AND $wpdb->posts.post_type = 'post'
AND $wpdb->posts.post_status = 'publish'
ORDER BY $wpdb->postmeta.meta_value DESC
)
LIMIT 0 , 10";
If you are using WPDB class try to catch the errors
<?php $wpdb->show_errors(); ?>
Other way you can use WP's built in functions
$args = array(
'posts_per_page' => 10,
'post_type' => 'post',
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'most_liked',
'value' => '',
'compare' => '!='
),
'category__in' => 'id here or skip this argument'
'orderby' => 'meta_value_num',
);
$tariler_post=new WP_Query($args);
Ive written a query that searches for all posts that have X as a meta/custom field value.
// PSV National Query
if ($_POST['vehicleType'] == 'psv' && $_POST['coverageRegion'] == 'national' ) {
$customkey = 'vehicleType';
$customvalue = $_POST['vehicleType'];
$customkey1 = 'coverageRegion';
$customvalue1 = $_POST['coverageRegion'];
$customkey2 = 'locationType';
$customvalue2 = $_POST['locationType'];
global $wpdb;
$my_posts = $wpdb->get_results("
SELECT $wpdb->posts.*
FROM $wpdb->posts, $wpdb->postmeta, $wpdb->postmeta AS mt1
WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id
AND $wpdb->postmeta.meta_key = '$customkey'
AND $wpdb->postmeta.meta_value = '$customvalue'
AND mt1.meta_key = '$customkey1'
AND mt1.meta_value = '$customvalue1'
AND mt2.meta_key = '$customkey2'
AND mt2.meta_value = '$customvalue2'
AND $wpdb->posts.post_status = 'publish'
AND $wpdb->posts.post_type = 'post'
ORDER BY $wpdb->posts.post_date DESC
");
$args = array(
'meta_query' => array(
array(
'key' => $customkey,
'value' => $customvalue,
'compare' => '='
),
array(
'key' => $customkey1,
'value' => $customvalue1,
'compare' => '='
),
array(
'key' => $customkey2,
'value' => $customvalue2,
'compare' => '='
)
)
);
$query = new WP_Query( $args );
foreach ($query as $post) :
setup_postdata($post);
echo '<div><a href="';
the_permalink();
echo '"></div>';
the_title();
endforeach;
}
Now for my post I have 1 value for each meta key and this works fine, I want to however have multiple values.
For example...
"gas, electricity, water"
When I add multiple values however the query returns null, I presumer its because im saying if...
postmeta.meta_value = '$customvalue'
Can anybody give me advice on where im going wrong?
Why don't you use the built in WP QUERY, it supports meta field array operators.
for example:
$query = new WP_Query( array(
'meta_key' => 'color',
'meta_value' => 'blue',
'meta_compare' => '!='
));
Ref: http://codex.wordpress.org/Class_Reference/WP_Query#Custom_Field_Parameters
If you are using multiple matches in a query's where clause you should use the IN variant istead of the =.
The only row I see in your query where you use $customvalue is here
AND $wpdb->postmeta.meta_value = '$customvalue'
You should change the = to IN and seperate the values with a , and quotes ' ' like this for example
AND $wpdb->postmeta.meta_value IN ('gas','electricity','water')
I ditched the $customvalue in the above code to make the point of the seperator of the IN value.
Hopefully this puts you on the right track.