Admin anything from Symfony — and bring your own everything.
Polysource is two products built on the same primitives, MIT-licensed, running side by side in the same app:
- The EasyAdmin filter bridge — drop it next to your existing EasyAdmin install (4.24+ or 5.0+) and your listings gain the UX below. Zero fork, zero config.
- The standalone admin engine — admin for resources that aren't Doctrine entities: Messenger failed messages, Redis keys, files on S3, external REST APIs, Meilisearch indexes.
composer require polysource/easyadmin-filter-bridgeThat one composer require — no config, no template change — upgrades every index page:
| EasyAdmin alone | with the bridge | |
|---|---|---|
| Filter modal | flat field list | tabs + group accordions (server-rendered, zero JS) |
| Active filters | invisible until you reopen the modal | chips bar above the table, one-click remove |
| Views | — | saved views — per-user / per-team / public, default ★ per user |
| Columns | fixed | per-user visibility + reordering + widths |
| Export | — | filter-aware streaming CSV / XLSX |
| Sharing a filtered list | copy a 400-char URL | short URL tokens |
| Row details | — | expandable per-row panels — lazy-loaded, per-row permission, no-JS fallback |
Plus: session-persisted filters per CRUD, 8 enhanced built-in filter types, 4 custom filters (Between/In/NotNull/FullText), filter-from-cell menu, per-column quick filter row, frozen columns, row density toggle, conditional row styles, bulk dry-run counts, toasts, keyboard-shortcuts panel, expandable row details (per-entity providers, lazy fragment, even a nested Polysource listing as the panel). Everything is opt-in per page, themable via CSS variables, translated (en/fr), and CSP-friendly (no inline CSS/JS).
![]() |
![]() |
| Filter modal — tabs + groups, no JavaScript required | Saved views — private / team / public |
Every feature has a server-side baseline (ADR-027): it all works before a single line of JavaScript loads. Honest per-filter comparison vs upstream EA: whats-new.md. Walkthrough: getting started.
composer require polysource/symfony-bundle polysource/adapter-messengerThe contract a resource must satisfy is 3 read methods + 3 write methods — no Doctrine inheritance, no entity manager. Once satisfied, every capability applies uniformly: filters, saved views, expandable row details, audit trail, async bulk actions, search palette, dashboard widgets, workflow integration.
![]() |
![]() |
| Messenger failed transport — browse, retry, dismiss, purge | Files on S3 via Flysystem — same UX, same filters |
6 adapters ready (Doctrine cohabitation, Messenger, Redis, S3/Flysystem, HTTP REST, Meilisearch — each ~80-300 LOC and a model for the next one you'll write), plus the opt-in capability packages: Cmd+K cross-resource search, dashboard widgets, async bulk with live Mercure progress + mid-flight cancel, GDPR Art. 30 / HIPAA audit trail, Symfony Workflow integration, field/action/resource-level permissions — down to per-record action gating (your voters receive the row's DataRecord as subject). Row details work on every adapter (HasRowDetailsInterface), including a nested Polysource listing as the panel (RowDetail::listing()).
git clone https://github.com/polysource/polysource && cd polysource
make showcase # http://localhost:8084 — every capability live (login on the page)Full stack: PHP 8.4 + Symfony 7.4 LTS + EasyAdmin 5 + Postgres + Redis + Mercure + Meilisearch + MinIO. Smaller focused demos: make demo-bridge (EA 5 host), make demo-bridge-v4 (EA 4 / Sf 6.4 floor), make demo-filter (no EasyAdmin at all), make demo (Messenger dashboard). Guided tour with screenshots: showcase-tour.md.
Symfony's admin landscape is mature for one shape of resource: a Doctrine ORM entity with a CRUD lifecycle. Anything else — a queue, a feature flag, a file on S3, an upstream API, a search index — sits outside that shape. Polysource starts from the other end: satisfy a 6-method contract, and every capability applies to whatever the resource is. That's why one admin handles your products table AND your failed transport AND your Redis flags AND your search index — one auth model, one audit trail, one progress tracker.
Most contracts are 1-5 methods — that's the design budget; past 5 we open an ADR (ADR-010).
| You want to… | Implement | Methods | Time |
|---|---|---|---|
| Admin a new data source (Stripe, your microservice, a queue) | DataSourceInterface |
3 read + 3 write | 1-2 hours |
| Plug a custom backend into Cmd+K search | SearchProviderInterface |
3 | 30 min |
| Pipe the audit log to Splunk / Datadog / a SIEM | AuditLoggerInterface |
1 | 15 min |
| Render a custom dashboard tile | WidgetInterface |
5 | 1 hour |
| Persist saved views in Redis instead of Doctrine | SavedViewStorageInterface |
5 | 1 hour |
| Replace the permission backend (LDAP / OPA / custom voter) | PermissionInterface |
1 | 15 min |
| Custom HTTP pagination (Link headers, RFC 5988…) | PaginationStrategyInterface |
2 | 30 min |
| Format a filter chip your way | ChipFormatterInterface |
1 | 10 min |
| Ship a self-contained capability bundle | AdminPluginInterface + #[AsPlugin] |
3 metadata | 1 hour |
No global registries, no XML, no magic — every extension is a Symfony service tag scanned by tagged_iterator(...). Full map with sample code: extensibility.md · Cookbook: build your own adapter.
- Not a fork of EasyAdmin. EA is excellent for Doctrine CRUD; we don't try to replace it. The bridge enriches it without forking.
- Not a frontal competitor on Doctrine. For pure Doctrine entities, keep using EasyAdmin.
polysource/adapter-doctrineexists for cohabitation, not replacement. - Not a no-code internal-tool builder (use Retool / Appsmith for that).
- Not a BI dashboard (use Grafana / Metabase).
- Not a SaaS — self-hosted Symfony packages, MIT-licensed.
- 1256 unit + functional tests and 28 integration tests (real kernel + SQLite) in the package matrix
- 47 browser E2E tests (Symfony Panther) + 24 showcase WebTestCase tests + 15 adapter real-container tests (Redis / Meilisearch / MinIO / WireMock) on every push
- PHPStan level max across all packages · PHP-CS-Fixer PSR-12 + Symfony rules
- Core coverage gate ≥ 90% (
polysource/coresits at 99%+) - CI matrix: PHP 8.2/8.3/8.4 × Symfony 6.4/7.2/7.4 × EasyAdmin 4.24/5.0, plus a bridge-alone floor smoke (Sf 6.4 + EA 4)
v1.2.0 — API frozen since v1.0.0 (2026-08-06) per ADR-018; SemVer applies strictly — breaking changes ship in major versions only, signalled in the CHANGELOG. 16 packages on Packagist, split automatically from this monorepo (ADR-026).
Baseline (ADR-015, v1.0 floors per ADR-011): PHP 8.2 → 8.4 · Symfony 6.4 / 7.2 / 7.4 LTS (^8.0 allowed by constraints, forward-compat, not yet in CI) · EasyAdmin 4.24+ / 5.0+ · Doctrine ORM 2.20+ / 3.6+.
Adopt early with eyes open — the honest limitations list (minimal UI surfaces, field helpers, cursor pagination trade-offs) lives in docs/user/installation.md and ADR-028 scope discipline.
The 16 packages
| Package | What it does |
|---|---|
polysource/core |
38 public types, zero Symfony dep — pure PHP 8.2+ contracts |
polysource/filter |
Filter primitives (collection, criteria, session persistence, saved views) + shared Stimulus controllers — usable standalone in any Symfony app |
polysource/easyadmin-filter-bridge |
Drop-in for EasyAdmin (4.24+ or 5.0+) — enhanced filter types, chips, saved views dropdown, column prefs, expandable row details |
polysource/symfony-bundle |
Symfony wiring for the standalone admin: DI, routing, controllers, CSRF, pagination caps, row detail panels |
polysource/twig-theme |
Default Bootstrap 5 templates for index/detail/forms/fields/row-detail panels |
polysource/adapter-messenger |
Browse + retry / dismiss / retry-all / purge Messenger failed envelopes |
polysource/adapter-doctrine |
Doctrine ORM cohabitation case (whitelisted filter properties) |
polysource/adapter-redis |
5 type-pure Redis data sources (string/list/hash/set/sorted-set), SCAN cursor pagination |
polysource/adapter-flysystem |
Browse + write files on S3 / local / Azure / GCS via Flysystem |
polysource/adapter-http |
Admin external REST APIs via Symfony HttpClient (page-number + cursor strategies) |
polysource/adapter-meilisearch |
Browse + write Meilisearch indexes |
polysource/audit |
GDPR Art. 30 audit trail (Doctrine storage, CSV export, retention purge) |
polysource/bulk-async |
Bulk actions over Messenger with live Mercure progress + cancel mid-flight |
polysource/widgets |
Dashboard widgets (KPI counters, top-N lists, sparkline charts) |
polysource/search |
Cross-resource search palette (Cmd+K) with fan-out aggregator |
polysource/workflow-bridge |
Symfony Workflow integration (auto transition buttons, state chip) |
User docs · Installation · Getting started · Bridge getting started · Row details (bridge) · Row details (native) · Theming · i18n · Cookbook · ADRs · Roadmap · Changelog
Read CONTRIBUTING.md. Scope is strict per ADR-012 and ADR-028 — check product-vision.md §2 before opening a feature request.




