Date post: | 15-Aug-2015 |
Category: |
Internet |
Upload: | wp-developers-club |
View: | 46 times |
Download: | 0 times |
@hellofromTonyaQuality Code by Design
Hello, I’m Tonya
Happy go-lucky Software & Electrical Engineer who finds coding, helping people, & solving problems fun.
Where can you find me?
@WP_Devs_Club
https://wpdevelopersclub.com
3 decades of professional experience
Spent 20+ years build & consulting on automated manufacturing systems
Then I found WordPress & Genesis
…and now I teach.
Know the Code Show(on Wednesdays)
@hellofromTonyaQuality Code by Design
We should care about the Quality of our code
Let’s discover the Why, How, and When.
@hellofromTonyaQuality Code by Design
“There is a big difference in writing code that works
vs.crafting quality code that
works.”
@hellofromTonyaQuality Code by Design
More predictableresults & behaviors
Easier to maintain
Easier to edit & extend
The Why
Easier to test
Elevates your craftMaximizes your fun
factor!
RespectRecognitio
n
@hellofromTonyaQuality Code by Design
Code SmellsIf you’re code
has any of these,
you might be
smelly.
@hellofromTonyaQuality Code by Design
Project Map, Spec, PlanJust start writing code without a written
project plan.
NEVER..ever…ever…
write a single line of code without a
written spec, map, or plan.
@hellofromTonyaQuality Code by Design
the Pledge
I ____________ do pledge to NEVER EVER write a single
line of code until I have a plan.
@hellofromTonyaQuality Code by Design
Common Project Spec
Child Theme
Child themeTheme code
Widgets
Custom Taxonomy
Custom Fields & Metaboxes
Portfolio
FAQ
Admin Settings Pages
Shortcodes
@hellofromTonyaQuality Code by Design
• Change the theme - functionality is gone.
• Repeating code…over & over & over
• Code is not reusable without edits
• Hard to maintain
• Hard to modify & extend
Problems with the all-in-one theme
@hellofromTonyaQuality Code by Design
• Themes are for presentation
• Plugins are for functionality
• Don’t intermingle
• Never ever repeat
• Organize by purpose
• Function - single tasking (does ONE thing only)
Rules of Thumb
• DRY
• Separation of Concerns
• Single Responsibility
• etc.
@hellofromTonyaQuality Code by Design
Say Hello to the Theme Core Plugin
CorePlugin
WidgetsCore functionality
Base ClassesAdmin Settings
Shortcodes
The companion in every project.
Example available on GitHub:https://github.com/wpdevelopersclub/WPDC_Core
@hellofromTonyaQuality Code by Design
Hello Config File
A configuration file set the starting
point before the work begins.
It gives a module and its
submodules what they need to
start their work.
@hellofromTonyaQuality Code by Design
“Abstracting fluid config & setup parameters
allows your functional code to be … reusable.”
Think ahead. Think modular.
@hellofromTonyaQuality Code by Design
What goes into a Config file?
Config
Everything that “configures”, sets a “default” state, and/or sets a starting point.
Sidebar configShortcodes$defaults
Widgets$defaults, $widget_ops, $control_ops, $control
Image sizes add_image_size()
Theme support add_theme_support()()
Custom Post Typesregister_post_type()
@hellofromTonyaQuality Code by Design
Refactored Project Scope
Portfolio Plugin
FAQ Plugin
Child Theme’sPresentational Code
Child theme
Core Plugin
These files feed their respective
code
@hellofromTonyaQuality Code by Design
Repeating patterns// Register widget areasgenesis_register_sidebar( array(
'id' => 'home-top','name' => __( 'Home - Top', 'magazine' ),'description' => __( 'This is the top section of the homepage.', ‘my-theme' ),
) );genesis_register_sidebar( array(
'id' => 'home-middle','name' => __( 'Home - Middle', 'magazine' ),'description' => __( 'This is the middle section of the homepage.', 'my-theme' ),
) );genesis_register_sidebar( array(
'id' => 'home-bottom','name' => __( 'Home - Bottom', 'magazine' ),'description' => __( 'This is the bottom section of the homepage.', 'my-theme' ),
) );genesis_register_sidebar( array(
'id' => 'after-entry','name' => __( 'After Entry', 'magazine' ),'description' => __( 'This is the after entry section.', 'my-theme' ),
) );
1
2
3
4
@hellofromTonyaQuality Code by Design
DRY Version
function register_sidebar( array $sidebars_config ) {
foreach ( $sidebars_config as $sidebar_config ) {genesis_register_sidebar( $sidebar_config );
}}
lib/support/theme-setup.php
@hellofromTonyaQuality Code by Design
Repeating patterns 2register_post_type( 'portfolio',
array('labels' => array(
'name' => __( 'Portfolio', 'my_theme' ),'singular_name' => __( 'Portfolio', 'my_theme' ),
),'exclude_from_search' => true,'has_archive' => true,'hierarchical' => true,'menu_icon' => 'dashicons-admin-page','public' => true,'rewrite' => array( 'slug' => 'portfolio', 'with_front' => false ),'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'revisions', 'page-attributes', 'genesis-seo' ),
));
register_post_type('faq',array(
'labels' => array('name' => __( 'FAQ', 'my_theme' ),'singular_name' => __( 'FAQ', 'my_theme' ),
),'exclude_from_search' => true,'has_archive' => true,'hierarchical' => true,'menu_icon' => 'dashicons-admin-page','public' => true,'rewrite' => array( 'slug' => 'faq', 'with_front' => false ),'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'revisions', 'page-attributes', 'genesis-seo' ),
));
1
2
@hellofromTonyaQuality Code by Design
DRY Version
function mycore_register_custom_post_types( array $cpts ) {foreach ( $cpts as $slug => $cpt_config ) {
register_post_type( $slug, $cpt_config );}
}
lib/custom/cpts.php
@hellofromTonyaQuality Code by Design
the DRY Principle
I will not repeat myself
I will not repeat myself
I will not
I will not repeat myselfI will not repeat myselfI will not repeat myself
I will not repeat myself
I will not repeat myself
I will not repeat myself
@hellofromTonyaQuality Code by Design
The Overachiever Patternfunction get_classes_for_grid_pattern( array $classes, $number_of_columns = 2 ) {
// If number of columns is less than 2, there's no reason to continue.// We are only allowing up to 6 columns; therefore, bail out if we// want more.if ( $number_of_columns < 2 || $number_of_columns > 6 ) {
return $classes;}
switch( $number_of_columns ) {case 6:
$classes[] = 'one-sixth';break;
case 5:$classes[] = 'one-fifth';break;
case 4:$classes[] = 'one-fourth';break;
case 3:$classes[] = 'one-third';break;
default:$classes[] = 'one-half';break;
}
global $wp_query;
if ( $wp_query->current_post % $number_of_columns == 0 ) {$classes[] = 'first';
}
return $classes;}
1
2
3
4
@hellofromTonyaQuality Code by Design
Abstract columns styling classes:
function get_column_class( $number_of_columns ) {$column_classes = array(
'', // index 0'', // index 1'one-half', // index 2 = represents 2 columns'one-third', // index 3'one-fourth', // index 4'one-fifth', // index 5'one-sixth', // index 6
);
return $number_of_columns <= 6? $column_classes[ $number_of_columns ]: '';
}
switch( $number_of_columns ) {case 6:
$classes[] = 'one-sixth';break;
case 5:$classes[] = 'one-fifth';break;
case 4:$classes[] = 'one-fourth';break;
case 3:$classes[] = 'one-third';break;
default:$classes[] = 'one-half';break;
}
function get_classes_for_grid_pattern( array $classes, $number_of_columns = 2 ) {$column_class = get_column_class( $number_of_columns );if ( ! $column_class ) {
return $classes;}$classes[] = $column_class;
1
2
@hellofromTonyaQuality Code by Design
Abstract out the 1st column code
function get_first_column_styling_class( $number_of_columns ) {global $wp_query;return $wp_query->current_post % $number_of_columns == 0 ? 'first' : '';
}
global $wp_query;if ( $wp_query->current_post % $number_of_columns == 0 ) {
$classes[] = 'first';}
function get_classes_for_grid_pattern( array $classes, $number_of_columns = 2 ) {$column_class = get_column_class( $number_of_columns );if ( ! $column_class ) {
return $classes;}$classes[] = $column_class;
$first = get_first_column_styling_class( $number_of_columns );if ( ! $first ) {
$classes[] = $first;}
return $classes;}
Do you see an opportunity to refactor more?
3
@hellofromTonyaQuality Code by Design
Getting it down to 1 thing:
function get_classes_for_grid_pattern( $number_of_columns = 2 ) {$column_class = get_column_class( $number_of_columns );if ( ! $column_class ) {
return false;}
$first = get_first_column_styling_class( $number_of_columns );
return $first ? array( $column_class, $first ) : array( $column_class );}
function get_classes_for_grid_pattern( array $classes, $number_of_columns = 2 ) {$column_class = get_column_class( $number_of_columns );if ( ! $column_class ) {
return $classes;}$classes[] = $column_class;
$first = get_first_column_styling_class( $number_of_columns );if ( ! $first ) {
$classes[] = $first;}
return $classes;}
Now what is the single responsibility of the function?
1
2
@hellofromTonyaQuality Code by Design
Complete Refactored Codeadd_filter( 'post_class', __NAMESPACE__ . '\\add_to_post_classes_for_grid_pattern' );function add_to_post_classes_for_grid_pattern( array $classes ) {
$column_classes = get_classes_for_grid_pattern( $classes, 2 );return empty( $column_classes ) ? $classes : array_merge( $classes, $column_classes );
}
function get_classes_for_grid_pattern( $number_of_columns = 2 ) {$column_class = get_column_class( $number_of_columns );if ( ! $column_class ) {
return false;}
$first = get_first_column_styling_class( $number_of_columns );
return $first ? array( $column_class, $first ) : array( $column_class );}
function get_first_column_styling_class( $number_of_columns ) {global $wp_query;return $wp_query->current_post % $number_of_columns == 0 ? 'first' : '';
}
function get_column_class( $number_of_columns ) {$column_classes = array(
'', // index 0'', // index 1'one-half', // index 2'one-third', // index 3'one-fourth', // index 4'one-fifth', // index 5'one-sixth', // index 6
);
return $number_of_columns <= 6? $column_classes[ $number_of_columns ]: '';
}
@hellofromTonyaQuality Code by Design
Other refactoring for decision trees
$processing_funcs = array('text' => __NAMESPACE__ . '\\process_text_field','number' => __NAMESPACE__ . '\\process_number_field','date' => __NAMESPACE__ . '\\process_date_field',
);
$func_name = $processing_funcs[ $field->type ];$func_name( $field );
Variable function
$processing_funcs = array('text' => __NAMESPACE__ . '\\process_text_field','number' => __NAMESPACE__ . '\\process_number_field','date' => __NAMESPACE__ . '\\process_date_field',
);
call_user_func( $processing_funcs[ $field->type ], $field );
call_user_func() or call_user_func_array()
@hellofromTonyaQuality Code by Design
Views are for HTMLclass My_Superduper_Widget extends WP_Widget {
function form( $instance ) {
//* Merge with defaults$instance = wp_parse_args( (array) $instance, $this->defaults );
?><p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>”><?php _e( 'Title', 'genesis' ); ?>:
</label><input type="text" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>”
</p>
class My_Superduper_Widget extends WP_Widget {
protected $form_view = '/lib/views/my-superduper-widget.php';
function form( $instance ) {
//* Merge with defaults$instance = wp_parse_args( (array) $instance, $this->defaults );
include( CHILD_DIR . $this->form_view );
@hellofromTonyaQuality Code by Design
• Well organized
• Focused & purposeful
• Modular
• More maintainable
• Changes occur in one
spot
• Much easier to test
• Skinny
• More predictable
@hellofromTonyaQuality Code by Design
Purposeful naming
function do_classes( array $classes, $num = 2 ) {// Number the columns required is less than 2, then...if ( $num < 2 ) {}
}
function get_classes_for_grid_pattern( array $post_classes, $number_of_columns = 2 ) {// do some cool stuff....
}
What does do_classes() do? What does $num represent?
@hellofromTonyaQuality Code by Design
Purposeful Naming ConventionName functions, methods, and classes by what they
DO.Name variables by what they represent.
@hellofromTonyaQuality Code by Design
Let your code tell…• More readable code
• Self-documenting
• Skinny functions & methods
• Defines the purpose to ensure the code supports its
parent.
@hellofromTonyaQuality Code by Design
Additional Refactoringfunction my_cool_shortcode( $atts ) {
$defaults = array('after' => '','before' => '','format' => get_option( 'date_format' ),'label' => '',
);
$atts = shortcode_atts( $defaults, $atts );// now do the shortcode work
}
function my_other_cool_shortcode( $atts ) {
$defaults = array('after' => '','before' => '','format' => get_option( 'time_format' ),'label' => '',
);
$atts = shortcode_atts( $defaults, $atts );// now do the shortcode work
}
@hellofromTonyaQuality Code by Design
Refactored Functional Shortcodesfunction my_cool_shortcode( $atts ) {
mcp_get_shortcode_atts( 'my_cool_shortcode', $atts );// now do the shortcode work
}
function my_other_cool_shortcode( $atts ) {
mcp_get_shortcode_atts( 'my_other_cool_shortcode', $atts );// now do the shortcode work
}
function mcp_get_shortcode_config( $config_key ) {static $config = array();if ( empty( $config ) ) {
$config = require_once( MY_CORE_PLUGIN_DIR . ‘/config/shortcodes.php' );}
if ( array_key_exists( $config_key, $config ) ) {return $config[ $config_key ];
}
new InvalidArgumentException(__( 'Shortcode configuration key does not exist.', 'my_cool_plugin' )
);}
function mcp_get_shortcode_atts( $config_key, &$atts ) {
$defaults = wp_parse_args(mcp_get_shortcode_config( $config_key ),array(
'after' => '','before' => '',
));
$atts = shortcode_atts( $defaults, $atts );}
@hellofromTonyaQuality Code by Design
Refactored Shortcodes in OOP<?php namespace WPDevsClub_Core\Shortcodes;
abstract class Shortcode implements I_Shortcode {
protected $config = array();protected $defaults = array();protected $atts = array();
public function __config( array $config ) {$this->init_config( $config );$this->init_hooks();
}
protected function init_hooks() {add_shortcode(
$this->config['shortcode_key'],array( $this, 'do_shortcode' )
);}
public function do_shortcode( $atts ) {$this->merge_atts_with_defaults( $atts );$this->do_functional_work();return $this->render();
}
protected function render() {do_start();include( $this->config['view_file'] );return ob_get_clean();
}
protected function init_config( $config ) {$this->config = $config;$this->defaults = wp_parse_args(
$this->config['defaults'],array(
'after' => '','before' => '',
));
}
abstract function do_functional_work();
protected function merge_atts_with_defaults( $atts ) {$this->atts = shortcode_atts( $this->defaults, $atts );
}}
@hellofromTonyaQuality Code by Design
Super Sniffer Clues
1.Long functions, classes, or methods
2.Vague naming
3.Multi-leveled conditional trees (selects & ifs)
4.Asking first. (not being ready to work at the
start)
@hellofromTonyaQuality Code by Design
More infoKeys to Writing Quality Code
https://wpdevelopersclub.com/keys-to-writing-quality-code/
Software Development Core Principles
https://wpdevelopersclub.com/software-development-core-principles/
Come join me each Wednesday onKnow the Code Show
https://hub.wpdevelopersclub.com/know-the-code-show-podcast-tutorials/