wordpress development paradigms, idiosyncrasies and other big words
Post on 27-Jan-2015
117 Views
Preview:
DESCRIPTION
TRANSCRIPT
WordPress Development Paradigms, Idiosyncrasies
and Other Big Words
Tom Auger, Zeitguys inc.
WordCamp Montreal, 2011
Introduction
• About me:– Developer of standalone RIAs and large-scale
server-side systems– Siebel integrator and database admin– Perl, PHP, JavaScript, ActionScript 3, SQL,
Python, Java, etc.
– Very much a “DIY Guy”
Surprising Learning Curve
• Lots of files!
• Confusing themes!
• Arcane symbols!– sprintf(__('Something %s', 'domain'), 'weird');
• Hooks everywhere!– add_action('init', 'obscure_func');– $var = apply_filters('obscure_filter');
• Oh my!
The biggest challenge...
• was learning what the topics were that I needed to learn about:– themes, templates, template parts and child themes– database access and the WP_Query in all its
instances– action and filter hooks– WordPress object types: posts, taxonomies and meta
data– i18n (l10n) – where to get reliable, high-level information and help
Meh.
• Let's just go skunkworks.
• I'm a seasoned dev after all...
DIY Approach = Low Level
• Direct database access
• In-place editing of plugin files
• Forking theme files
• In-place (gasp) editing of core files
• Brute force JavaScript injection
• Rolling your own sanitization and security measures
The Bad News
• Unsustainable, fragile to updates
• Non-scalable and highly one-off
• Big security gaps
• Missing out on built-in value-add intelligence
• Re-inventing the wheel
Example: Adding <p> to content
• The starting code:
$posts = get_posts(array(
'post_type' => 'my-custom-type'
));
foreach($posts as $post){
echo '<div>';
echo $post->post_content;
echo '<div>';
}
Option 1: Manually Wrap
• Wait... what about multiple paragraphs? or enclosing block tags?
$posts = get_posts(array(
'post_type' => 'my-custom-type'
));
foreach($posts as $post){
echo '<div>';
echo '<p>'.$post->post_content.'</p>;
echo '<div>';
}
Option 1b: RegEx
• Wheel, meet Tom. Tom, meet Wheel.
$blockElements = implode("|", array('ADDRESS','BLOCKQUOTE','CENTER','DIR','DIV','DL', etc...);
$added_p = preg_replace(
'/(?:\s*<('.$blockElements.')(?:\s+[^>]+\s*)*>\s*(.+?)\s*<\/\1>(?:\r|\n|$)*)|(?:\s*(.+?)\s*(?:\r|\n|$|(?=<(?:'.$blockElements.')>))+)/is','<p$1>$2$3<\/p\1>\n', $post->post_content);
But wait... doesn't WP already do this?
• wpautop(), defined in formatting.php handles all the logic and edge cases
foreach($posts as $post){
echo '<div>';
echo wpautop($post->post_content);
echo '<div>';
}
So what about the_content()?
• Let's exploit those globals and template tags
foreach($posts as $post){
setup_postdata($post);
echo '<div>';
the_content();
echo '<div>';
}
wp_reset_postdata();
So how does the_content() do it?
• IDE – site-wide search (or use phpxref or WPSeek)
• In post-template.php:
function the_content($more_link_text = null, $stripteaser = 0) {$content = get_the_content($more_link_text, $stripteaser);$content = apply_filters('the_content', $content);$content = str_replace(']]>', ']]>', $content);echo $content;
}
Keep following the White Rabbit
• Search for "the_content" within default-filters.php• It runs the following functions:
– capital_P_dangit– wptexturize– convert_smilies– convert_chars– wpautop– shortcode_unautop– prepend_attachment
• That's a lot of value add!
I want all that other good stuff, too!
• Leverage all those the_content methods
foreach($posts as $post){
echo '<div>';
echo apply_filters(
'the_content',
$page->post_content);
echo '<div>';
}
The lesson thus far
• TMTOWTDI, but not all ways are equal
• Use the method that:– is the most abstracted (high level)– requires the least amount of additional code– encapsulates the most value-added
intelligence
“Whenever you're going to write a code block that seems to be quite generic and common, check first if there's a WP function that can do it for you.” – Ozh Richard, de facto WP Plugin expert
Big Words
• Paradigm: a model or pattern that defines a way of thinking or working with a tool
• Idiosyncrasy: an unusual, distinctive and recognizable trait or pattern
• Idiom: a form of expression, natural to a language or context
WordPress Paradigms and Patterns
• Plugins
• Templates
• Hooks
• Post Types and Taxonomies
WordPress Idioms
• The Loop and the Query
• Template tags (and get_)
WordPress Idiosyncrasies
• $wpdb
• wp_enqueue_script()
• No TIME!! (part deux?)– wp_kses(), esc_attr()– body_class(), post_class(), comment_class()
Paradigm: Plugins
• Often confused with Widgets• Nothing more than a distributableway to
inject code at the start of the loading / execution cycle
• Not to be confused with pluggable functions
<?php /*Plugin Name: Name Of The Plugin*/?>
Paradigm:Themes and Templates
Themes
• Control visual look-and-feel of a site
• Require at minimum only index.php and style.css
• But wait, there's more, with functions.php:– Widgets– Admin options– Changes to core behaviour
Child Themes
• Best way to customize a theme for a specific client application
• Clever overloading scheme
• Leverage existing intelligence
%> wp-content/themes/my-child-theme/style.css/*Theme Name: Twenty Ten Child ThemeTemplate: twentyten*/
@import url('../twentyten/style.css');
Template Files
• PHP files, loaded contextually based on Query string
• Overloaded using a strict Template Hierarchy
• Follows conditional hierarchy
Template Parts
• Breaks down template hierarchy to custom level of granularity
• Code reuse design pattern• Allows multiple templates to share
sections
• Eg: events-by-date.php, events-by-category.php, all-events.php
• Note: local vars lose scope (uses "get")include(locate_template('template-part'));
get_template_part('template', 'part');
Page Templates
• Templates that can be admin-applied to Pages (not posts)
<?php/*Template Name: 3 Column*/?>
Post Formats
• Standardized contributor-assignable taxonomy for posts
• Meant to be consistent across all blogs
• aside, chat, gallery, link, image, quote, status, video, audio
<?php get_template_part( 'format', get_post_format());?>
Paradigm:Hooks, Actions and Filters
“WordPress action hooks are a means of providing a way for other developers to insert their own code in specific locations within your code, in order to change or expand the functionality of your code.” – Nathan Rice
http://www.nathanrice.net/blog/an-introduction-to-wordpress-action-hooks/
Hooks
• Spiritually related to GoF's Template Method Design Pattern
• Alternative extension method to subclassing
• Developers create hooks wherever they think other devs may wish to extend their code
• Documented under PluginAPI, but you will encounter them sooner than that
Actions vs. Filters
• Actions inject code into a workflow
• Filters take a variable, modify it and return the same type
// set up action hookdo_action('hook-name');
// set up filter hook$variable = apply_filters('filter-name',
$variable);
Leveraging Action Hooks
• Use an action hook to do something at a specific point in the execution cycle
• Multiple sources can register for a single action hook
add_action('init', 'stuff_to_do_at_init');
General Execution Order
• muplugins_loaded• plugins_loaded• sanitize_comment_cookies• setup_theme• load_textdomain• after_setup_theme• auth_cookie_malformed• set_current_user• init• widgets_init• register_sidebar• wp_register_sidebar_widget• wp_loaded• parse_request*• send_headers*• parse_query*• pre_get_posts*• posts_selection• wp*
• template_redirect• get_header• wp_head• wp_enqueue_scripts• wp_print_styles• wp_print_scripts• get_template_part_loop• loop_start*• the_post*• loop_end*• get_sidebar• dynamic_sidebar• get_search_form• wp_meta• get_footer• twentyten_credits• wp_footer• wp_print_footer_scripts• shutdown
http://codex.wordpress.org/Plugin_API/Action_Reference#Actions_Run_During_a_Typical_Request
Action examples
add_action('init', 'register_my_custom_post');
add_action('init', 'register_my_custom_taxonomy');
add_action('widgets_init', 'register_my_widget');
add_action('wp_enqueue_scripts', 'add_my_js_libs');
add_action('save_post', 'save_my_custom_meta');
Leveraging Filter Hooks
• Use a filter hook to modify output or an internal variable
add_filter('the_content', 'change_content');
function change_content($content){$content = 'All your base are belong to us!!!';return $content;
}
Priority and Parameters
• add_filter can take two additional arguments: $priority and $num_args
add_filter('contextual_help', 'my_help', 10, 3);
function my_help($text, $screen_id, $screen){if ($screen_id == 'edit-my_custom_post_type'){
$text = '<p>You are on your own pal</p>';}return $text;
}
Filter Examples
add_filter( 'excerpt_more', 'custom_more' );
add_filter( 'excerpt_length', 'my_length', 999 );function my_length($length){
return 6; // six is average, right?}
Existing Filters and Actions
• WordPress already registers filters and actions
• Find them in wp-includes/default-filters.php
• remove_filter($tag, $function, $priority)
• remove_action() is an alias
So How Do I Find Them?
• Partial lists in the codex:– codex.wordpress.org/Plugin_API/Filter_Reference
– codex.wordpress.org/Plugin_API/Action_Reference • Use a good IDE like Eclipse/PHP, Aptana,
NetBeans, PHPStorm etc... and search for:– do_action(), and do_action_ref_array()– apply_filters(), and apply_filters_ref_array()
• http://adambrown.info/p/wp_hooks
Good Citizenship
• Add your own action and filter hooks wherever it may be appropriate:– things a dev may wish to override but you
don't want to expose to an Admin user– default values (max/min limits etc)– logical spots for extension
• Example: zg-event-query (filter)
• For the love of Pete, document it!
Idiom:The Loop and The Query
Getting Your Content: "The Loop"
• One "The Loop" per page
• Sets globals (eg: $post)
• Drives all template tags (eg: the_content())
• Driven by "the query": the request string
• Affects conditional tags
• Can be supplanted by query_posts()
Secondary Loops
• Used in widgets and sidebars, sub-content and related content
• Must be careful not to clobber globals and affect the main Loop
• Leverage get_posts() or get_pages() for inobtrusive secondary loops
• Or get dirty with WP_Query class• Either way, be sure to read Codex WP_Query
Suspicious Stuff
• Leverage template tags in secondary loops by:– using the_post() method on WP_Query
instance– or setup_postdata() on a $post object
$the_query = new WP_Query( $args );while ( $the_query->have_posts() ){ $the_query->the_post();
the_title();}wp_reset_postdata();
Template Tags and get_
• Template tags– Used exclusively within The Loop– Always echo their results– Rarely (?) take arguments (always working on
the global $post object)
• get_– Most template tags have a get_ equivalent– Does not echo result– Usually takes the ID of a post as argument
Template Tag vs. get_
• Use Template Tag when:– you're in The Loop!
• Use get_ when:– you want the value, not the echo– you're not in The Loop– you are in the loop but want a related item
(eg: attachment)
Idiosyncrasy:$wpdb
wpdb Class
• Instantiated once and globalized in $wpdb
• Provides API to low-level DB calls– $wpdb->query()– $wpdb->get_results()– $wpdb->get_var()
Example: Events
• Custom post type "event"
• Categorized by taxonomy "event-type"
• Event start_date stored in meta
• Let's select: all 'speaking' events with a start date of '2011-07-09'
Pure SQLselect * from wp_postsjoin wp_term_relationships
on ID = object_idjoin wp_term_taxonomy
on wp_term_taxonomy.term_taxonomy_id =wp_term_relationships.term_taxonomy_idand taxonomy = 'event-type'
join wp_termson wp_term_taxonomy.term_id =wp_terms.term_idand wp_terms.slug = 'speaking'
join wp_postmetaon wp_postmeta.post_id = IDand meta_key = 'event-date'and meta_value = '2011-07-09'
wherepost_type = 'event'and post_status = 'published'
More Properer SQLselect * from wp_posts as Pjoin wp_term_relationships TR_EVENT
on P.ID = TR_EVENT.object_idjoin wp_term_taxonomy TT_EVENT
on TT_EVENT.term_taxonomy_id =TR_EVENT.term_taxonomy_id
TT_EVENT.taxonomy = 'event-type'join wp_terms T_EVENT
on TT_EVENT.term_id = T_EVENT.term_idand T_EVENT.slug = 'speaking'
join wp_postmeta M_DATEon M_DATE.post_id = P.IDand M_DATE.meta_key = 'event-date'and M_DATE.meta_value = '2011-07-09'
whereP.post_type = 'event'and P.post_status = 'published'
Don't Use Table Names!select * from {$wpdb->posts} Pjoin {$wpdb->term_relationships} TR_EVENT
on P.ID = TR_EVENT.object_idjoin {$wpdb->term_taxonomy} TT_EVENT
on TT_EVENT.term_taxonomy_id =TR_EVENT.term_taxonomy_id
TT_EVENT.taxonomy = 'event-type'join {$wpdb->terms} T_EVENT
on TT_EVENT.term_id =T_EVENT.term_idand T_EVENT.slug = 'speaking'
join {$wpdb->postmeta} M_DATEon M_DATE.post_id = P.IDand M_DATE.meta_key = 'event-date'and M_DATE.meta_value = '2011-07-09'
whereP.post_type = 'event'and P.post_status = 'published'
More Saferer Query$wpdb->prepare("select * from {$wpdb->posts} Pjoin {$wpdb->term_relationships} TR_EVENT
on P.ID = TR_EVENT.object_idjoin {$wpdb->term_taxonomy} TT_EVENT
on TT_EVENT.term_taxonomy_id =TR_EVENT.term_taxonomy_id
TT_EVENT.taxonomy = 'event-type'join {$wpdb->terms} T_EVENT
on TT_EVENT.term_id =T_EVENT.term_idand T_EVENT.slug = %s
join {$wpdb->postmeta} M_DATEon M_DATE.post_id = P.IDand M_DATE.meta_key = 'event-date'and M_DATE.meta_value = %s
whereP.post_type = 'event'and P.post_status = 'published'",
'speaking', '2011-07-09');
But, Whenever We Can...
get_posts(array('post_type' => 'event','tax_query' => array(array(
'taxonomy' => 'event-type','field' => 'slug''terms' => 'speaking'
)),'meta_query' => array(array(
'key' => 'event-date','value' => $todays_date
))));
Use Built-Ins Whenever You Can
• Take advantage of cacheing and optimization
• Future-proof code
• Chances are, there's a function or method to do what you want– Look in Codex under WP_Query– Look in source under query.php, post.php,
category.php, taxonomy.php
Idiosyncrasy:wp_enqueue_script
We Love jQuery
• (Don't)– Jump into header.php– Add <script src="my-jquery-min.js"></script>
• (Don't)– add_action('wp_head', 'add_my_jquery');
Enqueue = The Canadian Way, eh?
• DO
add_action('wp_enqueue_scripts', 'add_my_scripts');function add_my_scripts(){
wp_enqueue_scripts('jquery-ui-core',get_stylesheet_directory_uri() .
'/script',array('jquery'),'1.8.14',false // in-header
);}
add_action('admin_enqueue_scripts', 'add_admin_scripts'); // careful here...
No One Likes Conflict
• jQuery runs in "no-conflict" mode...
jQuery(document).ready(function($) { $('#mydiv').css({color:'red', border:'2px solid red'});});
(function($) {var test = "hello, world!";function testMe(){
alert(test);}$(document).ready(testMe);
})(jQuery);
Resources and Forums
• Documentation:– Codex (of course)– Google: "wordpress add_action"– Adam Brown– WPSeek, phpxref– Your own IDE
• Help:– wordpress.org/support (yeah, not really...)– www.experts-exchange.com
• http://www.experts-exchange.com/Web_Development/Blogs/WordPress/
– wordpress.stackexchange.com
Thanks!
www.tomauger.com
www.zeitguys.com
@TomAuger
Other Stuff for more time
Paradigm:Post Types and Taxonomies
Posts
• Posts are the atomic element for all WordPress content – "actual" posts– pages– comments– attachments– drafts and revisions– custom post types
Taxonomies
• Taxonomies are the relational device that group posts together.– categories, – tags, – nav menus– post formats?– custom taxonomies
• Groupings that are shared by different items
What About Meta?
• Use meta for discrete, non-grouping data, unique to the content item (eg: price)
• Sortables (eg: year)
• Groups that are not meaningfully shared (eg: Otto's TV show seasons across different shows)
Custom Post vs. Custom Tax
• Use Custom Post for content that:– should not appear in your feed– represents a distinct type of "content"– is always associated with a set of metadata– may require a customized input form– needs a separate capability / access– needs its own private taxonomy
• Examples: products, events, albums
Custom Post vs. Custom Tax
• Use a custom taxonomy when:– You want additional ways to group items
together– You want a way to filter out a set of items– You want a new metabox
• Eg: colour, software posts, manufacturers
top related