Search results page

For my search results page, I am using the default search capabilities in my WP site - no plugins. I have included items from the media library to be searchable:

function attachment_search( $query ) {
if ( $query->is_search ) {
$query->set( 'post_type', array( 'post', 'attachment' ) );
$query->set( 'post_status', array( 'publish', 'inherit' ) );
}

return $query;
}

Is there a way I could exclude media types from the search results? I only want the pdfs in my media library to be searchable e.g. show up in the search results, excluding .png, jpg, gif etc

Thanks

  • Huberson

    Hello robert475
    You could filter the result from the global query to remove these extensions based on 'post_mime_type' but in my opinion will take more code to work with the query.

    What you could do is add a second check inside the loop for the exiting search template file(search.php ) provided by your theme, and display the result only if it's not an image:

    while ( have_posts() ) :
    		the_post();
    		if(!wp_attachment_is_image( get_the_ID() )):
    			get_template_part( 'template-parts/post/content', 'excerpt' );
    		endif;
    endwhile; // End of the loop.

    The above snippet is an example from Twenty Seventeen theme search.php. The 'if(!wp_attachment_is_image( get_the_ID() )):' block is what checks if it's an image so it doesn't display.

    Hope that works for you.

    Cheers,
    Huberson

  • robert475

    Hi Huberson

    Thank you for your prompt reply.

    I have attempted what you said and it seems to have worked except that I have pagination in my search.php.

    So for example I have 10 pages of search results, pages 1-5 are blank because we have blocked the images from the loop. Is there a way the pagination could reflect this?

    My code is below, hopefully this makes sense...

    <div class="col">
    	<h1>Search Results for "<em><?php the_search_query() ?></em>"</h1>
    	<?php if ( have_posts() ) : ?>
    	<?php while ( have_posts() ) : the_post(); ?>
    	<?php if(!wp_attachment_is_image( get_the_ID() )): ?>
    	<div class="col-3">
    		<h2><?php the_title(); ?></h2>
    	</div>
    	<div class="col">
    		<p><?php the_excerpt(); ?></p>
    		<p><a href="<?php the_permalink(); ?>">Click here</a></p>
    	</div>
    	<?php endif; ?>
    	<?php endwhile; ?>
    	<?php numeric_posts_nav(); ?>
    	<?php else : ?>
    	<p>Sorry, but nothing matched your search criteria. Please try again with some different keywords.</p>
    	<?php endif; ?>
    </div>
  • Adam Czajczyk

    Hi robert475

    I hope you're having a nice day!

    If there's a pagination on search result page, that might be a bit more complex. The loop only returns a specified number of results (e.g. 10) and if some of them are removed it will result in less or no elements displayed on the page. Since you cannot "dynamically modify" that after the query has already been made (so you cannot "additionally fetch missing results") that would bring us back to the "post_mime_type" parameter mentioned in Huberson's previous post.

    What you would want to do though wouldn't be fetching only selected mime types but rather exclude some of them (even though in your case it would mean excluding pretty much all of them except PDFs). Take a look here at the "Get attachments that are not images" example:

    https://codex.wordpress.org/Class_Reference/WP_Query#Mime_Type_Parameters

    Following that you should be able to modify your original query to exclude everything but PDF's and with an original (not modified) loop that should work with pagination as well.

    Best regards,
    Adam

  • robert475

    Hi Adam

    I used the example in WP codex.

    $unsupported_mimes  = array( 'image/jpeg', 'image/gif', 'image/png', 'image/bmp', 'image/tiff', 'image/x-icon' );
    $all_mimes          = get_allowed_mime_types();
    $accepted_mimes     = array_diff( $all_mimes, $unsupported_mimes );
    $args		    = array(
        'post_type'         => 'attachment',
        'post_status'       => 'inherit',
        'post_mime_type'    => $accepted_mimes,
    );
    $query		    = new WP_Query( $query_args );

    This almost does what I need it to do but I do need to link to pages/post that may contain the search term.

    How do I include these within the results? I found this link and was able to put into my functions.php. This is what I used:

    function attachment_search( $query ) {
      if ( $query->is_search ) {
         $query->set( 'post_type', array( 'post', 'attachment', 'page', 'resources' ) );
         $query->set( 'post_status', array( 'publish', 'inherit' ) );
         //$query->set( 'post_mime_type', array( 'application/pdf' ) );
      }
    
     return $query;
    }
    
    add_filter( 'pre_get_posts', 'attachment_search' );

    Again it returns only the pdf attachments and not combined with pages/posts. I believe I am almost there any ideas please?

    Thanks
    Rob

  • Adam Czajczyk

    Hello robert475

    Thank you for your response!

    Let's make sure that we're on the same side the :slight_smile: This is how this is supposed to work:

    - user performs a search for some term, let's say "summer"
    - search page returns:

    a) posts, pages and "resources" (I guess that's some custom post type on your site) for the "summer" term
    b) PDF files found for "summer" term
    c) if the PDF file is in fact attached to some post/page - it gives link to that post/page or both: direct link to that PDF and a link to that post/page?
    d) if the PDF file is unattached (wasn't used in any post/page etc) - does it give a direct link to that PDF or does it ignore it?

    I think we can "combine" working solution out of all the codes that we discussed so far with a small addition of a slight adjustment of the loop but let me know first if I correctly understand the goal (or correct me if I'm wrong).

    Best regards,
    Adam

    • robert475

      Hi Adam
      a) and b) is correct. Resources is a custom post type
      c) I just need to link directly to the document. It could be a pdf or word doc. I only need to exclude images from the media library in the search results
      d) We can just ignore it because it is not used in any posts/pages

      I know its almost working, its probably something simple I am missing. Thanks for your help in advance, I look forward to your reply.

      Thanks
      Rob

  • Adam Czajczyk

    Hi robert475

    Thanks for clarification!

    I've tested this a bit more and I realized that the "post_mime_type" actually does restrict query to only "mime types" (those allowed) which efficiently excludes posts/pages etc as they do not have any mime-type. Therefore I took a bit different route, mixing one of the codes discussed above with "posts_where" filter.

    Resulting code seems to be working on my test setup (no need to make any changes in the loop - should be standard). To give it a try, make sure that you reverted all the changes tested so far and then simply try this code in functions.php of your theme:

    # set search query post types
    function attachment_search( $query ) {
    
      // adjust search query (if search and if not admin back-end)
      if ( ( !is_admin() ) && ( $query->is_search ) ) {
    
    	// set query
    	$query->set( 'post_type', array( 'post', 'attachment', 'page', 'resources' ) );
        $query->set( 'post_status', array( 'publish', 'inherit' ) );
        //$query->set( 'post_mime_type', $accepted_mimes );
      }
    
     return $query;
    }
    add_filter( 'pre_get_posts', 'attachment_search' );
    
    # and remove images from search
    function remove_images_from_search( $where ) {
        global $wpdb, $query;
    
    	// remove only if search page and not admin back-end
    	if ( ( !is_admin() ) && ( $query->is_search ) ) {
    		$where.=' AND '.$wpdb->posts.'.post_mime_type NOT LIKE \'image/%\'';
    	}
        return $where;
    }
    add_filter( 'posts_where' , 'remove_images_from_search' );

    Best regards,
    Adam

  • Adam Czajczyk

    Hello robert475

    You're right, that's not supposed to work actually. I've shared older version of the code that I was testing. That's my mistake and I apologize for it. The proper version that I was going to share is this:

    # set search query post types
    function attachment_search( $query ) {
    
      // adjust search query (if search and if not admin back-end)
      if ( ( !is_admin() ) && ( $query->is_search ) ) {
    
    	// set query
    	$query->set( 'post_type', array( 'post', 'attachment', 'page', 'resources' ) );
        $query->set( 'post_status', array( 'publish', 'inherit' ) );
      }
    
     return $query;
    }
    add_filter( 'pre_get_posts', 'attachment_search' );
    
    # and remove images from search
    function remove_images_from_search( $where, \WP_Query $query) {
        global $wpdb;
    
    	// remove only if search page and not admin back-end
    	if ( ( !is_admin() ) && ( $query->is_search ) ) {
    		$where.=' AND '.$wpdb->posts.'.post_mime_type NOT LIKE \'image/%\'';
    	}
    
        return $where;
    }
    add_filter( 'posts_where' , 'remove_images_from_search', 10, 2 );

    The difference (apart from commented line removed) is not big but significant: the callback function for "posts_where" takes two arguments where the second one is the WP_Query instance and that is instead global $query which simply doesn't work in this context.

    This code should work fine - that's the one I tested and just to make sure I also checked it again a minute ago and it seems to do the trick nicely.

    Best regards,
    Adam

  • robert475

    Hi Adam
    I believe this works now, Thank you!
    Just one quick question, for my search.php template I am using the below to display the search results. Not all the links to the attachments work though.

    Does this look ok to you?

    <h1>Search Results for "<em><?php the_search_query() ?></em>"</h1>
    <?php if ( have_posts() ) : ?>
    <?php while ( have_posts() ) : the_post(); ?>
    <div class="search-entry">
    <span class="search-title"><?php the_title(); ?></span><br>
    	<?php the_excerpt(); ?>
    	<?php
    	$posttype = get_post_type();
    
        	if ($posttype == 'page') { ?>
            <a class="perma" title="<?php the_title(); ?>" href="<?php the_permalink(); ?>">Link To Page</a>
    	<?php } else { ?>
    	<a class="attach" title="<?php the_title(); ?>" target="_blank" href="<?php echo wp_get_attachment_url(); ?>">Link To Attachment</a>
            <?php
    	}
            ?>
           </div>
            <?php endwhile; ?>
            <?php numeric_posts_nav(); ?>
            <?php else : ?>
     	<p>Sorry, but nothing matched your search criteria. Please try again with some different keywords.</p>
            <?php endif; ?>

    What I want to do is create a link to an attachment or page/post from the search results page.

    Thanks again
    Robert

  • Adam Czajczyk

    Hi robert475

    Thanks for confirmation that the code's working!

    As for missing links. The loop code from your search page template looks fine to me, at least at first glance :slight_smile:

    You say that not all the attachment links "are working". Does this mean that there are links but they are not right (e.g. they lead to 404 Not Found page or they are not "clickable")? Or that there's simply no link/URL for some attachments?

    It might be helpful if I could test it "live" so if you could point me to a search page on your site and some example search term that returns such results, it would be great.

    It would also be handy if I could take a look at the back-end. You can enable support access for me using our WPMU DEV Dashboard plugin by going to "WPMU DEV -> Support" page and clicking on "Grant support access" button there.

    I'm pretty sure we'll find an explanation for that :slight_smile:

    Best regards,
    Adam

  • Adam Czajczyk

    Hello robert475

    The way you're fetching attachment URLs seems fine, I don't think I'd do it in a different way.

    However, I did test your code on my end and noticed some other issue, which I'm not sure whether is on purpose or not. Let me explain :slight_smile:

    In a search code you're searching for attachments but also for these post types: "post", "page", "resources". But in your search result loop you're showing results like:

    - if it's a "page" post type, show link to the page
    - in every other case (so not only for attachments but also for "posts" and "resources") show link to attachment.

    The latter obviously won't work if a given post is not an "attachment" type. Now, if I correctly understand is that what you wish to achieve here is that:

    1) if a post is a "page" type - just display a link to that page (so that should be working perfectly fine with your code)

    2) if a post is an "attachment" type - just display direct link to that attachment file (and that should also be working fine with your code)

    3) if a post is "post" or "resources" type - assume that there's a file attached to it and display an URL of that attached file instead of a link to the post itself.

    If that is right then indeed the code would have to be modified a bit as in case of 3) you'd need to first find all attachments for that post. That gets a bit more complex though. You could replace this part of your search loop:

    $posttype = get_post_type();
    
        	if ($posttype == 'page') { ?>
            <a class="perma" title="<?php the_title(); ?>" href="<?php the_permalink(); ?>">Link To Page</a>
    	<?php } else { ?>
    	<a class="attach" title="<?php the_title(); ?>" target="_blank" href="<?php echo wp_get_attachment_url(); ?>">Link To Attachment</a>
            <?php
    	}
            ?>

    with code like this:

    $posttype = get_post_type();
    
    		switch ($posttype) {
    			case 'page': // for "page" post type
    				?> <p><a class="perma" title="<?php the_title(); ?>" href="<?php the_permalink(); ?>">Link To Page</a></p>	<?php
    				break;
    			case 'attachment': // for "attachment" post type
    				?> <p><a class="attach" title="<?php the_title(); ?>" target="_blank" href="<?php echo wp_get_attachment_url(); ?>">Link To Attachment</a></p> <?php
    				break;
    			default: // for any other post type
    				echo '<p>';
    
    				// get all attachments
    				$media = get_attached_media();
    
    				//filter to remove every not allowed mime-type again
    				// we searched for only certain mime-types but the post itself
    				//can contain other mime-type attachments as well
    				$attachments = array();
    				foreach ($media as $key=>$value) { // loop through all found and exclude "image" mime-type
    					if (!stristr($value->post_mime_type, 'image')) {
    						$attachments[] = $value;
    					}
    				}
    
    				// $attachments contains all non-image attachments
    				// if there are any, let's display link to the 1st one
    				if ( count( $attachments ) > 0 ) {
    					?> <p><a class="attach" title="<?php the_title(); ?>" target="_blank" href="<?php echo wp_get_attachment_url( $attachments[0]->ID ); ?>">Link To Post Attachment</a></p> <?php
    				}
    				// there's no attachments so let's just show link to a post page
    				else {
    					?> <p><a class="perma" title="<?php the_title(); ?>" href="<?php the_permalink(); ?>">Link To Post Page</a></p>	<?php
    				}
    
    				echo '</p>';
    		}
    ?>

    What it does is:

    1) if a search result is "page" post type - it displays link to that page

    2) if a search result is "attachment" post type - it displays direct link to a file

    3) if a search result is "post" or "resources" post type - it grabs information about all attachments added to this post and then:
    - filters all "image" type attachments
    - if there are any non-image attachments left it displays a direct link to the file of the first attachments
    - if there are no attachments left (because there were only image attachments) - it displays a link to that post.

    I hope that helps :slight_smile:

    Please note: that could be further modified/adjusted but I think that starts to go a bit to far into custom coding so if you need that to be more complex, please consider posting a job request at our "Jobs & Pros" job board (please note: no WPMU DEV staff involved) here:

    https://premium.wpmudev.org/wordpress-development/

    Best regards,
    Adam