Skip to content

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 directoriesapp.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 own sessions layout) runs only its own paths. Defaults to true.
  • Application::migrate() — public standalone entry point: connects the DB if needed (no session start / addons / session tracking), honours the same migrations/migration_cutoff/features config, and never throws (failures logged).
  • On-demand cross dependenciesMigrationRunner::setDependencyPool() lets a migration declare a $dependencies on one not in the batch; the runner pulls it (transitively) from Application::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 a RedisAdapter bound to the manager's host/port/db/password + prefix.
  • BroadcastingManager::instance() / setInstance(?self) — a lazy default manager pre-wired with a RedisDriver on the manager, redis active. Named instance() to avoid clashing with the existing setDefault(string) driver selector.
  • DelayedQueue::redis(string $namespace) — a factory for a Redis-backed delayed queue bound to the manager (<prefix><namespace>:delayed/:data).
  • Database session timezone — a new database.timezone setting (default unset). When set, the framework issues SET 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.