Developer Guide
Developer Guide Free & ProExtending Learnomy from a theme or a plugin of your own.
Pages in this section#
| Page | Description |
|---|---|
| WP-CLI Commands | The wp learnomy command group: enrolling, inspecting a learner, repairing data after an import, migrating, and exports. |
| Template Overrides | Copying a Learnomy template into your theme at theme/learnomy/*.php so you can change the markup without touching the plugin. |
| Hooks Reference | Every action and filter Learnomy fires, with its arguments and where it is fired. Generated from the code. |
| Hooks Reference (Pro) | The same for Learnomy Pro's extensions. |
| Store bridges for developers | Pro. Connect another store or membership plugin to Learnomy access: the bridge base class, the shared access picker and its two routes. |
| Background Jobs | The scheduled and queued jobs Learnomy and Learnomy Pro run, how often, and how to check them. |
| Payment Gateways and Purchase Sources | The purchase flow, a sample gateway adapter, adding a product type, adding an inbound purchase source, and the conformance gate. |
| Registration and Login Security | What protects the sign-up and sign-in routes, what does not, and how to add your own challenge. |
Where the rest of the developer material lives#
Learnomy's hooks and filters are explained on the page for the feature they belong to; each of these pages ends with a For developers section. The Hooks Reference lists all of them in one place and is regenerated from the code, so it cannot drift from it:
| Looking for | Page |
|---|---|
| URL slugs, the route registry, adding pages to a nav menu | Pages and URL Slugs |
| The private-academy gate and the routes it covers | General Settings |
| Per-course search snippet fields | Course SEO and Sharing |
| Canonical, Open Graph, and who owns the head tags | SEO Plugin Handoff |
| Repointing instructor links at another profile | Instructor Public Profile |
| REST endpoints, authentication, the mobile app contract | REST API and App |
| Registering LMS actions for AI assistants | AI Agents and the Abilities API |
| Outbound webhooks and their event list | Outgoing Webhooks |
| Giving access for a sale made in an external system | Incoming Access Webhook |
Working with stored dates#
Learnomy's tables declare their timestamp columns datetime NOT NULL DEFAULT '0000-00-00 00:00:00', which is what MySQL stores when a write leaves the
column out. That value is awkward to test for: it is not empty, and
strtotime() returns a large negative number, which is truthy - so the
obvious guard formats it as "November 30, -0001" and a comparison reads it as
long past.
If you render a stored date in a template or an extension, use the helper rather than writing your own guard:
echo esc_html( \Learnomy\date_label( $row->created_at ) ); // "August 3, 2026" or "—"
echo esc_html( \Learnomy\date_label( $row->expires_at, 'Never' ) ); // custom fallback
echo esc_html( \Learnomy\date_label( $row->issued_at, '—', 'Y-m-d' ) );
It returns the fallback for all four ways a date can be missing - NULL, an empty
string, the zero-date sentinel, and anything that does not parse - so you cannot
handle three of them and meet the fourth in production. \Learnomy\is_zero_date( $value ) is the bare predicate when you need to branch rather than render.
Writes are handled for you: Model::insert() and Model::update() fill in
created_at and updated_at when you omit them, so new rows never carry the
sentinel. A value you pass explicitly is always kept - an importer backdating
records keeps its own dates - and created_at is only ever set on insert, so
an update cannot rewrite a row's history.