Self-contained, reproducible WordPress site used as the source for a WP → Django migration challenge.
The site is a small consumer-reviews directory: brands with profile pages plus user-submitted reviews linked to each brand. Two custom post types, one taxonomy, ACF fields, Yoast SEO metadata.
cp .env.example .env
make upAfter ~20 seconds:
- Public site: http://localhost:8080
- WP admin: http://localhost:8080/wp-admin (
admin/admin) - phpMyAdmin: http://localhost:8081
- REST root: http://localhost:8080/wp-json/wp/v2/
The DB is auto-imported from db/dump.sql on first boot. No manual setup required for candidates.
| Service | Image | Port | Purpose |
|---|---|---|---|
db |
mariadb:11 |
- | Database; auto-imports db/dump.sql |
wordpress |
wordpress:6.9-php8.3-apache |
8080 | WP + Apache + PHP |
wpcli |
wordpress:cli-php8.3 |
- | Helper for re-seeding / dumping |
phpmyadmin |
phpmyadmin:5 |
8081 | Optional DB browser |
docker-compose.yml # the stack
.env.example # config defaults
Makefile # make targets
db/dump.sql # seeded database snapshot
scripts/seed.sh # bootstrap script (install + plugins + seed)
wp-content/plugins/ # WP default plugins + ACF + Yoast (committed for offline reproducibility)
wp-content/mu-plugins/
cpt.php # registers `brand` and `review` CPTs and `industry` taxonomy
acf-fields.php # registers ACF field groups in code
wp-content/themes/
consumer-reviews/ # custom theme used by the site
WP core files and the default themes (e.g. twentytwentyfour) live inside the wp_files named volume and are recreated from the image on first boot. Only the custom consumer-reviews theme is committed to the repo.
make up # start db + wordpress + phpmyadmin
make down # stop containers (keeps volumes)
make reset # stop AND wipe volumes (next 'up' re-imports dump.sql)
make logs # tail wordpress + db
make wp ARGS="plugin list" # run wp-cli inside the stack
make bootstrap # full re-seed: WP core install + plugin install + content seed
make dump # export the current DB to db/dump.sql
make status # show running services
If you change the schema or seed data:
make reset
make up
make bootstrap
make dump
git add db/dump.sql wp-content/plugins
git commit -m "Refresh seed data"make bootstrap is idempotent enough to re-run, but the cleanest path is reset → up → bootstrap → dump.
brand CPT at /brand/<slug>/
- Standard fields: title, content (body), featured image (optional)
- ACF fields:
website_url,founded_year,headquarters,average_rating - Taxonomy:
industry - Yoast:
_yoast_wpseo_title,_yoast_wpseo_metadescin postmeta
review CPT at /review/<slug>/
- Standard fields: title, content (review body), post_author (mapped to a real WP user)
- ACF fields:
rating(1-5),reviewer_name,reviewer_location,brand(post object → brand id)
Seeded volume: 5 brands (one per industry), 60 reviews (12 per brand) authored by 8 reviewer users, 3 static pages (Home/About/Contact), 1 admin user.
The active theme is a custom one at wp-content/themes/consumer-reviews/ (committed to the repo). It renders:
- Homepage with featured brands and recent reviews
- Brand archive (
/brand/) — card grid - Single brand page (
/brand/<slug>/) — hero with industry badge, star rating, website / founded / HQ metadata, body content, and full list of customer reviews - Review archive (
/review/) — paginated list - Single review (
/review/<slug>/) — back-link to brand, star rating, reviewer name/location, body - Page template for About / Contact / Home pages
The theme is intentionally small (~10 PHP files + one stylesheet) and is meant to show candidates what the data renders as. They are not expected to clone the theme — just to migrate the underlying data.
/wp-json/wp/v2/brands— list brands (note:rest_baseisbrandseven though post type isbrand)/wp-json/wp/v2/brands/<id>— single brand with ACF fields underacf/wp-json/wp/v2/reviews— list reviews/wp-json/wp/v2/industry— taxonomy terms/wp-json/wp/v2/users— reviewer users/wp-json/wp/v2/pages— static pages
ACF fields are exposed natively via show_in_rest: true set in wp-content/mu-plugins/acf-fields.php.
- The
wp-content/plugins/advanced-custom-fields/andwp-content/plugins/wordpress-seo/plugin code is GPL and committed to the repo so candidates don't need internet on first boot. wp-content/plugins/akismet/andhello.phpare WP defaults — left in place but inactive.- The site URL is hardcoded as
http://localhost:8080in the dump (inwp_options.siteurlandhome). Changing the port requires a search/replace indb/dump.sqlbefore commit, or just override viaWP_HOME/WP_SITEURLconstants. _yoast_wpseo_*postmeta is seeded for brand posts only.