If you already know why CMB2 is awesome, please feel free to skip this intro and move onto the Installation section
For many who use WP Rig, adding custom fields to any page, custom post type, etc. is as easy as just adding the Advanced Custom Fields ( ACF ) plugin as a dependency to your theme, and never thinking about writing a line a PHP. Although this might be fine for a situation where this theme is only being used on one site, that you are building, and will never be distributed to the masses, this scenario is very specific. So what if you want to add custom fields without requiring ACF to be installed/activated in WordPress? Why should we care?
Problems with relying to heavily on ACF
WordPress will break if you activate you theme before someone installs and activates ACF ( get_field() is not a core WordPress function )
Relying on 3rd party plugins in order for your theme to work is not great if you plan to sell or mass distribute your theme
You can’t version control your custom fields if the data that controls them resides in the WordPress database
Just to name a few…
So how can we easily get custom fields to be a part of our theme without ACF? Well, first lets understand that custom fields go inside custom meta boxes. So we first need to add a custom meta box to house our custom fields. WordPress does have a page on the codex about how this can be done manually. This is fine if you only need one or two fields total, but once you read that whole page, you’ll notice that there are a few steps and considerations that go into creating these the WP way. It’s not all that simple.
What if your theme is very complex and needs many custom fields across many different custom post types in many custom meta boxes!? One popular approach used by experienced WP developers is to use CMB2! But before we go just using some library we know little about, especially in terms of size/weight, lets answer some questions first.
If the answer is to add custom fields to pages, posts, or a custom post type included in your theme, then carry on.
How many custom fields do we need and what kinds of field controls do we want?
If you don’t need more than a handful, then using CMB2 might be a bit overkill and not optimal. CMB2 can be a bit heavy.
If you need a lot of custom fields and you want the controls for these fields to be fancy and lots of them, then carry on.
Installation
The proper way to go about this is just like you would add any WordPress sub-framework to your theme when using WP Rig. When adding big logical bodies of PHP to our theme, it’s best to do it using our Component structure in WP Rig. These “Components” can be found in the /inc directory of WP Rig. All WP Rig “Components” are initialized ( Newed ) from the Theme.php file in the /inc directory. So any time we want to add our own custom “Component”, we must add it to the array $components array in Theme.php in the get_default_components() method. So, here we go…
Navigate in your terminal/command line to the /inc directory of WP Rig
Clone the CMB2 repo into the /inc directory
git clone https://github.com/CMB2/CMB2.git
Create a Component.php file for your component class in the CMB2 directory that was just added to the /inc directory
Copy and paste the component structure from one of the other components. Here is an example you can copy:
<?php
/**
* WP_Rig\WP_Rig\CMB2\Component class
*
* @package wp_rig
*/
namespace WP_Rig\WP_Rig\CMB2;
use WP_Rig\WP_Rig\Component_Interface;
use function add_action;
/**
* Class for managing Customizer integration.
*/
class Component implements Component_Interface {
/**
* Gets the unique identifier for the theme component.
*
* @return string Component slug.
*/
public function get_slug() : string {
return 'CMB2';
}
/**
* Adds the action and filter hooks to integrate with WordPress.
*/
public function initialize() {
require_once __DIR__ . '/init.php';
add_action( 'cmb2_admin_init', array( $this, 'cmb2_sample_metaboxes') );
}
/**
* Create our new metabox and add our fields to it
*/
public function cmb2_sample_metaboxes(){
$cmb = new_cmb2_box( array(
'id' => 'test_metabox',
'title' => __( 'Test Metabox', 'cmb2' ),
'object_types' => array( 'page', ), // Post type
'context' => 'normal',
'priority' => 'high',
'show_names' => true, // Show field names on the left
'show_in_rest' => true,
// 'cmb_styles' => false, // false to disable the CMB stylesheet
// 'closed' => true, // Keep the metabox closed by default
) );
// Regular text field
$cmb->add_field( array(
'name' => __( 'Test Text', 'cmb2' ),
'desc' => __( 'field description (optional)', 'cmb2' ),
'id' => 'yourprefix_text',
'type' => 'text',
'show_on_cb' => 'cmb2_hide_if_no_cats'
) );
}
}
As you can see above, we are hooking into CMB2’s custom init action, then passing a reference to our method that handles the registration of our meta box and associated meta fields. The example above is only utilizing the basic text field, but CMB2 has a vast selection of fields to choose from. See the CMB2 documentation or the example-functions.php file in the CMB2 directory for more example. To learn more about how components work in WP Rig, see our documentation on our architecture.
Some important things to note here:
Since our hook is referencing a method (not a basic function), we need to also pass a reference to the class ( $this ) the method is part of in addition to the name of the method itself. This is why we are passing an array for the second argument of the add_action hook instead of the more standard string reference approach. OOP complicates things in that way a bit, but the concise structure is what we are after here (not to mention autoloading all of our class files).
We do have to require the main CMB2 library file in order for our class to know what a new_cmb2_box even is.
Depending on how many fields you plan on adding, it may be cleaner to not combine the registration of the fields in the same method as the registration of the meta box. Just something to consider.
Now, the last thing we need to do after creating this class is to add our new “Component” to our Theme.php file in the $components array in the get_default_components() method. Like so:
protected function get_default_components() : array {
$components = array(
new Localization\Component(),
new Base_Support\Component(),
new Editor\Component(),
new Accessibility\Component(),
new Image_Sizes\Component(),
new AMP\Component(),
new PWA\Component(),
new Comments\Component(),
new Nav_Menus\Component(),
new Sidebars\Component(),
new Custom_Background\Component(),
new Custom_Header\Component(),
new Custom_Logo\Component(),
new Post_Thumbnails\Component(),
new Customizer\Component(),
new Styles\Component(),
new CMB2\Component(), // <- our new CMB2 component!
);
if ( defined( 'JETPACK__VERSION' ) ) {
$components[] = new Jetpack\Component();
}
return $components;
}
Conclusion
Although this is not quite as simple as installing ACF and configuring your fields in the ACF settings page, we can now rest assured that:
Our theme will never break because ACF is not installed or active
We can now distribute our theme anywhere without forcing the admin to install ACF
Our custom fields can be version controlled with the rest of our code
CMB2 comes with a number of other benefits as well, but I won’t get into detail about that here. See their repo for more info on that.
Any level of ACF support can be added to your theme very easily following the PHP Architecture guide for WP Rig. We can create a custom component for ACF that we can use as a main controller for all ACF-specific concerns in the theme.
Create a new ACF component in the in the /inc folder by following this guide that outlines scaffolding a new component in WP Rig
After you create fields or import into WP dashboard they compile as json files in the acf-json folder and this can be synced across environments. Read more here: https://www.advancedcustomfields.com/resources/local-json/
WP Rig changes the way WordPress themes are developed, not only on the client side, but also on the server side.
Historically, the common WordPress theme’s PHP codebase has consisted of random PHP functions, scattered across different files, without too many architectural thoughts behind it. And while it is debatable whether themes should even have to contain as much server-side functionality as they do and not only focus on HTML, CSS and JavaScript (which the Gutenberg project might fix in the long run), the current state is that this is simply required.
WP Rig introduces several new patterns in how theme PHP code is structured and executed. These are not just for the sake of changing things, they solve actual problems. In other areas where a change wouldn’t be more than of cosmetic nature, WP Rig stays as close as possible to the way themes have been developed in the past: As an example for that, WP Rig still uses a directory inc for its PHP functionality although nowadays it’s more common to have this called src or similar – but changing that wouldn’t solve a real problem, it would just be one more (in this case, admittedly simple) thing to get used to.
This article will first outline the goals of the WP Rig architecture and the problems it solves. It will then go into more detail what the architecture and API actually looks like and how to work with it. If you don’t care about the background and just want to quickly start modifying and extending WP Rig’s PHP code for your theme, you can skip the first section – although reading that may give you a better understanding and possibly provide you with ideas for other non-WP Rig WordPress projects as well.
Goals of the WP Rig Architecture
Clear Definition of Public API
WP Rig moves away from the function paradigm and organizes its code entirely in classes. Contrary to functions, which are always publicly available, class methods (i.e. “functions in classes”) can have different visibility levels, for example to prevent calling a method from the outside of the class.
Most functions that themes include are internal to the theme, so they should not be exposed to the outside. Sure, if you have followed theme development for a while, you probably just know that you should not call a function like mytheme_setup() manually ever – as the theme already hooks it into after_setup_theme, thus has it be called automatically by WordPress. And its code is written in a way the function should definitely not be called again in the same request. However, this requires documentation and developers to know that. Nothing in the code restricts a developer from calling the function at any random point.
One might argue that calling this a problem is a bit overkill – but, to use another example, precisely the lack of proper definition of public vs private APIs causes WordPress core (which similar to themes mostly consists of public functions) to be so problematic with backward-compatibility. Almost every change that happens in WordPress core is in some way a breaking change. What is not considered a breaking change is probably so only because of the (in most cases true) assumption that nobody uses the respective function that’s being changed in unexpected ways. In many other cases though, bug fixes get lost after endless discussions about things like “we probably should not fix this function, as someone might be using it in that way although it should not be used at all” – which brings us back here. One goal of WP Rig is have the code itself define that something is public or not public (of course still including actual documentation, like what you are reading now :) ).
Separation of Concerns
WP Rig’s classes are organized into so-called components. A component is responsible for a distinct feature of the theme. That can be an obvious feature literally like for example support for WordPress core’s “Custom Header”, but it can also be a feature in the more broad sense, such as the theme being fully compatible with “AMP” or the theme’s focus on “accessibility” by providing a suite of generic accessibility fixes that WordPress itself does not include. Each of these components has a single main class, but can consist of as many classes as needed (although, at the point of writing, every component has in fact only the one main class). The components are then organized in different directories.
WordPress themes have historically done an okay-ish job of separating concerns. Aside from the terrible “you can dump this in your functions.php” approach, there are usually different files that contain the functions, often called for example inc/template-tags.php for functions that the template files need to use, or inc/customize.php for Customizer integration. However, as mentioned there is still use of the generic functions.php file, and even some functions themselves take care of multiple things that belong to different features. An example for that is the typical mytheme_setup() function that adds theme support for pretty much anything that theme support can be added for. That approach makes sense for one-off features, like automatic-feed-links or title-tag, things that every theme just needs to support and that are nothing more than a flag to enable. However, mixing in support for things like custom-header, custom-background or editor-color-palette is questionable, as each of these is a separate non-trivial feature with arguments (or at least is related to a separate feature like the editor). In the case of custom-header, adding theme support possibly even relies on other functions that need to be part of the theme.
Separating concerns helps keeping your codebase organized. This in turn helps navigating the codebase. Especially with WP Rig being a starter theme, it should be easy to find out which piece of code to navigate to when wanting to adjust a particular feature. For example, you might want to render the custom-header feature in a different way in your theme. Or you might not even want to support the custom-header feature at all. When looking at the historically used code organization of a WordPress theme, you’d never be able to find out where the code for that part was located. Of course, if you are a seasoned theme developer, you’d guess that it is in functions.php, but let’s be honest, that’s cheating because you already have made that experience often enough. In WP Rig, you instead have a “Custom Header” component, and thus also directory called Custom_Header, so it is obvious where to navigate. In fact, if you didn’t want to support that feature, all you would have to do is delete the directory and remove its instantiation in the Theme class (more on the latter in the next section).
Another example that highlights the organization of the codebase is that WP Rig has a “Styles” component, but not a “Scripts” component. “Styles” is its own component since the theme’s CSS files, while being granular, are not granular enough to apply per feature, as that would end up causing way too many CSS files to be present. Furthermore, CSS files in WP Rig are loaded in a special way to help performance, requiring extra PHP logic that makes the existence of a distinct “Styles” component more viable. JavaScript files however always exist to support one specific feature. For example there is the navigation.js script that makes the navigation menu more accessible – as such, it is part of the “Accessibility” component. Or there is the lazyload.js script taking care of lazy loading, making it part of the “Lazyload” component. Instead of dumping all wp_enqueue_script() calls of those scripts into one file, together with all the wp_enqueue_style() calls, JavaScript is loaded from the component that it is used for. This is precisely what the separation of concerns is about, organizing the codebase by its features, not by the WordPress functions it calls.
Separating Invocation from Definition
Each of WP Rig’s component main classes has an initialize() method that adds the necessary actions and filters. That means that the hooks are not automatically added just by including the file that the hook callback methods are part of – the theme explicitly needs to call the initialize() method. You might now ask “Why would I want to write extra code instead to call that method?” The reason is testability.
Preferably every piece of code in a project should be covered by tests that verify it works as expected. WP Rig provides both unit and integration tests to ensure that and to prevent unnoticed regressions. Particularly for unit tests, it is important that each unit (for example a PHP function or method) is tested in isolation without needing to consider side effects. By automatically having code executed on load of the same files that contain that code, this becomes impossible. In other words, invoking a function or method should not happen in the same location that also declares that function or method. In addition, the initialize() method of each component bundling all related add_action() and add_filter() calls ensures that there is a single entry point to hook in that component, i.e. integrate it with WordPress.
Don’t worry, you still don’t actually need to manually call the initialize() method of each component. Since each component must implement a specific interface (more on that later), the theme knows that each component has such an initialize() method and will invoke it automatically. As a matter of fact, the Theme class has its own initialize() method which calls the initialize() methods of all components. As such, this method is the single canonical entry point to the entire theme, integrating all theme components with WordPress.
How to Work with the WP Rig Architecture
Before actually diving into the architecture, let’s highlight that WP Rig uses PHP namespaces for its codebase. It also relies on autoloading, so every file containing a class, interface or trait should only contain that structure and nothing else. Such files should not be loaded anywhere manually, you can just rely on the autoloader. The root namespace for all code is WP_Rig\WP_Rig (remember that your own namespace will only be applied to your code when building the theme for production), and all code in that namespace (except tests) is located in the inc directory. From there on, sub-namespaces map to subdirectory names, and the class name itself must match the file name (followed by the .php extension of course), following the PSR-4 standard. Make sure to follow these conventions for all files you might add to your theme based on WP Rig.
Something else to be aware of is that WP Rig uses function and method parameter type hints and return type hints wherever possible. Since the theme requires PHP 8.0 as minimum version, it can use these features, and they are a simple means to make your code more error-proof.
As mentioned earlier, the different PHP features of WP Rig are organized in so-called components. Whether you plan to adjust existing theme behavior or add an entirely new feature to the theme, you will work with components. For the sake of this guide, we will implement our own component while explaining how things work. Particularly, we will add a component that allows a user of the theme to toggle a Customizer setting to decide whether the theme’s archive views should display excerpts (using the_excerpt()) or regular shortened content using the <!-- more --> tag (using the_content()). Maybe you even want to use this in your theme – the reason WP Rig does not include something like this is that it would still be too opinionated. WP Rig should be a starter theme and not get bloated by too many features people might use (but likely more often would need to remove). But now, let’s write our own component!
Scaffolding the Component Class
As of v3.1, WP Rig includes a component scaffolding system that makes it easy to create new PHP components under the inc/ directory. This allows developers to quickly add new functionality to their theme following WP Rig’s component architecture.
Create a new component at inc/Related_Posts/Component.php
Implement both Component_Interface and Templating_Component_Interface
Add an empty template_tags() method ready to be customized
Create a test file at tests/phpunit/unit/inc/Related_Posts/ComponentTest.php
Auto-register the component in Theme.php
Every component has its own directory in inc, with the directory name matching the sub-namespace part, as explained in the previous paragraph. Let’s call our component “Archive_Content”. Based on that, the directory name must be Archive_Content and the namespace must be WP_Rig\WP_Rig\Archive_Content. Every component must have at least one class, the so-called component main class. That class should simply be called Component (note that the class name will still be unique because of the namespace, making the fully qualified class name to be something like WP_Rig\WP_Rig\Archive_Content\Component). The class must implement the WP_Rig\WP_Rig\Component_Interface interface, thus you must at least implement the methods get_slug() and initialize(). Here is what this file could look like:
As you can see, get_slug() should simply return the slug of the component. This is used internally and should preferably match the component name in lowercase. In initialize() we will later add the actions and filters needed. Note the use ... statement below the namespace declaration: This line basically imports the WP_Rig\WP_Rig\Component_Interface so that we can use it in the namespace we are currently in. This is necessary because that interface’s namespace is not the same as the namespace of our file. And apart from that, it also helps getting a quick overview of which language constructs from other locations are used in the file, i.e. which dependencies our file has. Also, keep in mind to thoroughly document your code at all times. This helps others in collaborating with you, and even if you’re entirely on your own, it will help you remember your thoughts when you possibly come back to this code in two years. Especially if you’re building a theme that should be public in any way, for example distributed on wordpress.org or sold through another platform, solid documentation is super important.
Adding Hooks
All hooks generally required to integrate a component with WordPress should be added in the initialize() method. The hook callbacks should be public methods in the same class, and as common pattern should be prefixed with either action_ or filter_, depending on the respective hook type they are used for. The hook callbacks need to be public because otherwise WordPress will not be able to call them. If you read through the section about public APIs earlier in this article, you might now be asking “I thought interal hook callbacks should not be public!”. Yes, it’s true, and if the WordPress hook system supported calling protected or private methods, they indeed would not be. However, WP Rig’s approach here still works because the component instances themselves are not available to the outside. As mentioned before, the WP_Rig\WP_Rig\Theme class holds all the component instances and calls their initialize() methods, but it does not expose these classes themselves to the outside. As such, essentially not even public methods in component classes are publicly usable, except for the hook that they are registered to directly. No worries, components can still contain methods that are actually publicly usable and that you can use from outside locations such as PHP template parts. We will get to that in a bit. But first, let’s see what the hook implementations for our component look like:
<?php/***WP_Rig\WP_Rig\Archive_Content\Componentclass**@packagewp_rig*/namespaceWP_Rig\WP_Rig\Archive_Content;useWP_Rig\WP_Rig\Component_Interface;useWP_Customize_Manager;usefunctionadd_action;usefunction__;/***Classforallowingadministratorstodecidewhethertodisplaycontentorexcerptinarchives.*/classComponentimplementsComponent_Interface{/***Getstheuniqueidentifierforthethemecomponent.**@returnstringComponentslug.*/publicfunctionget_slug():string{return'archive_content'; }/***AddstheactionandfilterhookstointegratewithWordPress.*/publicfunctioninitialize(){add_action('customize_register',array($this, 'action_register_customizer_control') ); }/***AddsaCustomizersettingandcontrolfortogglingusageof`the_content()`or`the_excerpt()`in archives.** @param WP_Customize_Manager $wp_customize Customizer manager instance.*/public function action_register_customizer_control(WP_Customize_Manager$wp_customize) {// Register the Customizer setting and ensure its value is a boolean.$wp_customize->add_setting('archives_use_excerpt',array('default' =>false,'sanitize_callback' =>'rest_sanitize_boolean', ) );// Register the Customizer control for the setting, using a checkbox.$wp_customize->add_control('archives_use_excerpt',array('label' => __('Use excerpts in archive views?','wp-rig'),'section' =>'theme_options','type' =>'checkbox', ) ); }}
Most of the newly added code should be familiar code if you have worked with the Customizer before. Similar as in the previous version of the code, note the new use ... statements. This time we import the WP_Customize_Manager class from WordPress core that is used here, plus several core functions. Contrary to classes, including statements for the functions is not necessary here since namespaces will by default fall back to the global namespace (of which all WordPress core functions are a part of). However having these use ... statements in the file once again helps to quickly be able to tell which outside functions the file depends on.
With the above code, you already have the Customizer integration in place, and its value will be stored in a theme mod, so you can retrieve it with the WordPress function get_theme_mod( 'archives_use_excerpt' ). So now you can go into the respective template file that displays post content and adjust it as follows:
<?php/***Templatepartfordisplayingapost's content * * @package wp_rig */namespace WP_Rig\WP_Rig;?><div class="entry-content"> <?php if ( ! is_singular( get_post_type() ) && get_theme_mod( 'archives_use_excerpt' ) ) { the_excerpt(); } else { the_content( // Same arguments as before. ); } // Same code as before. ?></div><!-- .entry-content -->
However, it would be much nicer to have a dedicated function to call for this, for example something like using_archive_excerpts(), that you can use from your template. We could of course just introduce a function for that and place it somewhere. However, that would go against our separation of concerns since it clearly belongs to our “Archive_Content” component. So let’s look into how we can expose specific methods in our component to the outside.
Exposing Template Tags
WordPress typically calls functions that should be used from the template files of your theme “template tags”. And there’s no reason to change that naming convention. Historically, you would have put these as functions in a file called template-functions.php or template-tags.php. In WP Rig however, you can place them as methods alongside the rest of the code they actually integrate with, as part of the feature they are actually part of. While none of the component methods are exposed to the outside of the class by default, WP Rig contains a mechanism to work around that specifically for that purpose.
You might remember that each component has to implement the WP_Rig\WP_Rig\Component_Interface interface. A great thing about interfaces contrary is that it is possible for a class to implement multiple interfaces, contrary to abstract classes where a child can only extend a single parent class. WP Rig includes a WP_Rig\WP_Rig\Templating_Component_Interface interface, which is not mandatory to implement for each component, but only for those components that contain at least one method that should be exposed to the outside to be used in template files (again, these are called “template tags”). When a component main class implements this interface, it needs to include a method template_tags() which returns an associative array of $template_tag_name => $template_tag_callback pairs. The $template_tag_callback is the actual callback, which you should typically include as a public method on the class. The $template_tag_name is the name under which that template tag should globally be callable. This can in theory be different from the callback name, but it is recommended to keep the name consistent with the callback method, just for code readability. Any template tag made available like that can then be called via wp_rig()->{$template_tag_name}() – for example, for our component and the previously suggested name for the template tag we would use wp_rig()->using_archive_excerpts(). Internally, aggregating these template tags and exposing them via the wp_rig() function is handled by the internal WP_Rig\WP_Rig\Template_Tags class, in combination with WP_Rig\WP_Rig\Theme. You don’t necessarily need to worry about how this works – if you’re interested, feel free to check out the code of the two classes and the wp_rig() function in more detail. Here is the full code for our component including exposure of the template tag:
<?php/***WP_Rig\WP_Rig\Archive_Content\Componentclass**@packagewp_rig*/namespaceWP_Rig\WP_Rig\Archive_Content;useWP_Rig\WP_Rig\Component_Interface;useWP_Rig\WP_Rig\Templating_Component_Interface;useWP_Customize_Manager;usefunctionadd_action;usefunction__;usefunctionget_theme_mod;/***Classforallowingadministratorstodecidewhethertodisplaycontentorexcerptinarchives.**Exposestemplatetags:**`wp_rig()->using_archive_excerpts()`*/class Component implements Component_Interface, Templating_Component_Interface {/*** Gets the unique identifier for the theme component.** @return string Component slug.*/public function get_slug() : string {return'archive_content'; }/*** Adds the action and filter hooks to integrate with WordPress.*/public function initialize() {add_action('customize_register', array($this, 'action_register_customizer_control') ); }/*** Gets template tags to expose as methods on the Template_Tags class instance, accessible through `wp_rig()`.** @return array Associative array of $method_name =>$callback_info pairs. Each $callback_info must either be* a callable or an array with key 'callable'. This approach is used to reserve the possibility of* adding support for further arguments in the future.*/public function template_tags() : array {return array('using_archive_excerpts' => array($this, 'using_archive_excerpts'),); }/*** Adds a Customizer setting and control for toggling usage of `the_content()`or`the_excerpt()`in archives.** @param WP_Customize_Manager $wp_customize Customizer manager instance.*/public function action_register_customizer_control(WP_Customize_Manager$wp_customize) {// Register the Customizer setting and ensure its value is a boolean.$wp_customize->add_setting('archives_use_excerpt',array('default' =>false,'sanitize_callback' =>'rest_sanitize_boolean', ) );// Register the Customizer control for the setting, using a checkbox.$wp_customize->add_control('archives_use_excerpt',array('label' => __('Use excerpts in archive views?','wp-rig'),'section' =>'theme_options','type' =>'checkbox', ) ); }/*** Determines whether archives should use excerpts instead of regular post content.** @return bool Whether to use excerpts in archives.*/public function using_archive_excerpts() : bool {return (bool) get_theme_mod('archives_use_excerpt'); }}
As you can see, a new using_archive_excerpts() method is added and then exported to the outside via the new template_tags() method that is added in combination with implementing WP_Rig\WP_Rig\Templating_Component_Interface. A good idea related to that is to document the available template tags in the source code, as seen in the doc block for the component class. Again, take note of how the additional dependencies (the interface and WordPress’s get_theme_mod() function) have been added as use ... statements.
We now have the component class completed. Let’s open the post content template file again and replace our previously used if condition with ! is_singular( get_post_type() ) && wp_rig()->using_archive_excerpts(). What remains is that we need to make the theme aware of it.
Registering the Component Class
In v3.1, if you are using the create-rig-component command, you don’t need to worry about registering your new component because the command does this for you.
Making the theme use our new component class is very straightforward. Simply navigate to the WP_Rig\WP_Rig\Theme::get_default_components() method and add the new component in there. Note that the order of the components there matters in some cases, for example if similar hooks use a similar priority. Generally, a good approach to follow is to add more foundational components towards the top and more pluggable components or components that might even rely on other components towards the bottom of the list. For our case, let’s append WP_Rig\WP_Rig\Archive_Content\Component to the end of the array. Note that since the class itself is in the WP_Rig\WP_Rig namespace, we can refer to our class relatively from that namespace, via Archive_Content\Component. Adding our component to the list is all we needed to do for the theme to handle it. It will call its initialize() method automatically now.
And that’s it for our component! It might have been a bit of work getting this rather simple enhancement done and understanding how all the different parts connect. Once you become more familiar over time though, the architectural patterns used in WP Rig should boost your productivity significantly.
WP Rig runs PHPCodeSniffer (PHPCS) as part of its build process to make sure all PHP complies with WordPress Coding Standards. Any errors or warnings found by PHPCS are displayed in the command line terminal as the various WP Rig tasks run.
To capture (and visualize) errors and warnings in VS Code as you are developing with WP Rig, you can activate the PHPCS extension and configure it to use the WP Rig settings:
In VS Code, select extensions and search for “phpcs” and install the free PHPCS extension from the Visual Studio Marketplace.
In VS Code, open Workspace Settings (Ctrl+, on Windows, CMD+, on Mac) and add the following configuration settings:
Make sure the path in phpcs.composerJsonPath points at the composer.php file in your WP Rig folder relative to the open project folder in VS Code. So if you opened users/documents/mysite/ as your project folder and your WP Rig theme sits in wp-content/themes/mytheme/, the value should be ./wp-content/themes/mytheme/composer.json.
With PHPCS configured in VS Code you will see green or red squiggly lines under any PHP which does not match the WordPress Coding Standards, and hovering your mouse over the highlighted code will open a tooltip telling you why PHPCS flagged this piece of code.