Skip to content

Instantly share code, notes, and snippets.

@scribu
Created June 3, 2012 21:42
Show Gist options
  • Save scribu/2865132 to your computer and use it in GitHub Desktop.
Save scribu/2865132 to your computer and use it in GitHub Desktop.
APP_View
/**
* Helper class for controlling all aspects of a view.
*
* Supported methods (automatically hooked):
* - init() - for registering post types, taxonomies, rewrite rules etc.
* - parse_query() - for correcting query flags
* - pre_get_posts() - for altering the query, without affecting the query flags
* - posts_search(), posts_clauses(), posts_request() - for direct SQL manipulation
* - the_posts() - for various other manipulations
* - template_redirect() - for enqueuing scripts etc.
* - template_include( $path ) - for loading a different template file
* - title_parts( $parts ) - for changing the title
* - breadcrumbs( $trail ) - for changing the breadcrumbs
* - notices() - for displaying notices
*/
abstract class APP_View {
/**
* Test if this class should handle the current view.
*
* Use is_*() conditional tags and get_query_var()
*
* @return bool
*/
abstract function condition();
function __construct() {
// 'init' hook (always ran)
if ( method_exists( $this, 'init' ) )
add_action( 'init', array( $this, 'init' ) );
// $wp_query hooks
$actions = array( 'parse_query', 'pre_get_posts' );
$filters = array( 'posts_search', 'posts_clauses', 'posts_request', 'the_posts' );
foreach ( $actions as $method ) {
if ( method_exists( $this, $method ) )
add_action( $method, array( $this, '_action' ) );
}
foreach ( $filters as $method ) {
if ( method_exists( $this, $method ) )
add_filter( $method, array( $this, '_filter' ), 10, 2 );
}
// other hooks
add_action( 'template_redirect', array( $this, '_template_redirect' ) );
}
final function _action( $wp_query ) {
if ( $wp_query->is_main_query() && $this->condition() ) {
$method = current_filter();
// debug( get_class( $this ) . '->' . $method . '()' );
$this->$method( $wp_query );
}
}
final function _filter( $value, $wp_query ) {
if ( $wp_query->is_main_query() && $this->condition() ) {
$method = current_filter();
// debug( get_class( $this ) . '->' . $method . '()' );
$value = $this->$method( $value, $wp_query );
}
return $value;
}
final function _template_redirect() {
if ( !$this->condition() )
return;
if ( method_exists( $this, 'template_redirect' ) )
$this->template_redirect();
$filters = array(
'template_include' => 'template_include',
'appthemes_title_parts' => 'title_parts',
'appthemes_notices' => 'notices',
'breadcrumb_trail_items' => 'breadcrumbs',
);
foreach ( $filters as $filter => $method ) {
if ( method_exists( $this, $method ) )
add_filter( $filter, array( $this, $method ) );
}
}
}
@scribu
Copy link
Author

scribu commented Jun 3, 2012

@rilwis
Copy link

rilwis commented Jun 4, 2012

It would be better if you separate class $actions and $filters into "get" functions, that allow extended classes to change them.

@franz-josef-kaiser
Copy link

When looking at APP_View_Controller and your is_* comment, I just have to link my old ticket.

@franz-josef-kaiser
Copy link

Just saw your comment on trac. Would you mind adding an extra file here, showing your solution - I know it's much better than mine (7 month old says a lot :)

@scribu
Copy link
Author

scribu commented Jun 4, 2012

@rilwis Restricting the WP_Query actions and filters was kind of on purpose. The ones in _template_redirect() could be extended, though, but on the other hand, you can already do that, like so:

class My_View_Controller extends APP_View_Controller {

  function template_redirect() {
    add_filter( 'another_filter', array( $this, 'another_filter' ), 10, 3 );
  }

  function another_filter( $foo, $bar, $baz ) {
    // runs only when $this->condition() == true
  }
}

@rilwis
Copy link

rilwis commented Jun 5, 2012

@scribu: I don't think restricting actions & filters is a good idea. It makes the class less extendable in the future when WP (or we) adds more query actions. Of course we can do the way you suggested but it won't use the benefits of the class (the easy way to implement hooks).

I think we can do something like this:

function default_actions() {
    $actions = array( 'parse_query' );
    return apply_filter( 'app_view_controller_default_actions', $actions );
}

@scribu
Copy link
Author

scribu commented Jun 5, 2012

The main idea of the class isn't to have an easy way to add hooks; it's to control a single part of a site from a single place in the codebase.

Another idea is that you take this class and make it your own. It's a parent class, but the mechanism itself is not meant to be extensible, since then you get a mess of figuring out if a particular method should/n't automatically be hooked.

@rilwis
Copy link

rilwis commented Jun 5, 2012

I got it. Maybe I'm wrong when implement add_filter in the code above. My main idea is just stripping out the list of default actions, filters to a function, that will make child class easier to add more automatic hooks, just in case we want to use it in another framework (for ex. the list of hooks in _template_redirect is appthemes-specific, and we want to change it).

@scribu
Copy link
Author

scribu commented Jun 5, 2012

Yes, and what I'm saying is that you should change them directly in the class; and also change the APP_ prefix while you're at it. :)

@rilwis
Copy link

rilwis commented Jun 5, 2012

Oh, got it. I'm too greedy when I want a class-for-all ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment