WordPress search returning duplicate results - php

There is a search box on a WordPress site I'm currently working on that is returning somewhat sporadic duplicate results based on the number of keywords entered into the search. For example, if I enter the keywords "Green Lake" into the box, it will return Post A twice, Post B twice, Post C once or twice. If I enter "Green Lake Lake" (three keywords), it will return Post A three times, Post B three times, Post C two or three times. Etc etc.
I think I've narrowed the issue down to the following code. Can anyone point out anything obvious? I think it has something to do with the foreach loop on $keywords, but I don't know how to fix it. Thank you in advance.
if ( isset( $_REQUEST['keywords'] ) && ! empty( $_REQUEST['keywords'] ) ) {
$filter_posts = [];
$keywords = explode( ' ', strtolower( $_REQUEST['keywords'] ) );
foreach ( $posts as $post ) {
foreach ( $keywords as $keyword ) {
// if the keyword is in the title the don't search the post content
if (
strpos( strtolower( $post->post_title ), $keyword ) !== false
||
strpos( strtolower( $post->post_content ), $keyword ) !== false
) {
$filter_posts[] = $post;
}
}
}
$posts = $filter_posts;
}
wp_send_json_success( json_encode( $posts ) );

Related

Best performance for mySQL and PHP: Lots of small queries, or a large one?

So i have this technicall dilemma:
I have a Wordpress site, with more than 50 categories (and rising). I want to get just the six categories with the newest article in them, and -if i get this right- i can't do it with a single typical WP Query, i'll have to improvise:
a) get from the database the last 200-300 posts, and by elimination in PHP filter by category, and show the six last updated categories, or
b) get from the database all my categories, and for each category ask again the database the last article by date - and show the six last updated categories.
i know that the first solution is not very safe, and will strech a little my server calculating with PHP, but the second will have more than 50+1 queries (and rising).
Considering that the site i'm working on has a couple of million visitors each month, which one do you think will give, from a technical point of view, the less stress to my servers?
Or i'm missing an more obvius solution?
Thanks for your time! :)
Every time you post something, you could save the taxonomy's name/slug/id in a transient on publishing/updating through set_transient() and save_post action hook.
Saving the data as an array and slicing it once it reach 6... array_search before updating...
To display them you would simply loop through the transient's value and get_taxonomy() or build a custom WP_Query looping through each of them.
Which would avoid you a lot of queries. Pretty much none...
here is an untested demo:
<?php
//Retieve the transient through get_transient( '_transient_wpso_73900689' );
add_action( 'save_post', function ( $post_ID, $post, $update ) {
$transient = '_transient_wpso_73900689';
$expiration = 0;
$prev_value = ! empty( get_transient( $transient ) ) ? get_transient( $transient ) : array();
$next_value = get_post_taxonomies( $post_ID );
$buffer = array();
if ( ! empty( $next_value ) ) {
foreach ( $next_value as $taxonomy ) {
//Search the previous transient array.
if ( array_search( $taxonomy, $prev_value ) === false ) {
//If the value doesn't exist, push the value to the array.
array_push( $buffer, $taxonomy );
};
};
$value = array_merge( $prev_value, $buffer );
//Reverse the array.
$value = array_reverse( $value );
//Retrieve the last 6.
$value = array_slice( $value, 0, 6 );
//Set the transient.
set_transient( $transient, $value, 0 );
};
}, 10, 3 );
Can,t you just join the articles and sort by the joined table ordered by date desc?
Something like:
Select from categories c
Left join articles a on a.category = c.id
Order by a.date desc group by c.id limit 6

Wordpress+ACF - Filtering through multiselect options in a repeater field

I’m integrating a form of $_POST method with ajax filtering, to filter through repeater filed in all posts. so i’m not filtering posts (always all posts are displayed), just filtering inside each post.
the repeater field contain two sub fields – multiselect and Wysiwyg Editor.
Now, according to what the user selected in the front-end form, if it’s equal to a post multiselect values, it should display the Wysiwyg Editor field of the matching multiselect.
I have this working without multiselect. but with the multiselect, i can’t get a conditional to be ALL selected filters values, to be the exact match of ALL multiselect values.
So the result i get, as it is in a foreach loop, is multiple Wysiwyg Editor fields.
I tried a lot of things, this is an example code of one of them:
(‘cond_options’ – multiselect
‘description’ – Wysiwyg Editor’
‘ weather/sky/night’ – filters values)
if ( have_rows('cond-repeater') ):
while (have_rows('cond-repeater') ) : the_row();
$select_options = get_sub_field('cond_options');
$selectdesc = get_sub_field('description');
if( $select_options ):
foreach( $select_options as $select ):
if( isset( $_POST['weather'] ) && $_POST['weather'] && isset( $_POST['sky'] ) && $_POST['sky'] && isset( $_POST['night'] ) && $_POST['night'] == $select ){
echo $selectdesc;
}
echo $select; //just to see the output of selected options
endforeach;
endif;
endwhile;
endif;
Without knowing much about your data and the actual end result you are trying to achieve. I have put together something that might help steer you in the right direction.
The main thing to point out is the use of in_array, with this we can check if a value exists in the multiple select without having to loop through it with a foreach.
Let me know if this helps.
$_weather = !empty( $_POST[ 'weather' ] ) ? $_POST[ 'weather' ] : null;
$_sky = !empty( $_POST[ 'sky' ] ) ? $_POST[ 'sky' ] : null;
$_night = !empty( $_POST[ 'night' ] ) ? $_POST[ 'night' ] : null;
if ( have_rows('cond-repeater') ) {
while ( have_rows('cond-repeater') ) {
the_row();
if ( $options = get_sub_field( 'cond_options' ) ) {
// Check payload includes all required parameters
if ( $_weather && $_sky && $_night ) {
// Check if all parameters exist in the multiselect value
if ( in_array( $_weather, $options ) && in_array( $_sky, $options ) && in_array( $_night, $options ) ) {
echo get_sub_field( 'description' );
} else {
echo $select; // Warning: Undefined variable!
}
}
}
}
}

wp-admin search posts by title only and show sub pages

I've got a custom post type that in the CMS I would to be able to alter the search so it only searches by title of the post, then also pulls through all sub pages of that page.
Currently I have the following, which does limit search to title but I'm struggling how to best approach pulling through the subpages and returning them on this query.
add_filter( 'posts_search', 'admin_search_shops', null, 2 );
function admin_search_shops( $search, $a_wp_query ) {
if ( !is_admin() ) return $search;
$search = preg_replace( "# OR \(.*posts\.post_content LIKE \\'%.*%\\'\)#", "", $search );
return $search;
}
Rather than trying to reconstruct a complicated SQL query, it would be much simpler to add the children in the 'posts_results' filter at the end. Combined with what you have done already (limiting the search to the titles), the following will add in any child pages.
add_filter( 'posts_results', 'admin_postsearch_shops', null, 2);
function admin_postsearch_shops( $posts ) {
foreach( $posts as $post ) {
$args = array( 'post_parent' => $post->ID);
foreach ( get_children( $args ) as $child ) {
$posts[] = $child;
}
}
return $posts;
}

How can I get in_array() to work with get_option()?

I have two custom fields in my admin settings that allow me to enter two sets of valid zip codes ('woocommerce_local_delivery_zone_a' and 'woocommerce_local_delivery_zone_b').
I cannot figure out why this code isn't working:
$codes_a = get_option( 'woocommerce_local_delivery_zone_a' );
$codes_b = get_option( 'woocommerce_local_delivery_zone_b' );
if ( in_array( "55410", array( $codes_a ) )) {
echo "Codes A";
}
if ( in_array( "55410", array( $codes_b ) )) {
echo "Codes B";
}
If I echo $codes_a or $codes_b (outside of the "if" statement), the string of zip codes returns on the page. If I take that string and enter it directly where the variable is in if (in_array( "55410", array( NUMBERS HERE ))) the text I am trying to echo (Codes A or Codes B) works.
I also tried placing array( get_option( 'field_name')) directly in the if statement, and that didn't do anything either. Does in_array not work with getting admin custom fields?
If $codes_a is a string like this: 08847,032434,35545 then you need to use explode() to turn it into an array. I suspect what you really want is this:
if ( in_array( "55410", explode( ',', $codes_a ) ) {
echo "Codes A";
}

Not managing to find a way to use is_page() with a variable inside storing an array

Building a Wordpress plugin here. I have a text form in which user can insert pages IDs - separated by commas - to display a BAR in it.
Then I set a variable to get those Ids and I am trying to use this variable with the conditional tag "is_page( array() )".
Problem is, the BAR is showing only on the page related to the first ID given by the user. It is strange, cause I echoed the variable and it is showing all Id's given, separated by commas. Hardcoding the IDs, like this "is_page( array(207,306) )", I get the result I want and the BAR is displaying on the 2 pages related to the IDs. But when I use "is_page( array($setting_value) )", only page related to the first ID is showing the BAR, even when "echo $setting_value" results in 207,306.
Can anybody show me what is wrong?
Here's the parts of the code related to it:
//getting the IDs
$setting_value = esc_attr( (get_option( 'ma-singular' ) ) );
if ( $home != 'onlyHome' ) {
echo "<input class='text' type='text' name='ma-singular' value='" . $setting_value . "' />";
}
//displaying the BAR on the PAGES related to the IDs typed by users
$singular = esc_attr (get_option ( 'ma-singular' ));
if ( $home != 'onlyHome' && is_page( array($singular) ) ) {
if ( is_user_logged_in() && !empty($alttext) ) {
echo '<div class="ma-' . $cor = get_option( 'ma-cor') .
'" style="background-color:' . $cordefundo = get_option( 'ma-fundo' ) .
'">' . $alttext . '</div>';
}
You need to explode $singular before using it. It's going wrong because what you think is an array of values is a single value inside the array.
Let's say for example 'ma-singular' = 127,135,189.
array( $singular ) would be equal to array( '127,135,189' ) when what you need is array( 127, 135, 189 ).
So to fix this you would do:
$singular = esc_attr (get_option ( 'ma-singular' ));
if ( ! empty( $singular ) ) {
$singular = explode( ',', $singular );
}
if ( $home != 'onlyHome' && is_page( $singular ) ) {

Categories