Here is an interesting approach:
THE PLAN AND LOGIC:
Custom fields are a build in functionality that will fit our needs perfectly. We can set a new custom field and then use that field to enter and store our custom data. We can then use a widget to display this custom content in the sidebar of our single pages
Example of custom field in post editor
Possible issues:
There are a few issues we need to take into account
Whether or not a widget has content or not, it counts as an active sidebar. The issue is, we do not want to display the widget on any other page that the single post page, also, we would want to hide the sidebar when we do not have any other widgets and when our custom widget is empty. There is no build in function to set the is_active_sidebar()
conditional statement to false, or have any similar function to hide a sidebar when we have a sidebar with an empty widget only
Some themes do not have the ability to switch between full width mode and normal mode with sidebar when the status of a sidebar changes from active to inactive and vice versa
SOLUTIONS TO OUR MAIN ISSUE:
Our main issue is how to hide the widget when it is empty or if we are on a page that is not a single post page. Our plan here would be remove the custom widget from the array of sidebars widgets returned via the wp_get_sidebars_widgets()
function. We specially make use of the build in filter sidebars_widgets
.
It is really straight forward to determine the page we are on by simply using the is_single()
conditional statement.
As to how to determine if our widget is empty or not, the logic is quite simple. We will need a helper function to get the post meta value from our custom filed on single post pages. This helper function will be used by our widget to display the content. Now, this logic can be used to determine if our widget will be empty or not, and according to this, we can hide/remove the widget from the wp_get_sidebars_widgets()
which is used by functions like is_active_sidebar()
The issue regarding full width vs normal display according to the status of a sidebar is quite easy, and I'm not going to go into this right here. In short though, if you look at the twentyfourteen theme, it uses body_class
es to set a CSS class of full-width
when the content sidebar (sidebar-2
) is empty. This means that the content area is set to 100% width (if you want to call it that) to hide the blank space on the right of the content if there are no widgets in the sidebar.
Here is the section responsible for this
function twentyfourteen_body_classes( $classes ) {
/* ...................*/
if ( ( ! is_active_sidebar( 'sidebar-2' ) )
|| is_page_template( 'page-templates/full-width.php' )
|| is_page_template( 'page-templates/contributors.php' )
|| is_attachment() ) {
$classes[] = 'full-width';
}
/* ...................*/
return $classes;
}
add_filter( 'body_class', 'twentyfourteen_body_classes' );
You can, in your own time, have a look at the CSS that is used to switch between full width and normal view with a sidebar, and be sure to play around with the possibilities
THE CODE:
HELPER FUNCTION get_custom_field_content
We will first write our helper function to return the custom content from the custom field from the post in single view. I have commented the code, so it will be easy to follow
function get_custom_field_content( $key = '' )
{
// Make sure this is a singel page, if not, return false
if ( !is_single() )
return false;
// Make sure we have a custom field key value, if not, return false
if ( !$key )
return false;
// We have made it this far, so let sanitize the $key input
$key = filter_var( $key, FILTER_SANITIZE_STRING );
// OK, we've come this far, all our conditions checked out, lets get the current post ID
$current_post_ID = get_queried_object_id();
// Lets us now get and return our custom post meta value
$custom_content = get_post_meta(
$current_post_ID, // Current post ID to get post meta from
$key, // Custom field to get value from
true // Return single value
);
return $custom_content;
}
This function will be used in our widget to get the custom content and then display it. The $key
parameter in the function will be the custom field name, in our example, that will be custom_content
. This function will also be used by our filter function to determine if our widget is empty and to remove our widget from our sidebars on a conditional statement
THE WIDGET:
This our widget, which will have a field where you should enter the custom field name in the specified field. Again, the code is commented to make it easy to follow
/**
* RelatedContentWidget widget class
*
* Displays posts from a selected category
*
* @since 1.0.0
*/
class RelatedContentWidget extends WP_Widget
{
public function __construct()
{
parent::__construct(
'widget_related_content',
_x( 'Related Content Widget', 'Related Content Widget' ),
[ 'description' => __( 'Display a list of related content.' ) ]
);
$this->alt_option_name = 'widget_related_content';
add_action( 'save_post', [$this, 'flush_widget_cache'] );
add_action( 'deleted_post', [$this, 'flush_widget_cache'] );
add_action( 'switch_theme', [$this, 'flush_widget_cache'] );
}
public function widget( $args, $instance )
{
$cache = [];
if ( ! $this->is_preview() ) {
$cache = wp_cache_get( 'widget_rel_content', 'widget' );
}
if ( ! is_array( $cache ) ) {
$cache = [];
}
if ( ! isset( $args['widget_id'] ) ) {
$args['widget_id'] = $this->id;
}
if ( isset( $cache[ $args['widget_id'] ] ) ) {
echo $cache[ $args['widget_id'] ];
return;
}
ob_start();
$title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Related Content' );
/** This filter is documented in wp-includes/default-widgets.php */
$title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
// This holds the custom field key that holds the custom content
$custom_field_key = ( ! empty( $instance['custom_field_key'] ) ) ? $instance['custom_field_key'] : '';
/**
* Make sure we only run this on a singular page/single post.
* Also make sure that we have a value inside $custom_field_key
*/
if ( $custom_field_key
&& is_single()
) {
/**
* Get the custom field having our custom content
* First make sure that the custom function get_custom_field_content() exists to avoid fatal errors
*/
if ( function_exists( 'get_custom_field_content' ) ) {
$custom_content = get_custom_field_content(
$custom_field_key // The custom field we need to retrieve content from
);
// Make sure we actually have a value before we display anything
if ( $custom_content ) {
echo $args['before_widget'];
if ( $title ) {
echo $args['before_title'] . $title . $args['after_title'];
}
// Display our current content
echo $custom_content;
echo $args['after_widget'];
}
}
}
if ( ! $this->is_preview() ) {
$cache[ $args['widget_id'] ] = ob_get_flush();
wp_cache_set( 'widget_rel_content', $cache, 'widget' );
} else {
ob_end_flush();
}
}
public function update( $new_instance, $old_instance )
{
$instance = $old_instance;
$instance['title'] = filter_var( $new_instance['title'], FILTER_SANITIZE_STRING );
$instance['custom_field_key'] = filter_var( $new_instance['custom_field_key'], FILTER_SANITIZE_STRING );
$this->flush_widget_cache();
$alloptions = wp_cache_get( 'alloptions', 'options' );
if ( isset($alloptions['widget_related_content']) )
delete_option('widget_related_content');
return $instance;
}
public function flush_widget_cache()
{
wp_cache_delete('widget_rel_content', 'widget');
}
public function form( $instance )
{
$title = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : '';
$custom_field_key = isset( $instance['custom_field_key'] ) ? esc_attr( $instance['custom_field_key'] ) : '';
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo $title; ?>" />
</p>
<p>
<label for="<?php echo $this->get_field_id( 'custom_field_key' ); ?>"><?php _e( 'Custom field key:' ); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'custom_field_key' ); ?>" name="<?php echo $this->get_field_name( 'custom_field_key' ); ?>" type="text" value="<?php echo $custom_field_key; ?>" />
</p>
<?php
}
}
add_action( 'widgets_init', function ()
{
register_widget( 'RelatedContentWidget' );
});
This is how our widget will look like in back end
FILTER FUNCTION:
Lastly is our helper function to remove the widget on all pages except single post pages and whenever the widget is empty. Again, the code is commented to make it easy to follow
add_filter( 'sidebars_widgets', function ( $sidebars_widgets )
{
// Return our filter when we are on admin screen
if ( is_admin() )
return $sidebars_widgets;
// Widget we need to target. This should be the name/id we used to register it, in this case widget_related_content
$custom_widget = 'widget_related_content';
// Set our custom field key to get our custom content
$custom_field_key = 'custom_content'; // Be sure to change this to your exact name
/**
* Only run the next block for all templates/pages except the single page. You can
* adjust this as needed.
*
* We also want to run this on a single page if our custom widget is empty
*/
// Set a variable to test for single pages
$single_post = false;
// Set variable to hold the custom content
$custom_content = '';
/**
* Check for a single page and if it is, check if we have custom content
* We will use the custom function we have created, get_custom_field_content()
* to run this conditions
*/
if ( is_single() ) {
if ( function_exists( 'get_custom_field_content' ) )
$custom_content = get_custom_field_content( $custom_field_key );
$single_post = true;
}
// Stop and return $sidebars_widgets if we are on a single page and have custom content
if ( $single_post && $custom_content )
return $sidebars_widgets;
// We have come this far, let us wrap this up
// See if our custom content widget exists is any sidebar, if so, get the array index
foreach ( $sidebars_widgets as $sidebars_key=>$sidebars_widget ) {
// Skip the wp_inactive_widgets set, we do not need them
if ( $sidebars_key == 'wp_inactive_widgets' )
continue;
// Only continue our operation if $sidebars_widget are not an empty array
if ( $sidebars_widget ) {
foreach ( $sidebars_widget as $k=>$v ) {
/**
* Look for our custom widget, if found, unset it from the $sidebars_widgets array
* @see stripos()
*/
if ( stripos( $v, $custom_widget ) !== false )
unset( $sidebars_widgets[$sidebars_key][$k] );
} // endforeach $sidebars_widget
} // endif $sidebars_widget
} // endforeach $sidebars_widgets
return $sidebars_widgets;
});
And that is all we need. Using the twentyfourteen theme, this is what you will see if we have custom content
and here is a post without custom content