#complexwp | wplib.org | @mikeschinkel Developing Complex WordPress Sites* *Without Fear of Failure Presenter: Mike Schinkel WordCamp Raleigh 2015
#complexwp | wplib.org | @mikeschinkel
Developing Complex WordPress Sites**Without Fear of Failure
Presenter: Mike SchinkelWordCamp Raleigh 2015
#complexwp | wplib.org | @mikeschinkel
What to Expect• Target Audience for the Talk• Overview of Professional Workflow• Use of Object Orientation• Demo of code from a real-world project• How to make it easy by using WPLib
#complexwp | wplib.org | @mikeschinkel
Prerequisites• Experience building WordPress websites
• Comfortable developing in PHP
#complexwp | wplib.org | @mikeschinkel
About Me
• Self-Styled WordPress Architect• Typical Work: $100k+ Agency Projects • Many WordCamps, LoopConf• WordPress is my 4th developer ecosystem
2006-2009: Drupal1993-2006: Visual Basic 1986-1993: Clipper - a dBase Compiler
• Yada, yada
#complexwp | wplib.org | @mikeschinkel
Avoiding a WordPressHouse of Cards
A.K.A. AvoidingKevin Spacey
#complexwp | wplib.org | @mikeschinkel
Best For Large Budget Projects
Where clients just expect
us to:
#complexwp | wplib.org | @mikeschinkel
This approach NOT for "Tinkerers"
Who want to fiddle with it after
you leave.
(And then yell at you and ask for support when they break it.)
#complexwp | wplib.org | @mikeschinkel
Disallow Adding of Plugins and Themes in the Admin Consule
• Use a Custom Theme
• Minimize 3rd Party Plugins
#complexwp | wplib.org | @mikeschinkel
Use a Professional Workflow● A Professional IDE
o PhpStorm+XDebug● Version Control/Branching
o For Features, and o For Bug Fixes
● A Deployment Processo Development (Local)o Test Servero Staging Servero Production Server
● See "Advanced WordPress Workflow” o That is Micah Wood's talk, next session...
#complexwp | wplib.org | @mikeschinkel
Use an Intelligent Process• Focus on Requirements
• Create clickable mockups using moqups.com
• Then Architecture• Decide on Post Types, Taxonomies, etc
• Separate Theming from Architecture & Backend Coding• Best if people with the different skills
are in the different roles
• Maximize “Deployable” Code• User-entered Configuration Sux
#complexwp | wplib.org | @mikeschinkel
Use an Object Oriented Architecture•Heard of MVC?
• Embrace Models and Views.
• And Items,• And Lists,• And Applications.
• And Helpers, • And Templates, • And Modules.
• More on those in a bit…
#complexwp | wplib.org | @mikeschinkel
Wherest thou Objects?• Post Types• Taxonomies• User Roles• And more!
#complexwp | wplib.org | @mikeschinkel
• Controllers = the "C" in MVC
• But Objects/Classes for Routing != WordPress
• But you can usually hack it:
URL Routing In WordPresshttp://tinyurl.com/wp-url-routing
WordPress handles the Controller (mostly)
#complexwp | wplib.org | @mikeschinkel
DEMO
Reviewing the Code of a Real World Client Project
Warts and All
(see http://github/wplib for online examples)
#complexwp | wplib.org | @mikeschinkel
Make it easy with WPLib• Designed for PHP Developers• Assumes Persistent Cache• Great for Building Reusable Code
#complexwp | wplib.org | @mikeschinkel
•It is Small• Does not try to do that much
•Highly Consistent!•Presents Structured Constraints•Compatible with (all?) other Frameworks
• WPLib provides incremental value• Can be used a little, or a lot• Solves a problem no one else(?) is addressing
WPLib's Positive Attributes
#complexwp | wplib.org | @mikeschinkel
• One Application per site
• Provides root class for a site/app.
• Provides global but site-specific functionality, i.e.
$list = MyApp::get_featured_slide_list([$query]);
MyAPP::the_current_campaign_html();
$url = MyAPP::get_asset_url( 'images/visa-logo.png' );
if ( MyApp::is_featured_slider_enabled() ) {
//...
}
The Application Class
#complexwp | wplib.org | @mikeschinkel
Theme Class• One Theme Class per theme
• Put your theme hooks here, e.g.'wp_head''wp_enqueue_scripts'
• Location for theme global functionality, e.g.$theme->the_ad_html();$theme->the_slider_html();
• Lots of functionality in parent class, e.g.$theme->the_header_html(); // same as get_header()$theme->the_site_name();$page_id = $theme->front_page_id();
#complexwp | wplib.org | @mikeschinkel
Model Classes• Most of the “business logic" goes here.
• All the “facts” about a type of Item
• One per Item type
class MyApp_Story_Model extends WPLib_Post_Model {
function has_sponsor() {
$has = get_post_meta( $this->ID(), '_myapp_has_sponsor', true ); return 1 == intval( $has );
}
}
#complexwp | wplib.org | @mikeschinkel
View Classes• Specific methods for outputting Item info
• One or more Views per Item type
• Maybe one View for HTML, one for JSON, etc.
• Most functionality needed in parent class
class MyApp_Featured_Slide_View extends WPLib_Post_View_Base {
function the_embed_code_html() {echo $this->embed_code();
}
function the_embed_code_textarea() {echo htmlspecialchars( $this->embed_code() );
}
}
#complexwp | wplib.org | @mikeschinkel
Item Classes• Container for a Model and a View.• ONLY class of Model+View+Item you actually use• Most functionality in the parent class• Rarely has much if any code
class MyApp_Campaign extends WPLib_Post_Base {
const POST_TYPE = MyApp_Campaigns::POST_TYPE; const VAR_NAME = 'campaign';
}
#complexwp | wplib.org | @mikeschinkel
List Classes• Smart arrays of Items• Can be custom classes with use-case specific methods• But usually you only need the Default List Class
foreach( MyApp_Featured_Slides::get_list() as $slide ) {$slide->the_template( 'slide-card' );
}
// OR JUST:
MyApp_Featured_Slides::get_list()->the_template( 'slide-card' );
#complexwp | wplib.org | @mikeschinkel
Helper Classes•Think of a Helper as "A List of Related Functions"
•Contributes Static Methods •To your Application class•We do the same for the WPLib class
•Enables making small, easy to learn APIs•The Application class is the method "router"•Helper Method names must be unique across an Application
•Better approach than a single "God" Class•Allows for many smaller files •Clearer source code organization •Better source file management
#complexwp | wplib.org | @mikeschinkel
Helper Classes (cont'd)class MyApp_Date_Range extends WPLib_Module_Base { static $_post_types = array(); static function on_load() { self::register_helper( __CLASS__, 'MyApp' ); } static function register_date_range_post_type( $post_type ) { self::$_post_types[ $post_type ] = $post_type; } static function date_range_post_types() { return self::$_post_types; }}MyApp_Date_Range::on_load();
//----------MyApp::register_date_range_post_type( MyApp_Story::POST_TYPE );print_r( MyApp::date_range_post_types() );
#complexwp | wplib.org | @mikeschinkel
Module Classes• Provides Functionality
• Like a Plugin
• Designed for Developers• Unlike a Plugin
• Designed to be small • If one grows too big, split it up!
• Should be highly cohesive• Group related functionality together
#complexwp | wplib.org | @mikeschinkel
• A Main "Entry Point" file• Contains a main class• Should hook all hooks needed on page load• Optimized to load efficiently
• Zero or more /includes/ files• One class per file• Include files are autoloaded
• Architected to be Composible• Reusability: Yeah Baby!
Module Classes (cont'd)
#complexwp | wplib.org | @mikeschinkel
WPLib Templates• Like Template Parts
• Not template files like single.php, home.php, et. al.
• Typically scoped to an Item • Or scoped to the Theme class ($theme) • Or with no scope (called using MyApp::)
• Loaded by $item->the_template('part') method• Templates in {theme}/templates/{part}.php
#complexwp | wplib.org | @mikeschinkel
Constraints are your Friends• Templates should contain NO
implementation details
•Should contain ONLY site-specific HTML or CSS and method calls.
•No WP_Query, no get_post_meta(), etc.
#complexwp | wplib.org | @mikeschinkel
• Contains NO site-specific HTML or CSS•Auto-generate "the_" methods
•For rendering output•Conventions for escaping output
•"_url"•"_attr"•"_html" •suffix, etc.
View Methods
#complexwp | wplib.org | @mikeschinkel
A Few Well-Known Constants•POST_TYPE
•Use to specify the post type in a Post Type Module.
•TAXONOMY•Use to specify the taxonomy in a Taxonomy Module.
•ROLE•Use to specify the user role in a User Role Module.
•VAR_NAME•Use to specify an $item's auto assigned variable name in a template.
•INSTANCE_CLASS•Use to specify the $item's class name in an MVI module class.
#complexwp | wplib.org | @mikeschinkel
To Learn More about WPLib•Clone from GitHub, Review sample apps:
•github.com/wplib
•Visit wplib.org •For Quick Start and other Docs
•Submit issues on GitHub:•github.com/wplib/wplib/issues
•Ask questions on Twitter:•@mikeschinkel / @wpscholar
•Discuss on Slack•Ok: wordpress.slack.com (mikeschinkel/wpscholar)•Best: thecodersguild.slack.com#wplib
•Email [email protected] for access
#complexwp | wplib.org | @mikeschinkel
Questions?
#complexwp | wplib.org | @mikeschinkel
THANK YOU
Mike [email protected]
about.me/mikeschinkel
See also: www.slideshare.net/mikeschinkel