phillipsharring / handlr-module-ab
A/B testing module for handlr apps -- deterministic variant assignment, event capture, and results.
Package info
github.com/phillipsharring/handlr-module-ab
pkg:composer/phillipsharring/handlr-module-ab
Requires
- php: >=8.4
- phillipsharring/handlr-backend: ^0.8
README
A/B testing module for handlr apps. Deterministic, session-scoped variant assignment; a {{#ab}} Handlebars helper for variant markup; and conversion capture with aggregated daily counts.
Install
composer require phillipsharring/handlr-module-ab npm install @phillipsharring/handlr-module-ab
One module, two manifests, lockstep version. Composer installs the PHP backend (service provider, pipes, tables, migrations); npm installs the frontend (Handlebars helper + client runtime).
Setup
1. Register the service provider in backend/app/config.php:
'providers' => [ // ... Handlr\Module\Ab\AbServiceProvider::class, ],
This auto-registers the routes (under the app's existing api.public and api.admin junctions) and the migrations. Seed data (which tests exist) stays app-owned.
2. Wire the frontend in your app entry (e.g. frontend/src/app.js):
import Handlebars from 'handlebars'; import { registerAbHelpers, ab } from '@phillipsharring/handlr-module-ab'; registerAbHelpers(Handlebars); // enables {{#ab "test" "variant"}}…{{/ab}} ab.init(); // fetch assignments + wire conversion capture
3. Run the migrations to create the ab_tests and ab_events tables:
composer run migrate
What's included
Backend (Handlr\Module\Ab\)
Routes registered on the app's junctions:
| Method | Path | Junction | Pipe |
|---|---|---|---|
| GET | /api/ab/assignments |
api.public |
GetAbAssignments |
| POST | /api/ab/capture |
api.public |
CaptureAbEvent |
| GET | /api/admin/ab |
api.admin |
GetAbTests |
| POST | /api/admin/ab |
api.admin |
PostCreateAbTest |
| GET | /api/admin/ab/{id} |
api.admin |
GetAbTestResults |
| PATCH | /api/admin/ab/{id} |
api.admin |
PatchUpdateAbTest |
AbService— deterministicassignVariant()(crc32 of session + test name), assignment lookup, and event recording.ab_tests/ab_eventstable migrations (auto-discovered viamigrationPaths()). Seed data (which tests to run) is app-owned — define it in your app's seeds againstHandlr\Module\Ab\Data\AbTestsTable.- Dispatches the
ab.event.capturedevent (AbEventCapturedEvent) on each capture, so apps can listen.
Frontend
registerAbHelpers(Handlebars)— the{{#ab "test" "variant"}}…{{/ab}}block helper.ab.init()— fetches assignments once per page lifetime, re-applies after boosted navigation, and delegatesdata-ab-captureclicks.capture(event)/getAssignments()— programmatic conversion tracking.
Markup conventions:
<!-- Static variant HTML --> <div data-ab-test="landing-cta" data-ab-variant="a">Original CTA</div> <div data-ab-test="landing-cta" data-ab-variant="b">New CTA</div> <!-- Conversion tracking --> <button data-ab-capture="signup">Sign up</button>
Removal
- Remove
AbServiceProvider::classfromconfig.php. - Remove the
registerAbHelpers/abimports from the app entry. composer remove phillipsharring/handlr-module-abandnpm uninstall @phillipsharring/handlr-module-ab.- Roll back the migrations (or drop the
ab_eventsandab_teststables).
Requires
phillipsharring/handlr-backend^0.8 (the framework release where A/B was extracted out of core)@phillipsharring/handlr-frontend^0.8 (peer — providesapiFetchand the boosted-nav lifecycle hooks)
License
MIT