Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rogerlos/e57ad1e2272107c12ba3 to your computer and use it in GitHub Desktop.
Save rogerlos/e57ad1e2272107c12ba3 to your computer and use it in GitHub Desktop.
Wordpress SearchWP: Substitute Post for Attachment in Search Results
<?php
/**
* This function will see if a PDF or other "attachment" post-type returned by SearchWP
* is present in a custom field in a regular post, and will return that post instead. Note
* this will only process documents which are referenced via the "attachment" post type.
*
* For example, you may have a product specification PDF and would like it in the search
* results, but would rather people got to it by visiting the product page itself.
*
* There are some filters you can set to provide some control over both the attachments
* processed and the post-types they might be attached to.
*
* The ideal use case is a custom post type which sets a custom field to the ID or filename
* of a file from the media library.
*
*/
// SearchWP hook
add_filter( 'searchwp_results', 'my_searchwp_search_results', 10, 2 );
/**
* Substitute a post for an attachment by looking for attachment URL or ID in custom post field
*
* @param $posts : Array of WP_Post objects from SearchWP
* @param array $params : Parameters sent by SearchWP (as of 2.4.8 - unused below, but available):
* 'terms' => Terms searched for
* 'page' => Which page of search results this is
* 'order' => Results return order (ex: DESC)
* 'foundPosts' => Number of found posts in results
* 'maxNumPages' => Maximum number of result pages
* 'engine' => Which SearchWP engine is in use
*
* @return array
*/
function my_searchwp_search_results( $posts, $params = array() ) {
$results = $posts;
$attachments = array();
/**
* Required: $custom_meta_field (string)
* The field in the post you wish to substitute which will be checked for the
* attachment filename or ID. This field will be matched against the attachment URL
* and the attachment ID, if it matches either the substitution will occur.
*/
$custom_meta_field = '';
/**
* Optional: $custom_description_field (string)
* SearchWP normally returns the excerpt. Setting a custom field value here will set
* the WP_Post excerpt to be the contents of this field. Note: Not escaped or otherwise
* filtered, use caution or add checks!
*/
$custom_description_field = null;
// Initial filtering of attachments by their properties:
/**
* Optional: $upload_directory_filter (string)
* Limit the substitution to attachments uploaded to directory with this string in path.
* Matched to ANY part of the path, be sure to be as specific as possible.
* Set to 'null' to check all attachments.
*/
$upload_directory_filter = null;
/**
* Optional: $filename_filter (string)
* Limit the substitution to attachments with this string as part of filename.
* Matched to ANY part of the filename, be sure to be as specific as possible.
* Set to 'null' to check all attachments.
*/
$filename_filter = null;
/**
* Optional: $extension_filter (array)
* Limit substitution to specific attachment types. Empty array
* allows all extensions. Example: array( 'pdf', 'txt' )
*/
$extension_filter = array();
/**
* Optional: $hide_others_filter (bool)
* If you set filters above and want non-matching attachments removed from search
* results entirely, set below to true
*/
$hide_others_filter = false;
// Matching filtered attachments:
/**
* Optional: $post_type_filter (array)
* Limit substitution to specific post types. Cannot be "attachment". Empty array
* dfaults to "post". Example: array( 'page', 'my_custom_post' )
*/
$post_types = array();
/**
* Optional: $hide_filtered_attachments (bool)
* Set to true if you want attachments hidden which passed initial
* filtering (path/name/extension) but cannot be matched against selected posts/fields
*/
$hide_filtered_attachments = false;
// if an attachment is in $results, add to $attachments
foreach ( $results as $key => $post ) {
// see if it's an attachment
if ( $post->post_type == 'attachment' ) {
// get the attachment URL and parse it
$check_file = wp_get_attachment_url( $post->ID );
$file_parsed = parse_url( $check_file );
// there has to be a path to proceed
if ( isset ( $file_parsed['path'] ) ) {
// use pathinfo to get filename
$check_file_info = pathinfo( $file_parsed['path'] );
// filter by path contents
if ( $upload_directory_filter !== null && ! strstr( $check_file_info['dirname'], $upload_directory_filter ) ) {
$check_file_info = '';
}
// filter by filename contents
if ( $filename_filter !== null && ! strstr( $check_file_info['filename'], $filename_filter ) ) {
$check_file_info = '';
}
// filter by extension
if ( ! empty( $extension_filter ) && ! in_array( $check_file_info['extension'], $extension_filter ) ) {
$check_file_info = '';
}
// did we find a filename?
if ( isset( $check_file_info['basename'] ) && $check_file_info['basename'] ) {
// Use the $posts key to facilitate later substitution
$attachments[ $key ]['path'] = $file_parsed['path'];
$attachments[ $key ]['ID'] = $post->ID;
}
}
// if $hide_others_filter is true and attachment was not added to $attachments, remove from results
if ( ! isset( $attachments[ $key ]['file'] ) && $hide_others_filter === true ) {
unset( $results[$key] );
}
}
}
// if $attachments were found, match them against posts
if ( ! empty( $attachments ) ) {
// holds our posts to check for our filtered attachments
$check_posts = array();
// if $post_types was set
$arg_post_type = '';
if ( ! empty( $post_types ) ) {
$arg_post_type = $post_types;
}
/**
* Note, this is a very simple query. You may or will want to modify to account for
* taxonomy filtering, etc., etc.
*/
$args = array(
'post_type' => $arg_post_type,
'posts_per_page' => -1,
'post_status' => 'publish',
);
$all_posts = new WP_Query( $args );
// did we find posts?
if ( ! empty( $all_posts->posts ) ) {
$check_posts = $all_posts->posts;
}
wp_reset_postdata();
// now we check those attachments to find their matching toolbox posts
foreach ( $attachments as $attachment_key => $attachment ) {
// flag to check for applying $hide_filtered_attachments
$ok = false;
foreach ( $check_posts as $post ) {
// get custom meta field which contains the attachment ID or filepath
$check_thisfile = get_post_meta( $post->ID, $custom_meta_field, true );
// did we get a value back?
if ( $check_thisfile ) {
// if it is numeric, it's an ID
if ( is_numeric( $check_thisfile ) && ( intval( $check_thisfile ) == floatval( $check_thisfile ) ) ) {
// compare to our id, if it matches, make the substitution
if ( intval( $check_thisfile) == intval( $attachment['ID'] ) ) {
$results[ $attachment_key ] = $post;
$ok = true;
break;
}
} else {
// parse the URL from the meta field
$thisfile_parsed = parse_url( $check_thisfile );
// if we found a path from our parsing and it matches our attachment path, make substitution
if ( isset ( $thisfile_parsed['path'] ) && ( $thisfile_parsed['path'] == $attachment['path'] ) ) {
$results[ $attachment_key ] = $post;
$ok = true;
break;
}
}
}
}
// we made a substitution and $custom_description_field was set, make the substitution
if ( $ok === true && $custom_description_field !== null ) {
$results[ $attachment_key ]->post_excerpt = get_post_meta( $results[ $attachment_key ]->ID, $custom_description_field, true );
}
// unset the search result if we did not make a substitution and the filter is set to true
if ( $ok === false && $hide_filtered_attachments === true ) {
unset( $results[ $attachment_key ] );
}
}
}
// return the search results to SearchWP
return array_values( $results );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment