combine keyword search and tax query in wp_query - php

I've been trying to create a custom search query, and I've made some progress on it, but have hit another roadbump.
I'm trying to combine the meta_query, keyword search ('s') and tax_query in a wp_query with an 'OR' relationship.
I've gotten the meta_query and 's' to work together thanks to this fantastic post:
https://wordpress.stackexchange.com/questions/99849/search-that-will-look-in-custom-field-post-title-and-post-content
however, the tax_query is still giving me trouble. I tried adding it in via the same method, but it seems as though wordpress does some other magic with tax_query before it outputs it to the SQL query.
Here's what I've got thus far:
function add_join_wpse_news($joins)
{
global $wpdb;
return $joins . " INNER JOIN {$wpdb->postmeta} ON ({$wpdb->posts}.ID = {$wpdb->postmeta}.post_id)" ;
}
function alter_search_wpse_news($search,$qry)
{
global $wpdb;
$add = $wpdb->prepare("({$wpdb->postmeta}.meta_key = '_et_builder_settings' AND CAST({$wpdb->postmeta}.meta_value AS CHAR) LIKE '%%%s%%')",$qry->get('s'));
$pat = '|\(\((.+)\)\)|';
$search = preg_replace($pat,'(($1 OR '.$add.'))',$search);
return $search;
}
function alter_groupby_wpse_news($groupby)
{
global $wpdb;
$mygroupby = "{$wpdb->posts}.ID";
if( preg_match( "/$mygroupby/", $groupby )) {
// grouping we need is already there
return $groupby;
}
if( !strlen(trim($groupby))) {
// groupby was empty, use ours
return $mygroupby;
}
// wasn't empty, append ours
return $groupby . ", " . $mygroupby;
}
add_filter('posts_join','add_join_wpse_news');
add_filter('posts_search','alter_search_wpse_news',1,2);
add_filter('posts_groupby', 'alter_groupby_wpse_news' );
$args_condensed = array
(
'post_type' => 'news',
'paged' => $paged,
's' => $getname,
);
$the_query = new WP_Query($args_condensed);
$max_pages = $the_query->max_num_pages;
echo $GLOBALS['the_query']->request;
And this works. However, it doesn't include the search for the the Tags or Categories. I attempted to add it in manually via the posts_join and posts_search filter, but then I realized that wordpress is comparining values in the tax_query BEFORE the outputted SQL query, which causes problems when trying to add it in.
Any help would be greatly appreciated.
EDIT: for clarification, I'm trying to add in:
'tax_query' => array
(
'relation' => 'OR',
array //Search Tag
(
'taxonomy' => 'post_tag',
'field' => 'slug',
'terms' => array($getname)
),
array //Search Category
(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array($getname),
),
array //Search Category (Single Words)
(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => explode(" ",$getname),
),
array //Search Tag (Single Words)
(
'taxonomy' => 'post_tag',
'field' => 'slug',
'terms' => explode(" ",$getname),
)
),
but with an OR type relationship as opposed to the AND relationship wordpress adds by default.

There are no way you can use tax_query for this purpose. You must override filter that wordpress provided to achieve this mission. Here is my code. Hope it help:
function add_join_wpse_news($joins)
{
global $wpdb;
$joins = $joins . " INNER JOIN {$wpdb->postmeta} ON ({$wpdb->posts}.ID = {$wpdb->postmeta}.post_id)" ;
$joins .= " inner join {$wpdb->term_relationships} as wrel on {$wpdb->posts}.ID = wrel.object_id";
$joins .= " inner join {$wpdb->term_taxonomy} as wtax on wtax.term_taxonomy_id = wrel.term_taxonomy_id";
$joins .= " inner join {$wpdb->terms} as wter on wter.term_id = wtax.term_id";
return $joins;
}
function add_where_wpse_news($where) {
$getname = 'what you want';
return $where. ' AND '. "wter.slug like '%$getname%' ";
}
add_filter('posts_join','add_join_wpse_news');
add_filter('posts_where','add_where_wpse_news');
I just add posts_where and modify posts_join filter.

Related

Get product ids by sku first characters using a shortcode from an URL in Woocommerce

I have multiple products, whose sku names are like AL-888, A-2323, AL-etrere.
I want to find products on basis of this sku name's first two words.
I am creating shortcode for this purpose, but can't able to get it work.
Any help should be appreciated.
Code for this shortcode:
function all_state_list_function(){
$name = $_GET['sku']; //this give sku value from url like Al:2323
echo $name;
$args = array(
'post_type' => 'product',
'posts_per_page' => -1,
'orderby' => 'name',
'order' => 'DESC',
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'free_form',
'value' => '1',
),
array(
'key' => '_sku',
'value' => $name.'-%',
'compare' => 'LIKE',
),
),
);
$loop = new WP_Query( $args );
while ( $loop->have_posts() ) : $loop->the_post();
$product_id = get_the_ID();
echo $product_id;
endwhile;
wp_reset_query();
}
add_shortcode( 'all_state_list_shortcode', 'all_state_list_function' );
Based on your first version code that had an SQL query, here is the correct way to get product IDs from first SKU characters value using GET method from an URL:
add_shortcode( 'all_state_list_shortcode', 'all_state_list_function' );
function all_state_list_function(){
if ( isset($_GET['sku']) && ! empty($_GET['sku']) ) {
ob_start();
$sku = esc_attr( $_GET['sku'] ) .'%';
global $wpdb;
$results = $wpdb->get_col( "
SELECT p.ID FROM {$wpdb->prefix}posts as p
JOIN {$wpdb->prefix}postmeta as pm ON p.ID = pm.post_id
WHERE p.post_type LIKE 'product' AND p.post_status LIKE 'publish'
AND meta_key LIKE '_sku' AND meta_value LIKE '$sku'
" );
if( count($results) > 0 ) {
echo implode(',', $results);
} else {
echo 'Nothing found';
}
return ob_get_clean();
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

post__not_in not getting excluded

I am trying to exclude some posts with a custom meta_key within the hook pre_get_posts but for some reason is not working, the posts are not getting excluded. Taxonomies to be excluded work, but posts no.
add_action('pre_get_posts' , 'changeCourseCountry');
function changeCourseCountry($query){
global $wpdb;
$tax_query_merge = array(
'relation' => 'AND',
array(
'taxonomy' => 'course_category',
'field' => 'slug',
'terms' => array('short-courses', 'mega-course'),
'operator' => 'NOT IN'
),
);
$tax_query = array_merge($tax_query, $tax_query_merge);
$exclude = $wpdb->get_col("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_video_course'"); // here I get an array of posts
$query->set('tax_query' ,$tax_query);
$query->set('post__not_in', $exlcude);
return $query
}
Please use the following instead of getting the column data,
$exclude = $wpdb->get_row("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_video_course'"); // here I get an array of posts
So it will be ,
add_action('pre_get_posts' , 'changeCourseCountry');
function changeCourseCountry($query){
global $wpdb;
$tax_query_merge = array(
'relation' => 'AND',
array(
'taxonomy' => 'course_category',
'field' => 'slug',
'terms' => array('short-courses', 'mega-course'),
'operator' => 'NOT IN'
),
);
$tax_query = array_merge($tax_query, $tax_query_merge);
$exclude = $wpdb->get_row("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_video_course'",ARRAY_A); // here I get an array of posts
$query->set('tax_query' ,$tax_query);
$query->set('post__not_in', $exlcude);
return $query;
}
Also if you need multiple IDs then you need to use get_results and loop the array in foreach.
I also added ARRAY_A to $wpdb query to retrive the array.

WordPress - Search posts by Taxonomy

My custom wp search query in
$args = array(
'post_type' => 'post',
'taxonomy' => 'location',
'field' => 'name',
'posts_per_page' => 10,
'term' => $keyword,
);
$wp_query = new WP_Query($args);
I have 2 post which location is London and oxford,london .Now when i search london then it show only 1 post**(1st one)** .Also when i search oxford then it show no result found.
When i search oxford london then its working fine.
Befor WP_Query Run write this code
function advance_search_where($where){
global $wpdb;
if (is_search())
$where .= "OR (t.name LIKE '%".get_search_query()."%' AND {$wpdb->posts}.post_status = 'publish')";
return $where;
}
function advance_search_join($join){
global $wpdb;
if (is_search())
$join .= "LEFT JOIN {$wpdb->term_relationships} tr ON {$wpdb->posts}.ID = tr.object_id INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id=tr.term_taxonomy_id INNER JOIN {$wpdb->terms} t ON t.term_id = tt.term_id";
return $join;
}
function advance_search_groupby($groupby){
global $wpdb;
// we need to group on post ID
$groupby_id = "{$wpdb->posts}.ID";
if(!is_search() || strpos($groupby, $groupby_id) !== false) return $groupby;
// groupby was empty, use ours
if(!strlen(trim($groupby))) return $groupby_id;
// wasn't empty, append ours
return $groupby.", ".$groupby_id;
}
add_filter('posts_where','advance_search_where');
add_filter('posts_join', 'advance_search_join');
add_filter('posts_groupby', 'advance_search_groupby');
Now Run Your WP_Query Like this
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
's' => $keyword,
);
$wp_query = new WP_Query($args);
After all your result and that you want you have to just remove the above filter coz if you not remove that then it will apply in other wp_query of that page.
remove_filter('posts_where','advance_search_where');
remove_filter('posts_join', 'advance_search_join');
remove_filter('posts_groupby', 'advance_search_groupby');

How To Exclude Results From Custom WordPress MySQL Query By Taxonomy Term

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 );

Filter query_posts by two custom fields?

I'm trying to filter my posts to only show the ones with that have a custom value for the field "Model" while sorting the posts by another custom field called "Price."
Here's the function I'm using (not working):
<?php
global $query_string;
query_posts( $query_string . "&meta_value=Model&orderby=meta_value&meta_key=Price&order=ASC");
?>
This function only shows Models, yet doesn't sort the posts by Price. If i add &meta_value=Model after order=ASC it sorts by Price but shows all posts, and not just Models.
Have you looked at http://codex.wordpress.org/Class_Reference/WP_Query
Specifically this section:
Multiple Custom Field Handling:
Display posts from several custom field:
$args = array(
'post_type' => 'product',
'meta_query' => array(
array(
'key' => 'color',
'value' => 'blue',
'compare' => 'NOT LIKE'
),
array(
'key' => 'price',
'value' => array( 20, 100 ),
'type' => 'numeric',
'compare' => 'BETWEEN'
)
)
);
$query = new WP_Query( $args );
did you try and array?
$args = array(
'meta_value' => array('Model','Price')
);
query_posts($args);
I know this is an old question, but I needed the answer today and couldn't find it anywhere. I found the question and then created an answer (shown below):
<?php
$sql = "
SELECT ID, meta1.meta_value, meta2.meta_value from $wpdb->posts p
JOIN $wpdb->postmeta meta1 ON meta1.post_id = p.ID
JOIN $wpdb->postmeta meta2 ON meta2.post_id = p.ID
WHERE p.post_status = 'publish'
AND p.post_type = 'post'
AND meta1.meta_key = 'Model'
AND meta2.meta_key = 'Price'
ORDER BY meta2.meta_value DESC
";
$posts_with_meta = $wpdb->get_col($sql);
$my_query = new WP_Query();
foreach ($posts_with_meta as $p) {
$post = get_post(intval($p));
setup_postdata($post);
print_r($post);
}
?>

Categories