The Difference between do_action, add_action and add_filter
The Foundation: do_action
do_action creates the hook for things to hang. This is at the core of WordPress and even frameworks like Genesis and many other themes and plugins. do_action is the first domino in the chain of events with hooks. However, alone, it means nothing and does nothing. Simply, it tells WordPress to search to see if any functions are attached to it to fire. So do_actions will look something like this:
[php]<?php do_action( 'my-home' ); [/php]
It can also take and pass variables:
[php]<?php do_action( 'my-home' , $var1 , $var2 ); [/php]
The Difference between add_action and add_filter
The difference is primarily and technically semantic. Technically speaking, you can use them interchangeably, but it wouldn't follow code common sense or code "mentality" as one writer said. Filters should filter information, thus receiving information/data, applying the filter and returning information/data, and then used. However, filters are still action hooks.
WordPress defines add_filter as "Hooks a function to a specific filter action," and add_action as "Hooks a function on to a specific action."
add_action
Now, add_actions are a bit different. They hang items on the do_action hook and $priority determines the order.
Say, for example, you have this in your functions.php file:
[php]<?php
add_action( 'hook' , 'bob' );
add_action( 'hook' , 'andy' );
[/php]
The general order is "chronological" meaning if action 'bob' appears (coded/read by the server) before action 'andy', then the action hook 'bob' will fire first. However, with $priority, this order can be interrupted and changed. So...
[php]<?php
add_action( 'hook' , 'bob' , 10 );
add_action( 'hook' , 'andy' , 5 );
[/php]
With add_actions, variables and information may be passed back and forth as needed in the various functions as need. It should also be noted that not all actions are void of arguments or parameters. Some actions do have parameters, so:
[php]<?php add_action( $tag, $function_to_add, $priority, $accepted_args ); ?>[/php]
So, it would look something like this:
[php]<?php add_action( 'hook_name' , 'my_function_name' , 10 , 2 ); ?>[/php]
Extended Version Example
[php]<?php
function echo_comment_id( $comment_ID )
{
echo "I just received $comment_ID";
}
add_action( 'comment_id_not_found', 'echo_comment_id', 10, 1 );[/php]
add_filter
Now WordPress (and my theme) passes much of this page through various filters that check, validate, correct, and even modify various parts. Once it passes through the filter, the information then is applied to an action.
So here is the add_filter function:
[php]<?php add_filter( $tag, $function_to_add, $priority, $accepted_args ); ?>[/php]
This generally looks something like this:
[php]<?php
add_filter( 'filter_name' , 'my_filter_function_name' , 10 , 3 );
function filter_name( $val, $attr, $content = null ) {
//do something
}
?>[/php]
Just like add_actions go with do_actions, the same is true for filters logically speaking. With add_filter you must have apply_filters. Without the filter being called or applied then the filter means nothing, logically.
So, logicaly, in our example with filter 'filter_name', this would modify content/information that is coded like this:
[php]<?php
// Allow plugins/themes to override the default caption template.
$output = apply_filters( 'filter_name', $output , $val, $attr , $content );
if ( $output != '' )
return $output;
[/php]
So, the code is basically saying, “Take the value of the `$output` variable, apply any filters attached to the ‘filter_name’ hook passing the variables $val, $attr, $content to the filter function (whatever that may be and if it accepts them), and assign the filtered value back to the `$output` variable”.
However, technically speaking, PHP is rather forgiving and instead of filtering anything, it can function like add_action, adding the filter to a do_action, not filtering anything, which adds to the confusion. So if you have the following, it will work (though not good form):
[php]</php
do_action( 'my_action' );
add_filter( 'my_action' , 'my_function');
function my_function() {
//do something
}[/php]
Since the add_filter did not have any filters being applied, it worked as an add_action.
How to Get WordPress's Custom Menu Description
So recently, someone wanted to use menu descriptions as a tagline of sorts for a variety of pages, categories, archives, etc. instead of using basic metaboxes on pages (since this would also apply to category pages, etc.). So I wrote up a simple function that I believe may be helpful for others. So here is the code. To use, simply copy the function to your functions.php file and then use the function wherever you'd like.
[php]<?php
function wps_get_menu_description( $args = array() ) {
global $post;
// Default
$defaults = array(
'echo' => false,
'format' => '',
'description' => '',
'location' => 'primary',
'classes' => 'post-description'
);
$args = wp_parse_args( $args, $defaults );
extract( $args , EXTR_SKIP );
// Get menu
$menu_locations = get_nav_menu_locations();
$nav_items = wp_get_nav_menu_items( $menu_locations[ $location ] );
// Cycle through nav items
foreach ( $nav_items as $nav_item ) {
if ( ( is_page() || is_single() || is_archive() ) && ( $nav_item->object_id == $post->ID ) ) {
$description = $nav_item->description;
}
elseif ( ( is_category() ) && ( $nav_item->object == 'category' ) ) {
$cat = get_query_var( 'cat' );
if ( $nav_item->object_id == $cat )
$description = $nav_item->description;
}
}
// Get output formatting
if ( $format == 'html' )
$output = apply_filters( 'wps_get_menu_description_output' , '<div class="'. $classes .'">' . $description . '</div>' , $args );
else
$output = $description;
// Echo description
if ($echo)
echo $output;
// Return description
return $output;
}
[/php]
To call the function, you just set the args in an array like this:
wps_get_menu_description( array('echo' => true, 'format'=>'html', 'classes' => 'post-info') );
With most frameworks, like Genesis/Thesis, you may need to call it like this (example reflects Genesis):
[php]<?php
// Add and Customize a Tagline under the Page Title
add_action('genesis_after_post_title', 'my_tagline');
function my_tagline() {
wps_get_menu_description( array('echo' => true, 'format'=>'html', 'classes' => 'post-info') );
}[/php]
Word of Warning: Please check your descriptions, and do not assume that there is nothing there if you didn't put it there. Your theme or plugins may have inserted content there as I've seen the description contain the_content automagically.
Do you see anything that I missed? Is there something else that you wish was added?
How to Make a Genesis Grid Archive Template for Tags
To display posts from a specific tag or tags, you need to add a 'tag' argument to the $grid_args array.
In the StudioPress tutorial about categories, you are given this example for your home.php.
[php highlight="17"]<?php
remove_action( 'genesis_loop', 'genesis_do_loop' );
add_action( 'genesis_loop', 'child_grid_loop_helper' );
/** Add support for Genesis Grid Loop **/
function child_grid_loop_helper() {
if ( function_exists( 'genesis_grid_loop' ) ) {
genesis_grid_loop( array(
'features' => 2,
'feature_image_size' => 0,
'feature_image_class' => 'alignleft post-image',
'feature_content_limit' => 0,
'grid_image_size' => 'grid-thumbnail',
'grid_image_class' => 'alignleft post-image',
'grid_content_limit' => 0,
'more' => __( '[Continue reading...]', 'genesis' ),
'posts_per_page' => 6,
'cat' => '6,7' //enter your category IDs here separated by commas in ' '
) );
} else {
genesis_standard_loop();
}
}
/** Remove the post meta function for front page only **/
remove_action( 'genesis_after_post_content', 'genesis_post_meta' );
genesis();[/php]
Simply change the 'cat' argument to 'tag'. And you can have the customization you want. OR, you can grab it dynamically by making the following changes:
[php highlight="6,18"]<?php
remove_action( 'genesis_loop', 'genesis_do_loop' );
add_action( 'genesis_loop', 'child_grid_loop_helper' );
/** Add support for Genesis Grid Loop **/
function child_grid_loop_helper() {
$term = get_query_var( 'term' );
if ( function_exists( 'genesis_grid_loop' ) ) {
genesis_grid_loop( array(
'features' => 2,
'feature_image_size' => 0,
'feature_image_class' => 'alignleft post-image',
'feature_content_limit' => 0,
'grid_image_size' => 'grid-thumbnail',
'grid_image_class' => 'alignleft post-image',
'grid_content_limit' => 0,
'more' => __( '[Continue reading...]', 'genesis' ),
'posts_per_page' => 6,
'tag' => $term
) );
} else {
genesis_standard_loop();
}
}
/** Remove the post meta function for front page only **/
remove_action( 'genesis_after_post_content', 'genesis_post_meta' );
genesis();[/php]
You can save this as tag.php to make all of your tag archives as a grid. Or, you can simply apply this to a single tag by naming it tag-{SLUG}.php or tag-{ID}.php (see WordPress Codex for Tag Templates for more information).
How to Make a Custom Taxonomy Genesis Grid Archive Template
First, catch up on the Grid Loops by reading these posts:
- Customizing the Genesis Grid Content, by Bill Erickson
- How to Use the Genesis Grid Loop, by Brian Gardner
- Genesis Grid Loop Advanced, by Gary Jones
Now, that you have a basic understanding of the Grid, let's apply this to a custom taxonomy archives.
First, you need a new document saved as taxonomy-{taxName}.php (for more information, see the WordPress Codex). So if you have a custom taxonomy of "Book Type," registered as 'book_type,' then the file name will be taxonomy-book_type.php.
Second, create the header, just like a page template. This is primarily for your organization and your information. So I just model it after the page template just to be consistent.
[php]<?php
/*
Template Name: Book Type Taxonomy Archive
*/[/php]
Third, you want to set all your customizations like remove post meta, post info, page layout, etc.
Fourth, you want to include the Grid Looper Helper function. This is a standard protocol for the Genesis Grid Loop:
[php]<?php
remove_action( 'genesis_loop', 'genesis_do_loop' );
add_action( 'genesis_loop', 'wps_grid_loop_helper' );
/** Add support for Genesis Grid Loop **/
function wps_grid_loop_helper() {
global $grid_args, $post;
$taxonomy = 'book_type'; //change me
$term = get_query_var( 'term' );
$term_obj = get_term_by( 'slug' , $term , $taxonomy );
$cpt = 'wps_books'; //change me
if ( function_exists( 'genesis_grid_loop' ) ) {
$grid_args_tax = array(
'features' => 0,
'feature_image_size' => 'book_featured',
'feature_image_class' => 'aligncenter post-image',
'feature_content_limit' => 100,
'grid_image_size' => 'book_thumb',
'grid_image_class' => 'aligncenter post-image',
'grid_content_limit' => 0,
'more' => '',
'posts_per_page' => 10,
'post_type' => $cpt,
'paged' => get_query_var('paged') ? get_query_var('paged') : 1,
'tax_query' => array(
array(
'taxonomy' => $taxonomy,
'field' => 'slug',
'terms' => array( $term_obj->slug ),
)
)
);
printf('<h2 class="book-section"> %s Books</h2>' , $term_obj->name );
genesis_grid_loop($grid_args_tax);
} else {
genesis_standard_loop();
}
}
[/php]
Now, this can be rather complicated and intimidating, so let me break this down for you. While everything is the same as the previous Grid posts, there is one major change and that is the 'tax_query' (WordPress Codex). But just for those readers, who didn't take my advice to read the previous articles, let me briefly explain the $grid_args_tax (for those who know the basic args, skip the next paragraph).
'features' refers to the number of featured posts in the grid loop. So if you want to display 10 but feature 2, you would set this number to 2, posts_per_page to 10 and Genesis will do the math to ensure that everything is kosher. However, in our example, we want zero features for the taxonomy listing. 'feature_image_size' and 'grid_image_size' are the post thumbnail size that you want the image to appear as. 'feature_image_class' and 'grid_image_class' refer to the CSS image class for styling. 'feature_content_limit' and 'grid_content_limit' refers to the number of characters allowed in the content. See the StudioPress articles for futher explanations.
Now 'tax_query' refers to the taxonomy args. It must take an array of arrays of the following args:
- taxonomy (string): the taxonomy
- field (string): select taxonomy term by 'id' or 'slug'
- terms (int/string/array): taxonomy terms
- operator (string): 'IN', 'NOT IN', 'AND'
In our example, we have the template for a predetermined specific taxonomy and the template dynamically grabbing the appropriate taxonomy term (so someone may be searching for a 'Fiction' Book Type). If you wanted, it could also fetch the taxonomy dynamically, if you wanted all of your taxonomy archives to be on the grid. To do this, simply add the following code: $taxonomy = get_query_var( 'taxonomy' );
. For tags and categories, you probably could use this same method; however, there is also a simpler method which I will explain in a later post (Tags and Same Categories and Different Categories).
So, after you set the post type ('post', 'page', or some other custom post type registered name, e.g., 'wps_books') and the taxonomy (which is the same as the latter half of the file name, so in this example 'book_type'), the function first grabs the slug of the term: $term = get_query_var( 'term' );
(more information: get_query_var). Then it calls for the genesis custom grid loop.
Now obviously, with this custom post type, I am going to want to determine my own grid loop content. To do this, I simply make these additions:
[php] <?php
add_action('genesis_before_post', 'wps_custom_grid');
function wps_custom_grid() {
remove_action('genesis_post_content', 'genesis_grid_loop_content');
add_action('genesis_post_content', 'wps_grid_loop_content');
}
function wps_grid_loop_content() {
global $_genesis_loop_args, $wp_query;
//do something
}
[/php]
For an excellent post on customizing the content, see Bill Erickson's post, Customizing the Genesis Grid Loop Content.
Some other good grid functions include the following:
[php]<?php
// Add some extra post classes to the grid loop so we can style the columns
add_filter( 'genesis_grid_loop_post_class', 'wps_grid_loop_post_class' );
/**
* Add some extra body classes to grid posts.
*
* Change the $columns value to alter how many columns wide the grid uses.
*
* @author Gary Jones
* @link http://dev.studiopress.com/genesis-grid-loop-advanced.htm
*
* @global array $_genesis_loop_args
* @global integer $loop_counter
* @param array $classes
*/
function wps_grid_loop_post_class( $grid_classes ) {
global $_genesis_loop_args, $loop_counter;
// Alter this number to change the number of columns - used to add class names
$columns = 3;
// Only want extra classes on grid posts, not feature posts
if ( $loop_counter >= $_genesis_loop_args['features'] ) {
// Add genesis-grid-column-? class to know how many columns across we are
$grid_classes[] = sprintf( 'genesis-grid-column-%s', ( ( $loop_counter - $_genesis_loop_args['features'] ) % $columns ) + 1 );
// Add size1of? class to make it correct width
$grid_classes[] = sprintf( 'size1of%s', $columns );
}
return $grid_classes;
}
// Make sure the first page has a balanced grid
add_action( 'genesis_loop', 'wps_bal_grid_loop' );
function wps_bal_grid_loop() {
global $query_string, $paged, $grid_args, $_genesis_loop_args;
if ( 0 == $paged )
// If first page, add number of features to grid posts, so balance is maintained
$grid_args['posts_per_page'] += $grid_args['features'];
else
// Keep the offset maintained from our page 1 adjustment
$grid_args['offset'] = ( $paged - 1 ) * $grid_args['posts_per_page'] + $grid_args['features'];
}
[/php]
- « Previous Page
- 1
- …
- 33
- 34
- 35
- 36
- 37
- …
- 60
- Next Page »