28 July 2026¶
2 changes:
- Per-column search works again on server-side DataTables
- Serving init-less apps: migrations, capability accessors, test DB, daemon status
Per-column search works again on server-side DataTables¶
Html\Datatable emits a DataTables 1.10+ client config (serverSide + ajax),
so the browser sends columns[N][search][value] for column filters instead of
the legacy sSearch_N. Datatable\Datasource translated the modern paging,
ordering and global-search parameters into the legacy names it reads internally,
but not the per-column ones — so every column filter widget, footer search box
and fnFilter(value, N) call silently returned the unfiltered set.
Fixed¶
The modern → legacy translation in Datasource::render() now also maps:
| Modern parameter | Legacy parameter |
|---|---|
columns[N][search][value] |
sSearch_N |
columns[N][orderable] |
bSortable_N |
An sSearch_N the caller pre-set in $_POST before calling render() is never
overwritten by the client's value — applications inject their own server-side
column filters that way (mapping one visible filter onto a different database
column, for instance).
Column boolean flags are normalized through a single helper, so searchable /
orderable map to the same "true" / "false" strings whether they arrive as
form-encoded strings, real booleans or 0 / 1.
The "request already translated" marker moved off sEcho onto a dedicated
Datasource::MODERN_REQUEST_FLAG key. The translation writes sEcho itself, so
using it as the marker meant a second render() in the same request looked like
a legacy call and answered a modern client with aaData / iTotalDisplayRecords
instead of data / recordsFiltered.
Legacy DataTables 1.9 clients are unaffected: they send sSearch_N directly and
never enter the translation branch.
Tests¶
Five tests added to tests/Unit/Pramnos/Html/Datatable/DatasourceTest.php: modern
per-column search filters the result set, is ignored for a non-searchable column,
does not clobber a pre-set sSearch_N, and the response format stays modern
across repeated render() calls in one request.
Serving init-less apps: migrations, capability accessors, test DB, daemon status¶
A cluster of additive changes so that an application which deliberately does not adopt the framework's MVC request lifecycle (attribute-routed front controllers, "Services + API + SPA" apps, console bootstraps) still gets the framework's guarantees — auto-migration, pre-wired Redis capabilities, a real test database, and a queryable daemon supervisor — without re-implementing the wiring itself.
See the Migration, Cache, Redis, Queue, Realtime, Database, Testing and Console guides for the full walkthroughs.
Added¶
Auto-migration for non-MVC apps. Auto-run migrations are no longer limited to
the framework feature directories or the full init()/exec() request lifecycle:
- App-declared directories —
app.php'migrations' => ['paths' => [...]]lists app-owned dirs that auto-run scans in addition to the framework dirs, on the same fingerprint fast-path. - Framework opt-out —
'migrations' => ['framework' => false]skips the framework feature directories, so an app whose schema collides with a framework table (e.g. its ownsessionslayout) runs only its own paths. Defaults totrue. Application::migrate()— public standalone entry point: connects the DB if needed (no session start / addons / session tracking), honours the samemigrations/migration_cutoff/featuresconfig, and never throws (failures logged).- On-demand cross dependencies —
MigrationRunner::setDependencyPool()lets a migration declare a$dependencieson one not in the batch; the runner pulls it (transitively) fromApplication::frameworkMigrationPool()and orders it first, instead of failing "unknown dependency".
Capability accessors on the ConnectionManager. The Redis-backed capabilities can
now be obtained pre-wired from the shared Pramnos\Redis\ConnectionManager, so an app
configures Redis once (ConnectionManager::setInstance() in bootstrap) and gets the
capabilities without re-building adapters/drivers:
FlatCache::default()/setDefault(?FlatCache)— a lazy, process-default flat cache on aRedisAdapterbound to the manager's host/port/db/password + prefix.BroadcastingManager::instance()/setInstance(?self)— a lazy default manager pre-wired with aRedisDriveron the manager,redisactive. Namedinstance()to avoid clashing with the existingsetDefault(string)driver selector.DelayedQueue::redis(string $namespace)— a factory for a Redis-backed delayed queue bound to the manager (<prefix><namespace>:delayed/:data).Databasesession timezone — a newdatabase.timezonesetting (default unset). When set, the framework issuesSET TIME ZONE(PostgreSQL) /SET time_zone(MySQL) per connection; unset ⇒ no SET, so the connect path is byte-identical for existing apps.
All accessors resolve ConnectionManager::getInstance() lazily inside the method body,
so an app that binds the manager during bootstrap is always in effect.
Init-less test database. Pramnos\Framework\Testing\TestDatabase — a standalone
helper providing a raw \PDO connection (from the database settings, honouring
database.timezone) plus assertDatabaseHas() / assertDatabaseMissing() and
setConnection/reset seams, without running the MVC request lifecycle. For apps
that never call init() (and whose own sessions schema collides with the framework's
session tracking) but still want to seed the real database with plain SQL.
Daemon health snapshot. Pramnos\Console\DaemonOrchestrator::status() — a public,
read-only health snapshot returning the orchestrator's own liveness (running/pid
from the singleton lock), heartbeat_age_seconds (per-cycle state file age), and its
managed daemons (last-persisted state, each enriched with live process status) —
without running a reconcile cycle. Lets a dashboard read the real orchestrator
((new MyOrchestrator())->status()) instead of keeping a separate legacy supervisor
just to report "is the supervisor up?".
Tests¶
ApplicationAppMigrationsTest + the app-declared/framework-off/public-migrate() cases
in the MySQL+PostgreSQL auto-migration suites; FlatCacheTest, BroadcastingManagerTest,
DelayedQueueTest, DatabaseTimezonePostgreSQLTest; TestDatabaseHelperTest;
DaemonOrchestratorTest.