WordPress
workshop

Jaime Martínez | @jmslbam | Level Level | #svsim | 10 mrt 2014

Hola

Jaime Martinez



Happy coder @ Level Level
Thnx for the time!

Feature contributor and tester WP-CLI

Long time WordPress enthousiast

And you are?


Developers?
Designers?
Marketing?
Site owners?
Shop owners?

Level?

0 sites?

1 site?

2+ sites?

5+ sites?

10+ sites?

Overall thought

Find the right methods and tools

Overall thought


DRY #

Don't repeat yourself

DRY results in flexible and maintainable software

Let me seduce you to the 
proper WordPress side

So new to WordPress and themes?


Start here

http://make.wordpress.org/docs/theme-developer-handbook/

kthxbye

What do you need?

Some knowledge of HTML & PHP comes in handy
and some developertools

Handbook *

Intro, just read this

http://codex.wordpress.org/WordPress_Semantics#Terminology_Related_to_Design


and then the handbook :)

http://make.wordpress.org/docs/theme-developer-handbook/


*Read back at home or the office

Tools and skills *

Editor

Sublime, PHPStorm



Browsers

FireFox and Web Developer + FireBug

Chrome and Web Developer + Chrome tools

Internet Explorer + developer toolbar or modern.ie || ievms


The setup *


Online hosting


MAMP


Otherwise

Local with Homebrew and php-osx #


Recommended ++

Varying Vagrant Vagrants or an equal

Learn to debug

Turn on debug info! #

wp-config.php

define( 'WP_DEBUG', true );

if ( WP_DEBUG ) {

	define( 'SCRIPT_DEBUG', true );

	define( 'WP_DEBUG_LOG', true );

	define( 'WP_DEBUG_DISPLAY', true );

	@ini_set( 'display_errors', 1 );

}

Turn on debug info!

php.ini
display_errors = On;

error_reporting = E_ALL | E_STRICT; 

Also install Xdebug


Thanks Barry!

Development plugins

Debug bar | more info


What the file | more info


Use proper plugins

 without errors or notices 

Gravity Forms

Advanced Custom Fields  with the WP-CLI add-on

wp-pagenavi
Download monitor
Woo Sidebars
WP DB Migrate (Pro)

So where's the party?

User proper themes

_s
Roots
Genesis Framework #


Bones
Hybrid
Thematic

Theme anatomy

wp-content/themes/* 

Themes consist of a collection of HTML, CSS, PHP and probably JavaScript files

  • style.css
  • functions.php
  • index.php
  • ...

style.css

Must have!

/*
Theme Name: Roots
Theme URI: http://roots.io/
Description: Roots is a WordPress starter theme based on HTML5 Boilerplate & Bootstrap.
Version: 6.5.2
Author: Ben Word
Author URI: http://benword.com/

License: MIT License
License URI: http://opensource.org/licenses/MIT
*/ 

functions.php

don't put everything in this file... organize it!


https://github.com/Automattic/_s/blob/master/functions.php

https://github.com/roots/roots/blob/master/functions.php

functions.php


Template hierarchy

Say WHUT? #

Template hierarchy
Custom Post Types #

The Template file used to render the Archive Index page for a Custom Post Type

  1. archive-{post_type}.php – If the post type is product, WordPress will look for archive-product.php.
  2. archive.php
  3. index.php

Custom template

Only for pages, needs plugin for CPT's

<?php
/*
Template Name: Contact
*/
?>

Template hierarchy Page #

The Template file used to render a static page (page post-type)

  1. Custom template file – The Page Template assigned to the Page. See get_page_templates().
  2. page-{slug}.php – If the page slug is recent-news, WordPress will look to use page-recent-news.php
  3. page-{id}.php – If the page ID is 6, WordPress will look to use page-6.php
  4. page.php
  5. index.php

 Please don't use 2 & 3 

Theme anatomy

Inside a template

Theme anatomy #

index.php calls


  • get_header();
  • The Loop
  • get_sidebar();
  • get_footer();

Theme anatomy

Theme anatomy

Too enqueue scripts and styles (see last slides)


header.php
// Most important call in header.php
wp_head();
footer.php
// Most important call in  footer.php
wp_footer();

Pretty and sane templates

Sorry no twig or liquid templating

Get a template part - #

DRY

get_template_part

get_template_part

get_template_part

get_template_part

get_template_part

get_template_part


Get a template part

get_template_part

<?php
/*
Template Name: Front page
*/
?>

<div class="main">
	<?php get_template_part('templates/post', 'highlighted'); // The same ?>
	<?php get_template_part('templates/post', 'latest'); ?>
</div>
<div class="sidebar">
	<?php get_template_part('templates/event', 'highlighted'); ?>
	<?php 
		// Remember that on the front_page the global query contains the latest x posts else use get_template_part('templates/post', 'latest');
		get_template_part('templates/event', 'latest');
	?>
</div>

get_template_part

get_template_part

<?php
/*
Template Name: News
*/
?>

<div class="main">
<?php get_template_part('templates/post', 'highlighted'); // The same ?>
</div>
<div class="sidebar">
<?php get_template_part('templates/post', 'latest'); ?>

<p class="list-view-all-link">
<a class="read-more" href="<?php echo $archive_page; ?>"><?php echo __("More news", "sharedstories"); ?></a>
</p>
<?php endif; ?>

</div>

get_template_part

get_template_part

templates/post-highlighted.php

<article <?php post_class('highlighted'); ?>>
	
	<header>
	  <?php get_template_part('templates/post', 'meta'); ?>
	  <h2 class="entry-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
	</header>

	<?php
	if ( has_post_thumbnail() ) :
    	the_post_thumbnail('medium'); // wrapper link added in custom.php for all post_thumbnail
	endif;
	?>

  <div class="entry-summary">
    <?php echo ll_excerpt_by_id( get_the_ID(), POST_EXCERPT_LENGTH , roots_excerpt_more( true ) ); ?>
  </div>
</article>

get_template_part

templates/post-meta.php

<?php if( is_front_page() ) { ?>
<?php get_template_part('templates/entry-meta', 'time'); ?>
<?php } else {
$post_taxonomies = get_object_taxonomies( $post ); ?> <p class="meta"> <?php get_template_part('templates/entry-meta', 'time'); ?>
<?php echo get_the_term_list( get_the_ID(), $post_taxonomies, '&nbsp;-&nbsp; <span class="lc">', '</span>, <span class="lc">', '</span>' ); ?> - <span class="byline author vcard"><a href="<?php echo get_author_posts_url(get_the_author_meta('ID')); ?>" rel="author" class="fn"><?php echo get_the_author(); ?></a></span> </p>
<?php } ?>

get_template_part

templates/entry-meta-time.php
<?php $d = "d M";?>
<span class="date">
    <time class="updated" datetime="<?php echo get_the_time('c'); ?>"><?php echo get_the_date( $d ); ?></time>
</span>

Render a template part ++

Like every proper template engine this lets you pass $args to the template file # 

/templates/contact-persons.php

$people = '';
foreach ($loc_people as $key => $person) {

$people .= ll_get_template_part('templates/contact-person', array( 'person' => (object)$person['ss_contact_person'], 'return' => true ), false ) ;

}
echo $people;

Even widgets can have templates

Templates in widgets


Templates in widgets #

Simply include the templates from plugins/your-plugin/widget.php

public function form( $instance ) {

	wp_enqueue_media();

	$defaults = array( 'image-id' => false, 'image-preview' => '', 'url' => false );

	$instance = wp_parse_args( $instance, $defaults );

	$image_preview = esc_url_raw( $instance['image-preview'] );
	$image_id = absint( $instance['image-id'] );
	
	$image_thumbnail = $this->get_image_preview( $image_id );

	$url = esc_url_raw( $instance['url'] );

	// Display the admin form
	include( plugin_dir_path(__FILE__) . 'views/admin.php' );
}

Templates in widgets

plugins/your-plugin/views/admint.php

<!-- This file is used to markup the administration form of the widget. -->
<p class="abn-sidebar-widget-image"><?php echo $image_thumbnail; ?></p> <p> <a class="ll-sidebar-widget-image-button button button-secondary" data-uploader_title="Select banner" >Select image</a> </p> <p> <label for="<?php echo $this->get_field_name('url'); ?>">Link</label> <input class="ll-sidebar-widget-url widefat" type="text" name="<?php echo $this->get_field_name('url'); ?>" value="<?php echo esc_attr($url); ?>" /> </p> <input class="ll-sidebar-widget-image-preview" type="hidden" name="<?php echo $this->get_field_name('image-preview'); ?>" value="<?php echo esc_attr( $image_preview ); ?>" /> <input class="ll-sidebar-widget-image-id" type="hidden" name="<?php echo $this->get_field_name('image-id'); ?>" value="<?php echo esc_attr( $image_id ); ?>" />

Overwriting stuff

Child theme #

Child themes are themes that piggy-back on another (full) theme.
It only overrides the bare necessary

  • functions.php loads before and in addition to it's parent them
  • No child theme limbo

Why child themes?

Updates!

One theme to rule them all...

Why not child themes?

Payed theme builders don't take care backwards compatibility

Overwriting WooCommerce

    Simply copy it into a directory within your theme
    named /woocommerce keeping the same file structure

    woocommerce/templates/cart/cart.php

    to

    your-theme/woocommerce/cart/cart.php


    Or use hooks to add / move content

    Overwriting WooCommerce

    Overwriting Woocommerce

    in your-child-theme/functions.php

    // First mimic WooCommerce style loading from theme-actions.php
    if ( ! is_admin() ) { add_action( 'wp_enqueue_scripts', 'woo_load_frontend_css', 20 ); }
    function woo_load_frontend_css () {
    
        // Mimic WooCommerce style loading - theme-actions.php
        wp_register_style( 'woo-theme-stylesheet', get_template_directory_uri() . '/style.css' );
        wp_register_style( 'woo-layout', get_template_directory_uri() . '/css/layout.css' );
        wp_enqueue_style( 'woo-theme-stylesheet' );
        wp_enqueue_style( 'woo-layout' );
       
        // Mimic Woocommerce style loading - theme-woocommerce.php
        wp_register_style( 'woocommerce', esc_url( get_template_directory_uri() . '/css/woocommerce.css' ) );
        wp_enqueue_style( 'woocommerce' );
    
        // Manualy load Fabulous Women style
        wp_enqueue_style('theme-stylesheet', get_stylesheet_directory_uri().'/assets/less/style.less');
        //wp_register_style( 'theme-stylesheet', get_stylesheet_uri() );
        wp_enqueue_style( 'theme-stylesheet' );
    } 

    Again DRY

    Reuse The Events Calendar #


    //Let's use all code from tri.be and no custom!
    $args	= array(
    'posts_per_page' => 3,
    ); $query = TribeEventsQuery::getEvents( $args, true ); if ( $query->tribe_is_event && $query->posts ) : //setup_postdata( $post ); $query->the_post(); echo '<article class="hentry tribe-event">'; tribe_get_template_part( 'list/single', 'event' ); // This way all views will be the same ;) echo "</article>"; endif; // Clean up $event = $query = $highlighted_event = false; wp_reset_postdata();

    Extra goodies

    Conditional Tags #

    Checks where you are on the site


    // function.php ;)

    function ll_conditional_custom_category_limit( $query ) { if ( is_admin() || ! $query->is_main_query() ) return; if ( is_archive('specific-term') ) { $query->set( 'posts_per_page', 1 ); return; } } add_action( 'pre_get_posts', 'll_conditional_custom_category_limit', 1 );

    Enqueue scripts and styles #

    Why?

    Caching plugins

    Minify plugins

    Unload stylesheets and scripts from plugins

    /**
     * Enqueue scripts and styles.
     */
    function _s_scripts() {
        wp_enqueue_style( '_s-style', get_stylesheet_uri() );
        
        wp_enqueue_script( '_s-navigation', get_template_directory_uri() . '/js/navigation.js', array(), '20120206', true );
        
        if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
            wp_enqueue_script( 'comment-reply' );
        }
    }
    add_action( 'wp_enqueue_scripts', '_s_scripts' );
    

    Security #

    Prevent XSS attacks, escape your data!

    • esc_url
    • esc_html
    • esc_attr
    • etc…


    or stuff like

    absint( $int )

    sanitize_title( $title )

    $wpdb->prepare(...)

    wp_safe_redirect($location, $status = 302)


    Hooks  a.k.a.

    Filters and actions

    Actions

    Actions are events in the WordPress page lifecycle when certain things have occurred - certain resources are loaded, certain facilities are available, and, depending on how early the action has occurred, some things have yet to load.

    Actions

    Run this a that moment

    http://codex.wordpress.org/Plugin_API/Action_Reference

    http://code.tutsplus.com/articles/the-beginners-guide-to-wordpress-actions-and-filters--wp-27373

    Filters

    Filters are functions that WordPress passes data through during certain points of the page lifecycle.

    They are primarily responsible for intercepting, managing, and returning data before rendering it to the browser or saving data from the browser to the database.

    Filters

    add_filter( 'the_content', 'll_the_content_filter', 20 );
    /**
     * Add a icon to the beginning of every post page.
     *
     * @uses is_single()
     */
    function ll_the_content_filter( $content ) {
    
        if ( is_single() ){
            // Add image to the beginning of each page
            $content = $content . 'Facebook | LinkedIn | Twitter | Etc';

    } // Returns the content. } return $content;

    When do I use which hook?

    Use actions when you want to add something to the existing page such as stylesheets, JavaScript dependencies, or send an email when an event has happened.

    Use filters when you want to manipulate data coming out of the database prior to going to the browser, or coming from the browser prior to going into the database.

    pre_get_posts

    function ll_exclude_category( $wp_query ) {
    
        // Add the category to an array of excluded categories. In this case, though,
        // it's really just one.
        $excluded = array( '-1' );
    
        // Note that this is a cleaner way to write: $wp_query->set('category__not_in', $excluded);
        set_query_var( 'category__not_in', $excluded );
    
    }
    add_action( 'pre_get_posts', 'll_exclude_category' );

    WP_Query

    WP_Query

    To get Custom Post Type data

    WP_query is used in The Loop


    http://wordpress.stackexchange.com/questions/1753/when-should-you-use-wp-query-vs-query-posts-vs-get-posts



    WP_Query

    <?php
    $args = array(
    'post_type' => 'll-portfolio',
    );
    $ll_portfolio_items = new WP_Query( $args );

    if( $ll_portfolio_items->have_posts() ) : ?>

    <div class='items'>
    <?php while($ll_portfolio_items->have_posts()) : $ll_portfolio_items->the_post() ?>
    <div class='item'>
    <?php the_post_thumbnail() ?>
    </div>
    <?php endwhile ?>
    </div>

    <?php endif ?>

    Tips and tricks *

    Use the WordPress API's #

    Coding Standards

    Analyse other peoples work

    Read some nice websites like http://www.poststat.us

    Follow the WordPress Devel Twitter account

    Follow developers like Nacin or ?

    Use at least SFTP



    Tools and skills II *

    GIT

    Bitbucket, GitHub


    CMD

    oh-my-zsh & off course WP-CLI  ^^


    rly kthxbye

    WordPress verdieping Workshop

    By Jaime Martinez

    WordPress verdieping Workshop

    Sponsored by Level Level, given by Jaime Martinez

    • 5,198