I'm currently trying to add my theme posts custom meta values into search query, but it's not working for me. For example _My_meta_value_key3 is product code, but if i try to search it, wp don't find the match. I really want to make this without any plugins so any suggestions are welcome. Also, this code is currently located in theme functions.
function My_custom_search_query( $query ) {
if ($query->is_search()) {
$query->set('meta_query', array(
array(
'key' => '_My_meta_value_key',
'value' => $query->query_vars['s'],
'compare' => 'LIKE'
),
array(
'key' => '_My_meta_value_key2',
'value' => $query->query_vars['s'],
'compare' => 'LIKE'
),
array(
'key' => '_My_meta_value_key3',
'value' => $query->query_vars['s'],
'compare' => 'LIKE'
)
));
return $query;
};}
add_filter( 'pre_get_posts', 'My_custom_search_query');
I've spent an hour searching for a solution and there was nothing that works so I had to write the solution on my own. It is tested and working with Wordpress 5.3.1 and is the only solution that solves this problem I could find on the internet.
Place the following code in your functions.php file to hook into posts_clauses filter.
/**
* Include meta fields in search
*
* #author Mindaugas // meevly.com
* #link https://meevly.com/services/custom-wordpress-themes-and-plugins/
*
* #param array $pieces query pieces.
* #param WP_Query $args query object.
* #return array
*/
function mv_meta_in_search_query( $pieces, $args ) {
global $wpdb;
if ( ! empty( $args->query['s'] ) ) { // only run on search query.
$keywords = explode(' ', get_query_var('s'));
$escaped_percent = $wpdb->placeholder_escape(); // WordPress escapes "%" since 4.8.3 so we can't use percent character directly.
$query = "";
foreach ($keywords as $word) {
$query .= " (unique_postmeta_selector.meta_value LIKE '{$escaped_percent}{$word}{$escaped_percent}') OR ";
}
if ( ! empty( $query ) ) { // append necessary WHERE and JOIN options.
$pieces['where'] = str_replace( "((({$wpdb->posts}.post_title LIKE '{$escaped_percent}", "( {$query} (({$wpdb->posts}.post_title LIKE '{$escaped_percent}", $pieces['where'] );
$pieces['join'] = $pieces['join'] . " INNER JOIN {$wpdb->postmeta} AS unique_postmeta_selector ON ({$wpdb->posts}.ID = unique_postmeta_selector.post_id) ";
}
}
return $pieces;
}
add_filter( 'posts_clauses', 'mv_meta_in_search_query', 20, 2 );
And your query should look like the following. Please note that suppress_filters => false is mandatory! It will not work without it.
$search_posts_array = array(
'suppress_filters' => false,
's' => $keyword,
);
$search_results = get_posts( $search_posts_array );
Please do not hesitate to credit if I saved you tons of time! :)
Found this http://wpdevsnippets.com/extend-search-include-custom-fields-without-plugin/ :
function custom_search_where($pieces) {
// filter to select search query
if (is_search() && !is_admin()) {
global $wpdb;
$custom_fields = array('field1','field2'); // Array for custom meta fields
$keywords = explode(' ', get_query_var('s'));
$query = "";
foreach ($custom_fields as $field) {
foreach ($keywords as $word) {
$query .= "((mypm1.meta_key = '".$field."')";
$query .= " AND (mypm1.meta_value LIKE '%{$word}%')) OR ";
}
}
if (!empty($query)) {
// add to where clause
$pieces['where'] = str_replace("(((wp_posts.post_title LIKE '%", "( {$query} ((wp_posts.post_title LIKE '%", $pieces['where']);
$pieces['join'] = $pieces['join'] . " INNER JOIN {$wpdb->postmeta} AS mypm1 ON ({$wpdb->posts}.ID = mypm1.post_id)";
}
}
return ($pieces);
}
add_filter('posts_clauses', 'custom_search_where', 20, 1);
Related
The solution from here isn't solving our problem.
I have already a solution to change all links in a field form in our theme. I am using different arrays for every network like $example_network_1 $example_network_2 with a PHP foreach for each affiliate network.
Now I need a solution to use this same arrays for the content of a WordPress post.
This solution is working for one network, but it caused a 404e Error for YouTube videos. We put the URL from a YouTube video and WordPress generates automatically an embedded video. With the following code we get a 404 error iframe or something like this.
We need a solution for more than one network.
I am very thankful for every help!
$example_network_1 = array(
array('shop'=>'shop1.com','id'=>'11111'),
array('shop'=>'shop2.co.uk','id'=>'11112'),
);
$example_network_2 = array(
array('shop'=>'shop-x1.com','id'=>'11413'),
array('shop'=>'shop-x2.net','id'=>'11212'),
);
add_filter( 'the_content', 'wpso_change_urls' ) ;
function wpso_found_urls( $matches ) {
global $example_network_1,$example_network_2;
foreach( $example_network_1 as $rule ) {
if (strpos($matches[0], $rule['shop']) !== false) {
$raw_url = trim( $matches[0], '"' ) ;
return '"https://www.network-x.com/click/'. $rule['id'].'lorem_lorem='.rawurlencode($raw_url ) . '"';
}
/*
foreach( $example_network_2 as $rule ) {
if (strpos($matches[0], $rule['shop']) !== false) {
$raw_url = trim( $matches[0], '"' ) ;
return '"https://www.network-y.com/click/'. $rule['id'].'lorem='.rawurlencode($raw_url ) . '"';
}
*/
}
}
function wpso_change_urls( $content ) {
global $example_network_1,example_network_2;
return preg_replace_callback( '/"+(http|https)(\:\/\/\S*'. $example_network_1 ['shop'] .'\S*")/', 'wpso_found_urls', $content ) ;
// return preg_replace_callback( '/"+(http|https)(\:\/\/\S*'. $example_network_2 ['shop'] .'\S*")/', 'wpso_found_urls', $content ) ;
}
autoembed is hooked at the_content with priority 8 on wp-includes/class-wp-embed.php:39
Try to lower the priority of the the_content filter so that the URL replacement happens before the embed, something like this:
add_filter( 'the_content', function ( $content ) {
/*
* Here, we define the replacements for each site in the network.
* '1' = main blog
* '2' = site 2 in the network, and so on
*
* To add more sites, just add the key number '3', etc
*/
$network_replacements = [
'1' => [
[ 'shop' => 'shop1.com', 'id' => '11111' ],
[ 'shop' => 'shop2.co.uk', 'id' => '11112' ],
],
'2' => [
[ 'shop' => 'shop-x1.com', 'id' => '11413' ],
[ 'shop' => 'shop-x2.net', 'id' => '11212' ],
]
];
// Early bail: Current blog ID does not have replacements defined
if ( ! array_key_exists( get_current_blog_id(), $network_replacements ) ) {
return $content;
}
$replacements = $network_replacements[ get_current_blog_id() ];
return preg_replace_callback( '/"+(http|https)(\:\/\/\S*' . $replacements['shop'] . '\S*")/', function( $matches ) use ( $replacements ) {
foreach ( $replacements as $rule ) {
if ( strpos( $matches[0], $rule['shop'] ) !== false ) {
$raw_url = trim( $matches[0], '"' );
return '"https://www.network-x.com/click/' . $rule['id'] . 'lorem_lorem=' . rawurlencode( $raw_url ) . '"';
}
}
}, $content );
}, 1, 1 );
This is not a copy and paste solution, but should get you going. You might need to tweak your "preg_replace_callback" code, but you said it was working so I just left it is it was.
If preventing the wp auto-embed works, then just add this line to your theme functions.php
remove_filter( 'the_content', array( $GLOBALS['wp_embed'], 'autoembed' ), 8 );
I wrote solution without test. Your code is hard to test without your site but I think that problem is with regex. Callback is hard to debugging. My version below.
First step, change your structure. I suspect that domains are unique. One dimensional array is more useful.
$domains = array(
'shop1.com'=>'11111',
'shop2.co.uk'=>'11112',
'shop-x1.com'=>'11413',
'shop-x2.net'=>'11212',
);
Next:
$dangerouschars = array(
'.'=>'\.',
);
function wpso_change_urls( $content ) {
global $domains,$dangerouschars;
foreach($domains as $domain=>$id){
$escapedDomain = str_replace(array_keys($dangerouschars),array_values($dangerouschars), $domain);
if (preg_match_all('/=\s*"(\s*https?:\/\/(www\.)?'.$escapedDomain.'[^"]+)\s*"\s+/mi', $content, $matches)){
// $matches[0] - ="https://example.com"
// $matches[1] - https://example.com
for($i = 0; $i<count($matches[0]); $i++){
$matchedUrl = $matches[1][$i];
$url = rawurlencode($matchedUrl);
//is very important to replace with ="..."
$content = str_replace($matches[0][$i], "=\"https://www.network-x.com/click/{$id}lorem_lorem={$url}\" ", $content);
}
}
}
return $content;
}
Example script
So basically I am writing a new wordpress theme, Usually i add a new category in wordpress using the admin panel by filling in some info: "name" and the "slug".
I want to do this in my theme by overriding the funtions.php file
For example creating a new category named : New category , with the slug new_category .
Is it possible ?
And of course, I know that this idea doesn't exactly follow some theme programming guidelines such as envato guideline or the Wordpress.org publishers guideline. And yes the best thing would be to make a plugin for this. But here I am talking about a practical point of view, So is it possible? could you just point me up in the right direction (maybe a link to some doc or so?).
Thanks to #Coder comments I wrote this code that works like a charm for me :
$mycat_name = 'New category'; //The name of category that I want to add
$mycat_slug = 'new-category'; //The slug (no spaces, lower-cases)
$mycat = $wpdb->get_results( "SELECT t.*, tt.* FROM $wpdb->terms AS t
INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id
WHERE tt.taxonomy = 'category' AND t.name = '" . $mycat_name . "'");
$checking = sizeof($mycat) ;
//found out that size is 0 if there the category doesn't exist
if ($checking === 0){
$category = array('cat_name'=>$mycat_name, 'category_nicename'=>$mycat_slug);
wp_insert_category($category);
}
So this works and you an just add it anywhere (maybe index.php) , also this wil require you to add the following function that I got from wordpress taxonomy file
function wp_insert_category( $catarr, $wp_error = false ) {
$cat_defaults = array(
'cat_ID' => 0,
'taxonomy' => 'category',
'cat_name' => '',
'category_description' => '',
'category_nicename' => '',
'category_parent' => '',
);
$catarr = wp_parse_args( $catarr, $cat_defaults );
if ( trim( $catarr['cat_name'] ) == '' ) {
if ( ! $wp_error ) {
return 0;
} else {
return new WP_Error( 'cat_name', __( 'You did not enter a category name.' ) );
}
}
$catarr['cat_ID'] = (int) $catarr['cat_ID'];
// Are we updating or creating?
$update = ! empty( $catarr['cat_ID'] );
$name = $catarr['cat_name'];
$description = $catarr['category_description'];
$slug = $catarr['category_nicename'];
$parent = (int) $catarr['category_parent'];
if ( $parent < 0 ) {
$parent = 0;
}
if ( empty( $parent )
|| ! term_exists( $parent, $catarr['taxonomy'] )
|| ( $catarr['cat_ID'] && term_is_ancestor_of( $catarr['cat_ID'], $parent, $catarr['taxonomy'] ) ) ) {
$parent = 0;
}
$args = compact( 'name', 'slug', 'parent', 'description' );
if ( $update ) {
$catarr['cat_ID'] = wp_update_term( $catarr['cat_ID'], $catarr['taxonomy'], $args );
} else {
$catarr['cat_ID'] = wp_insert_term( $catarr['cat_name'], $catarr['taxonomy'], $args );
}
if ( is_wp_error( $catarr['cat_ID'] ) ) {
if ( $wp_error ) {
return $catarr['cat_ID'];
} else {
return 0;
}
}
return $catarr['cat_ID']['term_id'];
}
Works for me, a Quick and dirty way to push gategories in your thee directly.
From a practical point (this is good) but still a bad idea with regards to wordpress publishers guidelines where this code must at least be in a plugin.
I am trying to create a shortcode that returns a list of doctors matching a specialty.
So far, I can get the base shortcode to return the contents of the entire table, but I can't get it to query based on an attribute string.
Here's what I have:
// Add Shortcode
function list_docs( $atts ) {
// Attributes
$atts = shortcode_atts(
array(
'specialty' => '',
),
$atts,
'doctors'
);
global $wpdb;
$specialty = $atts['specialty'];
$specget = $wpdb->prepare("SELECT * FROM doctors WHERE specialty = '%s'", $specialty);
$specresults = $wpdb->get_results($specget);
foreach($specresults as $details) {
echo $details;
}
}
add_shortcode( 'doctors', 'list_docs' );
If I query the database directly with:
SELECT * FROM `doctors` WHERE `specialty` = 'cardiology'
I get the expected result.
I'm trying to call it with [doctors specialty="cardiology"] (I've tried double and single quotes) on the WordPress page.
Right now, I don't know what I don't know. I'm not sure if I've entered something wrong, have a typo, or am missing a line of code. Any assistance would be terrific.
May be problem is not with the query at all assuming that your table name is indeed doctors( & not wp_doctors or something like that )
$specresults will contain an array of objects. Let's say your doctors table has name column, then below changes may work for you.
function list_docs( $atts ) {
// Attributes
$atts = shortcode_atts(
array(
'specialty' => '',
),
$atts,
'doctors'
);
global $wpdb;
$specialty = $atts['specialty'];
$specget = $wpdb->prepare( 'SELECT * FROM doctors WHERE specialty = %s', $specialty );
$specresults = $wpdb->get_results( $specget );
if ( $specresults ) {
$doctor_names = array_map(
function( $doctor_object ) {
return $doctor_object->name;
},
$specresults
);
return implode( ', ', $doctor_names );
}
return '';
}
add_shortcode( 'doctors', 'list_docs' );
Couple of things to keep in mind:-
Shortcodes should always return & not echo directly.
Query only for required data from the database as far as possible instead of doing *
If you are going to need only one column, prefer using get_col method on $wpdb instead of get_results.
Please try with this -
function list_docs( $atts ) {
global $wpdb;
$specialty = $atts['specialty'];
$specget = $wpdb->prepare("SELECT * FROM doctors WHERE specialty = %s", $specialty);
$specresults = $wpdb->get_results($specget);
foreach($specresults as $details) {
echo $details;
}
}
add_shortcode( 'doctors', 'list_docs' );
on my wordpress site I have WooCommerce and the WooCommerce per product shipping extension (https://docs.woocommerce.com/document/per-product-shipping/). I currently have an issue, this is that whenever I duplicate a woocommerce product the per-product shipping information is not copied. I have tried to find a solution for this and have got the following code however I have not been able to get it work, can anyone see what is going wrong in the following
add_action( 'woocommerce_product_duplicate', 'wdm_duplicate_pps_entries',10,2);
function wdm_duplicate_pps_entries( $new_id, $post) {
global $wpdb;
$id = isset( $_REQUEST['post'] ) ? absint( $_REQUEST['post'] ) : '';
if(!empty($id)) {
$query = "Select * From " . $wpdb->prefix . "woocommerce_per_product_shipping_rule
Where product_id = '" . $id . "'";
$result = $wpdb->get_results($query);
$table_name = $wpdb->prefix . "woocommerce_per_product_shipping_rule";
foreach($result as $single_result) {
$data = array('product_id' => $new_id, 'rule_country' => $single_result->rule_country, 'rule_state' => $single_result->rule_state,'rule_postcode' => $single_result->rule_postcode,'rule_cost' => $single_result->rule_cost,'rule_item_cost' => $single_result->rule_item_cost,'rule_order' => $single_result->rule_order);
$wpdb->insert($table_name,$data);
}
}
}
This is an example of the additonal info I am trying to copy -
UPDATE: Wordpress has fixed this bug
The below code working perfect.
add_action( 'woocommerce_duplicate_product', 'wdm_duplicate_pps_entries',10,2);
function wdm_duplicate_pps_entries( $new_id, $post) {
global $wpdb;
$id = isset( $_REQUEST['post'] ) ? absint( $_REQUEST['post'] ) : '';
if(!empty($id)) {
$query = "Select * From " . WC_PRODUCT_VENDORS_PER_PRODUCT_SHIPPING_TABLE . "
Where product_id = '" . $id . "'";
$result = $wpdb->get_results($query);
$table_name = WC_PRODUCT_VENDORS_PER_PRODUCT_SHIPPING_TABLE;
foreach($result as $single_result) {
$data = array('product_id' => $new_id, 'rule_country' => $single_result->rule_country, 'rule_state' => $single_result->rule_state,'rule_postcode' => $single_result->rule_postcode,'rule_cost' => $single_result->rule_cost,'rule_item_cost' => $single_result->rule_item_cost,'rule_order' => $single_result->rule_order);
$wpdb->insert($table_name,$data);
}
}
}
I wanna limit the result to 10 elements (10 words). This is the code I got:
function ls_bp_hashtags_cloud() {
$args = array () ;
if ( bp_is_activity_component() ) {
$toHead = __( 'Popular Hashtags across network' , 'bp-hashtags' ) ;
}
if ( bp_is_user_activity() ) {
$toHead = __( 'Hashtags by user' , 'bp-hashtags' ) ;
$args[ 'user_id' ] = bp_displayed_user_id() ;
}
if ( bp_is_group_activity() || bp_is_group_home() ) {
$toHead = __( 'Hashtags in group' , 'bp-hashtags' ) ;
$args[ 'if_activity_item_id' ] = bp_get_current_group_id() ;
}
echo '<div align="right"><h5>' . $toHead . '</h5>' ;
echo ls_bp_hashtags_generate_cloud( $args ) ;
echo '</div>' ;
}
And thisone below is the ls_bp_hashtags_generate_cloud() function:
function ls_bp_hashtags_generate_cloud( $args = array() ) {
$hashtags = ls_bp_hashtags_get_hashtags( $args );
$defaults = array(
'smallest' => 10, 'largest' => 10, 'unit' => 'pt', 'number' => 0,
'format' => 'flat', 'separator' => ",\n\n", 'orderby' => 'count', 'order' => 'DESC',
'topic_count_text_callback' => 'default_topic_count_text',
'topic_count_scale_callback' => 'default_topic_count_scale', 'filter' => 1
);
$args = wp_parse_args( $args, $defaults );
extract( $args );
$tag_cloud = wp_generate_tag_cloud( $hashtags, $args );
$tag_cloud = '<div class="hashtags">' . $tag_cloud . '</div>';
return $tag_cloud;
}
I got this other one too, not sure if it's needed:
function ls_bp_hashtags_get_hashtags( $args = array() ) {
global $wpdb;
$bp = buddypress();
$link = $bp->root_domain . "/" . $bp->activity->slug . "/" . BP_ACTIVITY_HASHTAGS_SLUG . "/";
bp_hashtags_set_constants();
$data = maybe_unserialize( get_site_option( 'ls_bp_hashtags' ) );
if ( $data['style']['show_hashsymbol'] == '1' ) {
$hashtag_name = ' CONCAT( "#", hashtag_name)';
} else {
$hashtag_name = 'hashtag_name ';
}
$toWhere = ls_bp_hashtags_generate_query_limitations( $args );
$results = $wpdb->get_results( 'SELECT COUNT(hashtag_name) as count, '
. $hashtag_name . ' as name, '
. 'CONCAT("' . $link . '", hashtag_slug) as link
FROM ' . BP_HASHTAGS_TABLE . ' WHERE 1=1 ' . $toWhere . ' GROUP BY hashtag_name' );
return $results;
}
The first snippet I pasted here is in one file while the last 2 snippets are in another file.
I think I have to modify the function ls_bp_hashtags_generate_cloud(), the point is I don't know how to do that. I tried with array_slice() and with $sql = "SELECT * FROM bp_activity LIMIT 10"; I checked mysql database, my ashtags are located in bp_ashtags table, but when I open this table I see each hashtag is referred to be in bp_activity table.
This function gives me the ashtags cloud of my buddypress activity stream page, so it gives me all the ahstags present in the database, instead I wanna limit the result to just 10 #ashtags (it's a wordpress website). That said I'm really a novice so please if you can help me giving me the whole snippet to add and where to put it, I know almost nothing about php coding. If you need to know other things about this code, just tell me; thisone is an extract so I don't know if I'm missing something for the solution. Thank you in advance
Yes, I have to modify the function ls_bp_hashtags_generate_cloud(), the point is I don't know how to do that. I tried with array_slice() and with $sql = "SELECT * FROM bp_activity LIMIT 10"; I checked mysql database, my ashtags are located in bp_ashtags table, but when I open this table I see each hashtag is referred to be in bp_activity table. This function gives me the ashtags cloud of my buddypress activity stream page, so it gives me all the ahstags present in the database, instead I wanna limit the result to just 10 #ashtags (it's a wordpress website). That said I'm really a novice so please if you can help me giving me the whole snippet to add and where to put it.