B P Collins architecture
B P Collins (www.bpcollins.co.uk) is a WordPress site for a Buckinghamshire law
firm, hosted on WP Engine. This repository holds only the theme plus site-root
files — a fork of the StrategiQ base theme, heavily extended. Everything editors
touch is an ACF Gutenberg block; everything else is a custom post type.
Start with onboarding if you have not run the site locally yet.
Shape of the system
Section titled “Shape of the system”flowchart TD subgraph Content P[post = Insight] ; T[team] ; S[service] ; SEC[sector] C[career] ; O[office] ; R[review] ; F[faq] TX[taxonomies: positions, services, sectors on team] end subgraph Theme B[49 ACF blocks in blocks/*] ; TPL[page templates single-*.php] ; PA[partials + BP* classes] AJ[2 admin-ajax handlers: peopleFilter, hubFilter] SQ[search relevance SQL filters] SCH[Yoast graph pieces + FAQPage emitter] end subgraph Plugins Y[Yoast SEO] ; A[ACF PRO] ; CF[Contact Form 7] ; FLY[Fly Dynamic Image Resizer] RK[WP Rocket] ; CH[CookieHub] ; RD[Redirection] ; G[Google Apps Login] end subgraph Third party MB[Mapbox GL] ; GTM[GTM + Universal Analytics] ; MP[Moneypenny chat] ; RS[ReviewSolicitors] HIBP[HIBP range API] end Content --> Theme A --> B FLY --> PA Y --> SCH CF --> AJ Theme --> Third Theme -.->|git ftp via Bitbucket Pipelines| WPE[WP Engine: dev / staging / production]Content model
Section titled “Content model”| Type | Public? | URL | Purpose |
|---|---|---|---|
post |
yes | /<slug>/ |
Knowledge Hub “Insights” — relabelled in wp-admin |
team |
yes | /people/<slug>/ |
Solicitors and staff |
service |
yes | /services/… |
Practice areas, three levels deep |
sector |
yes | /sectors/<slug>/ |
Industry pages |
career |
yes | /careers/listing/<slug>/ |
Vacancies |
office |
no | — (page_link field) |
Locations for maps and the footer |
review |
no | — | Client reviews for the carousel and aggregate rating |
faq |
no | — | Reusable Q&A entries |
testimonial |
no | — | Largely unused |
Taxonomies positions, services and sectors all attach to team only, with
rewrites under /people/. The service-type taxonomy is an admin-only
classification on service.
The one distinction that causes the most confusion: service is a post type
(the marketing pages) and services is a taxonomy on team (who does what). They
are related by matching slugs or titles, never by ID.
Features
Section titled “Features”| Feature | What it covers |
|---|---|
| People directory and profiles | /people/, filters, name autocomplete, profile pages, seniority ordering |
| Knowledge Hub | Insights, AJAX filtering, category pages, Related Experts |
| Service and sector pages | The service hierarchy, conditional sidebar, LegalService schema |
| Careers and vacancies | career posts, accordion, generated application dropdown |
| Contact forms and enquiries | CF7 layer: form selection, honeypots, dynamic dropdowns, GA client ID |
| Reviews and ratings | review posts, carousel, aggregateRating |
| FAQs and FAQPage schema | Reusable FAQs and the request-scoped JSON-LD |
| Offices and maps | office posts, map-block, location-tabs, Mapbox |
| Drink driving penalty calculator | The gated four-question lead tool |
| Site search and relevance ranking | Custom SQL filters that rank people and services above insights |
| SEO and structured data | Yoast graph pieces, breadcrumbs, canonicals, video/podcast JSON-LD, sitemap |
| Header, footer and navigation | Hand-built mega menu, third-party scripts, footer content |
| ACF block system and asset pipeline | Block auto-registration, BlockMeta, responsive images, gulp |
| Security hardening | Headers, HSTS, breached-password rejection, SSO |
| Build and deployment | Gulp, committed assets, three git-ftp pipelines |
How the pieces fit
Section titled “How the pieces fit”Page composition happens two different ways, and knowing which applies is the first question to ask of any template:
- Editor-composed.
page.phpjust callsthe_content(); the page is whatever blocks the editor stacked. ACF’senqueue_assetsloads each block’s CSS/JS, with the first two blocks’ CSS inlined for LCP. - Template-composed.
single-service.php,single-team.php,single.php,category.php,page-news.php,single-career.phpandsearch.phprequireblock templates in a fixed order and enqueue their assets by hand in an array at the top of the file. Editors cannot reorder these.
Data access goes through thin accessor classes in inc/classes/ — BPPost,
BPTeam, BPService, BPSector, BPCareer, BPOffice, BPReview — each wrapping
get_post() plus that type’s ACF fields. BlockMeta does the same job for block
fields and adds render helpers. partial() renders partials/*.php with a $_DATA
payload.
Dynamic behaviour is two admin-ajax handlers, both in functions.php:
peopleFilter (people directory) and hubFilter (Knowledge Hub). Both re-render
HTML server-side and the JS swaps it into the page. Both duplicate card markup that
also exists as a partial — a recurring theme in this codebase.
Search, SEO and schema are cross-cutting: raw SQL filters for relevance, Yoast graph pieces for entity markup, and a request-scoped global for FAQ schema.
Third parties: Mapbox GL (maps), GTM plus a still-embedded Universal Analytics snippet, Moneypenny live chat, ReviewSolicitors rating widgets, and the HIBP range API during password changes.
Environments and deployment
Section titled “Environments and deployment”Three WP Engine environments fed by Bitbucket Pipelines using git ftp push over
SFTP: development and staging deploy automatically on push, production deploys
from master via a manual pipeline. Credentials are Bitbucket repository variables.
get_cache_key() (inc/enqueue.php:50) versions assets from the deployed commit by
reading .git-ftp.log on WP Engine, and from time() locally.
Codebase-wide characteristics
Section titled “Codebase-wide characteristics”Recurring patterns worth knowing before your first change:
- Assets are committed and CI does not build. Unbuilt SCSS/JS deploys as a no-op.
- No tests anywhere. No runner, no CI test step. All verification is manual.
- Duplicated markup between templates and AJAX handlers (people cards, hub cards, FAQ accordions). Changing one and not the other is the most common regression.
- Hardcoded slugs and titles as integration points:
/people/,/knowledge-hub/,/news/,/careers/, category namesvideosandpodcasts, the service titles “Property” and “Notary Services”. Renaming content can break code silently. - A lot of commented-out code, including a registered-but-disabled
newspost type whose templates and breadcrumbs still exist. Treatnewsas legacy. - Six blocks are unimplemented stubs still holding generator boilerplate:
reviews-tabs,content-strip,service-tiles,sticky-accordion,sub-navigation,full-width-content. functions.phpis ~1,690 lines and holds the AJAX handlers, search relevance SQL and the team-ordering machinery. New work belongs in a file underinc/, required fromfunctions.php, not appended to it.- The theme is a fork of the StrategiQ base theme. Files like
inc/security-functions.php,inc/image-functions.phpandinc/partial.phpare shared lineage — prefer their filters over editing them, so future base-theme merges stay possible.