byfareska / crud
Symfony bundle for admin panels: CRUDs from an attribute (Doctrine or DTO + use case drivers), a UI kit of Twig components, dashboards of widgets and a command palette - built on Symfony UX Live Components.
Requires
- php: >=8.4
- psr/cache: ^3.0
- psr/log: ^3.0
- symfony/config: ^8.0
- symfony/dependency-injection: ^8.0
- symfony/event-dispatcher: ^8.0
- symfony/form: ^8.1
- symfony/framework-bundle: ^8.0
- symfony/http-foundation: ^8.0
- symfony/http-kernel: ^8.0
- symfony/polyfill-php86: ^1.43
- symfony/property-access: ^8.0
- symfony/routing: ^8.0
- symfony/string: ^8.0
- symfony/translation: ^8.0
- symfony/twig-bridge: ^8.1
- symfony/twig-bundle: ^8.0
- symfony/ux-chartjs: ^2.20 || ^3.0
- symfony/ux-live-component: ^2.20 || ^3.0
- symfony/ux-twig-component: ^2.20 || ^3.0
- symfony/validator: ^8.0
- twig/twig: ^3.21
Requires (Dev)
- deptrac/deptrac: ^4.7
- doctrine/doctrine-bundle: ^3.0
- doctrine/orm: ^3.5
- intervention/image: ^4.4
- league/flysystem: ^3.29
- league/flysystem-memory: ^3.29
- phpstan/phpstan: ^2.3
- phpunit/phpunit: ^12.0
- rector/rector: ^2.7
- slevomat/coding-standard: ^8.31
- squizlabs/php_codesniffer: ^4.0
- symfony/asset-mapper: ^8.0
- symfony/mime: ^8.0
- symfony/security-bundle: ^8.0
- symfony/ux-autocomplete: ^3.5
- symfony/yaml: ^8.0
Suggests
- ext-intl: Accent-insensitive search of the command palette, ICU messages
- doctrine/doctrine-bundle: The doctrine driver (CRUD on entities) and dashboard layouts kept in the database
- intervention/image: (4 or later, with ext-imagick - keeps the colour profile of a photo - or ext-gd) The sizes of uploaded images: thumbnails of the lists, sizes of rich text (byfareska_crud.files.images)
- league/flysystem: Storages of uploaded files on a disk, FTP, SFTP, S3... (byfareska_crud.files)
- league/flysystem-bundle: The Flysystem storages as services, to name in byfareska_crud.files.storages
- symfony/asset-mapper: The panel's script through the application's importmap, with its Symfony UX controllers (byfareska_crud.assets: auto, the default, with symfony/stimulus-bundle)
- symfony/mime: Uploading files (byfareska_crud.files): what a file is, read from its bytes
- symfony/reprise: The assets of the panel built by Vite with the application's own (byfareska_crud.assets: reprise); or Webpack Encore, AssetMapper's importmap
- symfony/security-bundle: The actor of the panel (roles of resources, panels and widgets)
- symfony/ux-autocomplete: (3.5 or later) Relation filters of the lists searched as you type; widget settings with many choices to search in (the zone of the clock)
- symfony/ux-turbo: Page visits without reloads
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-09 15:47:28 UTC
README
Admin panels for Symfony, generated from one PHP attribute.
Put #[AsCrud] on a Doctrine entity and you get a list (search, filters, sorting, paging, bulk actions), a details
page with a preview drawer, add and edit forms, and delete with confirmation. Everything is built with Twig and
Symfony UX Live Components, so every list, form
and widget re-renders on its own without a JavaScript framework.
- Resources from an attribute, read by a driver:
doctrine(entities),dto(DTOs and use cases), or your own. - Panels: route scopes such as
/and/admin, each with its own menu, theme, roles and dashboard of widgets that users arrange themselves. - Themes: daisyUI (default), Tailwind CSS and Bootstrap 5. Override one template, not the whole theme.
- Command palette (the search field of the top bar): pages, commands and records, with a search algorithm you can replace.
- Files: uploads, galleries, a media library, rich text and image sizes, on any Flysystem storage.
The example application, byfareska/crud-example, shows all of it.
Requirements
- PHP 8.4+
- Symfony 8.0+ (Forms 8.1+)
- Symfony UX Live Component and Twig Component 2.20+ or 3.x, UX Chart.js
- Doctrine ORM 3 for the
doctrinedriver and dashboard layouts in a table (not needed for thedtodriver)
Strongly recommended
The panel works without these, but each one fixes something you will notice early. Install them together with the bundle:
composer require symfony/security-bundle symfony/ux-turbo symfony/ux-autocomplete
# and the intl extension of PHP (apt install php-intl, docker-php-ext-install intl, …)
| Package | Why | Without it |
|---|---|---|
symfony/security-bundle |
the signed-in user and their roles: role: of resources, panels and widgets, each user's own dashboard layout, the user menu with sign-out |
nobody is signed in and nobody has a role, so anything with a role is hidden from everybody. Dashboards can't be rearranged, because layouts belong to a user |
symfony/ux-turbo |
page visits without full reloads: moving between lists, forms and the dashboard keeps the scripts loaded and the toasts (data-turbo-permanent); the bundle marks its assets with data-turbo-track so a new build or a panel with other entries still loads the whole page |
every link and redirect is a full page load, so Stimulus and the live components start again on every page |
symfony/ux-autocomplete |
relations you type into, in the forms and in the filters of the lists: only the matching entities are loaded, in the scopes of the related resource (DoctrineScope), only for users who may add or edit records (forms) or see the list (filters), named as in the lists. The searches go through the panel's own address, so its firewall and access_control cover them |
a relation is a <select> of every related entity, which gets slow and unusable with thousands of rows |
ext-intl |
the bundle's texts are ICU messages (messages+intl-icu: plurals, dates and numbers in the user's locale), and the command palette matches without accents ("zolw" finds "żółw") |
translating any text of the panel throws "please install the intl PHP extension or symfony/polyfill-intl-messageformatter". The polyfills work, but format in English only; the accent-insensitive palette needs Normalizer (intl or symfony/polyfill-intl-normalizer) |
Once symfony/ux-autocomplete is installed, you don't need to configure anything for it:
- Every relation of a guessed form and every relation filter becomes a field you type into.
- The built-in assets already have its Stimulus controller and Tom Select in the look of the daisyui theme.
- With assets of your own, its recipe registers the controller, and the panel's stylesheet of your theme
(
panel.daisyui.css…) has Tom Select in the theme's look (Assets).
To keep plain choices, alias RelationFormField to ChoiceRelationFormField
(Doctrine resources).
Once you build your own assets, use Symfony Reprise. You don't need a build to start: the panel's assets come prebuilt, or through your AssetMapper. Once your templates or widgets add Tailwind classes of their own, or you change a theme, the stylesheet has to be built with them. With Symfony Reprise (Vite) that is one import in your stylesheet and one in your script, and it is what the bundle is tested with. Webpack Encore and AssetMapper also work, with more setup (Assets).
Quick start
composer require byfareska/crud
Symfony Flex registers the bundle. Import the routes of a panel:
# config/routes/byfareska_crud.yaml crud_panel: resource: panel # the name of the panel (its "scope") type: byfareska_crud
Put the attribute on an entity:
use Byfareska\Crud\Attribute\AsCrud; use Byfareska\Crud\Driver\Doctrine\DoctrineSource; #[ORM\Entity] #[AsCrud( name: 'product', path: '/products', icon: 'box-seam', // a Bootstrap Icons name, or any set: icon_template source: new DoctrineSource(Product::class), columns: ['name', 'category', 'createdAt'], section: 'Catalog', // the menu group: a translation key or plain text )] class Product { public function __construct(string $name, Category $category) { /* ... */ } // ... }
Open /products. You don't need a form type, a controller, a template or a __toString():
- Forms are guessed from the Doctrine mapping. Constructor arguments are filled from fields of the same name, and
properties without a setter (
createdAt) are shown in the list but left out of the edit form. - A relation is shown by the title of its own resource (its
title:or first column), else by__toString(), else by its id. - Texts fall back to readable names until you translate them ("Products", "Created at").
- Assets: nothing to set up.
- With AssetMapper (a
--webappproject), the panel runs its script through your importmap, with the Symfony UX controllers you have. - Without it, everything comes prebuilt.
- Once your templates need styles of their own, name entries and use Symfony Reprise (recommended), Encore or AssetMapper. It is one import in your stylesheet and one in your script (see Assets).
- With AssetMapper (a
When you need more, the configuration reference lists every option. The usual next steps: a theme built with your own Tailwind or Bootstrap (Assets), roles (Panels) and the Texts of your resources.
Documentation
| Configuration | every option of byfareska_crud with its default |
| Assets | the built-in assets, AssetMapper, Vite (Symfony Reprise), Webpack Encore |
| Doctrine resources | DoctrineSource: forms, filters, search, sorting, scopes, ids, errors of the database |
| DTO resources | resources of DTOs and use cases; a driver of your own |
| Resources in depth | columns and value formats, layouts, actions, bulk actions, pages of your own, access, events |
| Panels and themes | several panels, the menu, the signed-in user, themes and their templates |
| Dashboards | widgets, boards, layouts and who may change them |
| Command palette | its sources and a search of your own |
| Files | uploads, the library, rich text, image sizes |
| Texts | translation keys and the fallbacks |
| Extending | services to replace or decorate, resources declared by code |
| Troubleshooting | common errors and what they mean |
Tests
composer install && vendor/bin/phpunit make test make lint # deptrac, phpcs, PHPStan (level 10), Rector (dry run), composer validate, composer audit make lint fix=1 # Rector and phpcbf fix what they can first
deptrac.yaml keeps the optional dependencies apart. Doctrine is used only by its integration (Driver/Doctrine,
Chart/Sql, the Doctrine dashboard storage), and Symfony Security only by SecurityActor and its UserIdMapper.
The rest of the bundle must work without either.
License
MIT, see LICENSE.