An MVC approach to WordPress theme
development
What is MVC?
● Architectural pattern based on 3 components:○ Model - represents the data○ View - represents the presentation layer○ Controller - represents the logic
Why MVC?
● Based on the Separation of concerns● More structured code● Reduced code complexity● Decoupled code
Timber.
Timber separates your theme’s code:
● the PHP files focus only on supplying the data and logic
● A templating engine is used for the presentation: Twig
Twig.● PHP template engine
<!DOCTYPE html><html> <head> <title>Title</title> </head> <body> <ul id="list"> {% for item in list %} <li><a href="{{ item.href }}">{{ item.label }}</a></li> {% endfor %} </ul> </body></html>
WordPress as MVC. Model Controller View
index.php
<?php$ctx = Timber::get_context();$ctx['foo'] = 'Bar!';$ctx['post'] = new Timber/Post();Timber::render('single.twig', $ctx);?>
index.twig
{% extends "base.twig" %}{% block content %} <h1 class="big-title">{{foo}}</h1> <h2>{{post.title}}</h2> <img src="{{post.thumbnail.src}}" /> <div class="body"> {{post.content}} </div>{% endblock %}
ACF.
● Allows the users to create extra fields in a very user-friendly manner
● Has a lot of different field types to be configured
The normal approach.
● The content manager / back-end developer configures the ACF fields
● The back-end developer queries all the necessary data and appends it to the context variable
● The front-end developer uses the data from the context variable in the twig template, creates the markup and styles it
Our approach.
● We’re trying to remove the back-end developer from this equation
● Do to this we automatically map all the ACF fields defined into the twig context variable
Our approach.
● Timber has objects defined for each WordPress entity:○ Posts, Pages, Custom Post Types - Timber\Post○ Categories, Tags, Custom taxonomies - Timber\Term○ Users - Timber\User○ Image - Timber\Image
Our approach.● Before a Timber object is instantiated we go through all the
defined fields and verify if they apply to the current object● On the Timber object we add a new key called fields, where
all the fields that match the criteria for that object are appended
● All the fields added as theme options are stored on a special key called options
The fields.
● Text, Textarea, Wysiwyg, Select, Radio, Checkbox - their actual values are added● For other field types you get the entire set of data
○ Image, Gallery - mapped as Timber\Image○ Post Object - mapped as Timber\Post○ Taxonomy - mapped as Timber\Term○ User - mapped as Timber\User○ Repeater, Flexible Content - mapped as arrays containing actual values or
Timber objects
The fields. Text. Wysiwyg.array(2) { ["text_field"]=> string(26) "Lorem ipsum dolor sit amet" ["wysiwyg_field"]=> string(328) "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse lobortis, tortor ut elementum imperdiet, turpis sem ultricies erat, quis mattis nibh mauris nec ante. Nunc quis viverra dolor.</p><p>Mauris aliquam magna a convallis pretium. Duis ultricies viverra ornare. Donec suscipit risus lacinia luctus iaculis.</p>"}
The fields. Image.
array(1) { ["image_field"]=> object(Timber\Image)#1008 (47) { ["object_type"]=> string(5) "image" ["post_title"]=> string(4) "test" ["id"]=> int(222) ["sizes"]=> array(3) { ... } ... // Additional methods to get the src and // the srcset }}
The fields. Post Object.
Fields defined on the New post post
array(2) { ["text_field"]=> string(18) "Text field content" ["post_field"]=> object(Timber\Post)#968 (86) { ["ID"]=> int(119) ["post_content"]=> string(0) "" ["post_title"]=> string(8) "New post" ["post_type"]=> string(4) "post" ["fields"]=> array(2) { ["text_field"]=> string(26) "Lorem ipsum dolor sit amet" ["post_field"]=> NULL } }}
The fields. Repeater.array(1) { ["repeater_field"]=> array(3) { [0]=> array(2) { ["text_field_1"]=> string(26) "Lorem ipsum dolor sit amet" ["text_field_2"]=> string(27) "consectetur adipiscing elit" } [1]=> array(2) { ["text_field_1"]=> string(25) "Quisque posuere dui velit" ["text_field_2"]=> string(29) "quis egestas libero malesuada" } [2]=> array(2) { ["text_field_1"]=> string(19) "Mauris auctor augue" ["text_field_2"]=> string(22) "vitae diam consectetur" } }}
Challenges. Field caching.
While processing the fields for a certain object we need to go through all the field groups defined in ACF.
Solution: Store the ids of the fields for each WordPress entity.
Challenges. Infinite Loops.
If Post A has a reference to Post B and Post B has a reference back to Post A, while retrieving the fields we would find ourselves in an infinite loop.
Solution: If an object is already appended to the data variable, we append an empty array instead of the actual object.
Summary.
● Timber● ACF● For basic website templates the work of the back-end
developers can be almost completely replaced by the work of content managers and front-end developers
Questions?
Thank you!