Skip to content

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.

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]
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.

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

Page composition happens two different ways, and knowing which applies is the first question to ask of any template:

  1. Editor-composed. page.php just calls the_content(); the page is whatever blocks the editor stacked. ACF’s enqueue_assets loads each block’s CSS/JS, with the first two blocks’ CSS inlined for LCP.
  2. Template-composed. single-service.php, single-team.php, single.php, category.php, page-news.php, single-career.php and search.php require block 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.

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.

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 names videos and podcasts, the service titles “Property” and “Notary Services”. Renaming content can break code silently.
  • A lot of commented-out code, including a registered-but-disabled news post type whose templates and breadcrumbs still exist. Treat news as legacy.
  • Six blocks are unimplemented stubs still holding generator boilerplate: reviews-tabs, content-strip, service-tiles, sticky-accordion, sub-navigation, full-width-content.
  • functions.php is ~1,690 lines and holds the AJAX handlers, search relevance SQL and the team-ordering machinery. New work belongs in a file under inc/, required from functions.php, not appended to it.
  • The theme is a fork of the StrategiQ base theme. Files like inc/security-functions.php, inc/image-functions.php and inc/partial.php are shared lineage — prefer their filters over editing them, so future base-theme merges stay possible.