open-function-computers-llc / better-wordpress
A suite of classes to make WordPress theme development cleaner
Package info
github.com/open-function-computers-llc/rad-theme-engine
pkg:composer/open-function-computers-llc/better-wordpress
Requires
- php: >=8.1
- enshrined/svg-sanitize: ^0.22.0
- jjgrainger/posttypes: ^2.2
- salesforce/handlebars-php: ^2.3
- symfony/yaml: ^7.4
Requires (Dev)
- phpunit/phpunit: ^9.5
- yoast/phpunit-polyfills: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.39
- v1.0.38
- v1.0.37
- v1.0.36
- v1.0.35
- v1.0.34
- v1.0.33
- v1.0.32
- v1.0.31
- v1.0.30
- v1.0.29
- v1.0.28
- v1.0.27
- v1.0.26
- v1.0.25
- v1.0.24
- v1.0.22
- v1.0.21
- v1.0.20
- v1.0.19
- v1.0.18
- v1.0.17
- v1.0.16
- v1.0.15
- v1.0.14
- v1.0.13
- 1.0.12
- v1.0.11
- v1.0.10
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- dev-flex-slice-updates
- dev-feature-additional-commands
- dev-fields
- dev-rad-rebrand
- dev-better-wordpress-legacy
- dev-debug-empty-data
- dev-term-link-field
This package is auto-updated.
Last update: 2026-11-02 19:42:47 UTC
README
A suite of utilities to deliver a faster and more consistent WordPress theme development experience.
📦  View on Packagist
📃  Read the Docs
About
The purpose of this package is to enable developers to use the familiar model-view-controller pattern in the creation of WordPress themes. This is accomplished by keeping HTML and PHP code as seperated as possible and adding convenient methods to organize data before it's sent to the view controllers. Querying for posts, rendering menus, handling taxonomies and all the other essential parts of developing a WordPress theme are now easier than ever with the RAD Theme Engine.
Quick Start
Inside of your site's wp-content/themes folder, run the following command to create a new Rad Theme Engine project.
composer create-project open-function-computers-llc/wp-theme <theme-name>
Next, enter your new theme's folder and run npm install to get dependencies.
cd <theme-name>
npm install
And that's it! Read about advanced installations and asset bundling on the docs.
Companion Fields
Themes define admin edit fields in yml files next to their templates (e.g. tpl-flexible.yml, single-<post-type>.yml). Flexible fields render a modal slice picker, and repeaters render as sortable tables. Beyond the standard field keys, layout definitions support:
# tpl-flexible.yml fields: content: type: flexible layouts: Hero: title: Hero description: Full-width headline with a background image and one button. thumbnail: hero # assets/hero.png fields: headline: { type: text, required: true }
description:— shown under the layout name in the slice picker modal.thumbnail:— 16:9 preview image (1280×720 recommended) in the picker. A bare filename resolves inside the theme'sassets/directory; a value containing a slash is relative to the theme root (so a dedicated folder is explicit:assets/thumbnails/hero). The extension may be omitted and is resolved in this order: png, jpg, jpeg, webp, svg, gif.
Repeaters
Repeater rows are added with a plain "Add Row" button. Set the label explicitly in the yml — there is no automatic singularisation of the field label:
# tpl-flexible.yml fields: features: type: repeater label: Features add button text: Add Feature
Sharing layouts with imports:
Repeated slice definitions can live once in a shared file (e.g. slices/base.yml with a top-level layouts: map) and be pulled into any companion yml. imports: is only valid at the top of the file — never inside a field definition — and when it's present a flexible field needs no layouts (or even a definition body) below it unless it adds or overrides layouts of its own:
# single-service-area.yml imports: slices/base.yml # or a list of paths fields: content: flexible # all layouts come from the import other: type: flexible layouts: Intro: { ... } # local layouts win over imported ones of the same name
Imported layouts load first; a local layout with the same key replaces the imported one in place. Paths are relative to the theme root (imports outside the theme are rejected), imports may nest, cycles are detected, and resolution errors are shown inline on the field instead of failing silently.
Collapsed flexible slices stay collapsed: collapse state is stored per post + field + block in the browser's localStorage and restored on the next edit.
Local Development & Testing
This section is for contributors who want to work on rad-theme-engine itself with a full TDD setup.
Prerequisites
- PHP >= 8.1
- Composer
- MariaDB or MySQL running locally
- WP-CLI (
wp) - A local WordPress install (see below)
1. Clone the repos
Pick a working directory (e.g. ~/programming/php/ofco) and clone both repos into it:
git clone https://github.com/open-function-computers-llc/rad-theme-engine.git git clone https://github.com/open-function-computers-llc/wp-theme.git
2. Set up a local WordPress install
mkdir wordpress && cd wordpress wp core download wp config create --dbname=rad_tdd --dbuser=root --dbpass=YOUR_PASSWORD --dbhost=localhost wp core install \ --url=localhost:8080 \ --title="RAD TDD" \ --admin_user=admin \ --admin_password=password \ --admin_email=dev@localhost.com
Start the built-in PHP server to browse the site:
php -S localhost:8080
3. Create a dev theme with a symlinked package
From inside wp-content/themes, scaffold a new theme:
cd wordpress/wp-content/themes
composer create-project open-function-computers-llc/wp-theme dev-theme
Then update dev-theme/composer.json to add a path repository so Composer symlinks your local clone instead of downloading from Packagist:
{
"name": "open-function-computers-llc/wp-theme",
"require": {
"open-function-computers-llc/rad-theme-engine": "*"
},
"repositories": [
{
"type": "path",
"url": "/path/to/your/rad-theme-engine",
"options": {
"symlink": true
}
}
],
"autoload": {
"psr-4": {
"Helpers\\": "helpers/"
}
},
"minimum-stability": "dev",
"prefer-stable": false
}
Run composer update and verify the symlink:
ls -la vendor/open-function-computers-llc/
# rad-theme-engine -> /path/to/your/rad-theme-engine
Activate the theme:
cd /path/to/wordpress
wp theme activate dev-theme
Any changes you make in rad-theme-engine/src/ are now live immediately in the WordPress install — no reinstall required.
4. Set up the WordPress test suite
Clone wordpress-develop somewhere permanent on your machine:
git clone --depth=1 https://github.com/WordPress/wordpress-develop.git /path/to/wordpress-develop
Create a dedicated test database:
mysql -u root -p -e "CREATE DATABASE rad_tdd_tests;"
Copy and fill in the test config:
cp /path/to/wordpress-develop/wp-tests-config-sample.php \ /path/to/wordpress-develop/wp-tests-config.php
Edit wp-tests-config.php with your database credentials and point ABSPATH at your WordPress install:
define( 'DB_NAME', 'rad_tdd_tests' ); define( 'DB_USER', 'root' ); define( 'DB_PASSWORD', 'your_password' ); define( 'DB_HOST', 'localhost' ); define( 'ABSPATH', '/path/to/wordpress/' );
5. Configure the test environment
Inside rad-theme-engine, copy .env.example to .env and fill in your local paths:
cp .env.example .env
WP_TESTS_DIR=/path/to/wordpress-develop WP_TESTS_CONFIG=/path/to/wordpress-develop/wp-tests-config.php
This file is gitignored — it never gets committed.
6. Install dev dependencies and run the suite
cd rad-theme-engine composer install make test
You should see WordPress boot and all tests pass:
Installing...
Running as single site...
PHPUnit 9.6.23 by Sebastian Bergmann and contributors.
..... 5 / 5 (100%)
OK (5 tests, 13 assertions)
Available make commands
| Command | Description |
|---|---|
make test |
Run the full test suite |
make coverage |
Run tests and generate an HTML coverage report in ./report |
make watch |
Re-run tests automatically on every file save (requires entr) |
Install entr on Debian/Ubuntu with sudo apt install entr, or on macOS with brew install entr.
Writing tests
Tests live in the tests/ directory. Integration tests that need WordPress extend WP_UnitTestCase. The Site class is a singleton, so each test class that instantiates it must reset the instance in tearDown:
<?php namespace ofc\tests; use ofc\Site; use WP_UnitTestCase; class MyFeatureTest extends WP_UnitTestCase { protected function tearDown(): void { parent::tearDown(); $reflection = new \ReflectionClass(Site::class); $instance = $reflection->getProperty('instance'); $instance->setAccessible(true); $instance->setValue(null, null); } /** @test */ public function myFeatureWorkAsExpected() { Site::getInstance([ "handlebars" => false, // your config here ]); // your assertions here } }
Pure unit tests that don't need WordPress (like testing utility methods) can extend PHPUnit\Framework\TestCase directly and will run faster.
Example Projects
- Shirt Store – Demonstrates custom post types, taxonomies, handlebars, and more.
Authors
- Kurtis Holsapple – @lapubell
- Escher Wright-Dykhouse – @escherwd
- Gabriel Johnson - @gabriel-johnson
License
Licensed under the MIT license, see LICENSE
