Welcome, AI assistant. Please follow these guidelines when contributing to this repository.
EasyAdminBundle is a third-party Symfony bundle for creating admin backends. It provides CRUD controllers, dashboard management, and extensive field/filter configuration.
Requirements: PHP 8.2+, Symfony 6.4/7.x/8.x, Doctrine ORM 2.20+ or 3.6+
- Language: American English for code, comments, commits, branches
- Code quotes: wrap strings with single quotes in PHP, CSS, JavaScript
- Text quotes: straight quotes only (
'and", no typographic) - Security: prevent XSS, CSRF, injections, auth bypass, open redirects
vendor/- managed by Composernode_modules/- managed by Yarnvar/- Symfony cache/logspublic/bundles/- generated assetscomposer.lock,yarn.lock- update only via package manager commands
- Factory: ActionFactory, EntityFactory, FieldFactory, FilterFactory, FormFactory
- Configurator Chain: FieldConfiguratorInterface, FilterConfiguratorInterface
- Context Facade: AdminContext wraps Request/Crud/Dashboard/I18n contexts
- DTO Layer: Type-safe data transfer (ActionDto, EntityDto, FieldDto, etc.)
- Event Subscriber: AdminRouterSubscriber, CrudAutocompleteSubscriber
- Registry: AdminControllerRegistry, TemplateRegistry
- Provider: AdminContextProvider, FieldProvider
- Typed Collections: FieldCollection, ActionCollection, FilterCollection
- Argument Resolver: AdminContextResolver, BatchActionDtoResolver
Don't modify the public API in Contracts/ lightly — it's a stable, versioned interface that third-party code depends on.
- Never surface internal complexity to end users. Keep the public API small, simple, and consistent; push complexity down into internal classes (factories, configurators, DTOs), not onto the people configuring a backend.
- Public APIs are stable contracts: prefer additive, backward-compatible changes and don't leak internal types or implementation details through them.
Follow Symfony's BC policy: never break public API — deprecate first, keep the old path working, remove only in the next major. Emit deprecations with @trigger_deprecation('easycorp/easyadmin-bundle', '<version>', '<message>'), naming the replacement and removal version.
User-facing config uses fluent builders (Config/, Field/): static new() + chainable setters returning self. Each builder converts via getAsDto() into an immutable DTO (Dto/) for internal use. New configurable concepts follow this split — fluent builder for users, DTO internally; never expose a DTO as the config surface.
EasyCorp\Bundle\EasyAdminBundle\
The Makefile is the source of truth for build/lint/test commands; its targets match what CI runs. Prefer them over hand-typed invocations. Run make help to list every target.
make build # composer update + yarn install (first-time setup)
composer install # PHP dependencies only
yarn install # JS dependencies onlyRun the full check suite (linters + tests, identical to CI):
make checks-before-prRun the relevant target(s) for what you changed:
| Changed | Run |
|---|---|
| PHP code | make linter-phpstan, make linter-cs-fixer, make tests |
| JS / CSS | yarn ci, then make build-assets |
| Twig templates | make linter-twig |
| Documentation | make linter-docs |
| Translations | keep all locales consistent; use English as placeholder if unsure |
Run targeted tests with make tests ARGS=... (keeps the deprecation baseline and cache reset; full guide in tests/AGENTS.md):
make tests # whole suite
make tests ARGS="tests/Unit/Field/" # one directory
make tests ARGS="--filter=testFoo" # one testA bare ./vendor/bin/simple-phpunit run skips the deprecation baseline (tests/baseline-ignore.txt) and the cache reset.
- Use imperative mood: "Add feature" not "Added feature"
- First line: concise summary (50 chars max)
- Reference issues when applicable: "Fix #123"
- No period at end of subject line
- Feature:
<short description>(e.g.,add_new_field_type) - Bug fix:
fix_<issue number>(e.g.,fix_123) - Use lowercase with underscores
php-cs-fixer (@Symfony + @Symfony:risky) auto-formats most style — trailing commas, braces, strict comparisons, Yoda conditions, blank lines, quotes, etc. Run make linter-cs-fixer and let it handle formatting. The rules below are the conventions it does not enforce.
- PHP 8.2+ syntax with constructor property promotion
- Don't add
declare(strict_types=1);to PHP files - No service autowiring — configure explicitly in
config/services.php - Use enums (
UpperCamelCasecase names) instead of constants for fixed sets of values - Prefer project constants (
Action::EDIT,EA::QUERY) over hardcoded strings - Avoid
else/elseifafterreturn/throw— return/throw early instead return null;for nullable,return;for void- Use
sprintf()for exception messages withget_debug_type()for class names - Exception/error messages: start capital, end with a period, no backticks; concise but actionable (include class names, file paths)
- Handle exceptions explicitly (no silent catches)
- Comments: only for complex/unintuitive code, lowercase start, no period end; never as section separators (e.g.
// === Methods for dashboards ===) - Config files in PHP format (
config/services.php,translations/*.php)
- Variables/methods:
camelCase - Config/routes/Twig:
snake_case - Constants:
SCREAMING_SNAKE_CASE - Classes:
UpperCamelCase - Abstract classes:
Abstract*(except test cases) - Interfaces:
*Interface, Traits:*Trait, Exceptions:*Exception - Most classes add a suffix showing their type:
*Controller,*Configurator,*Context,*Dto,*Event,*Field,*Filter,*Subscriber,*Type,*Test - Templates/assets:
snake_case(e.g.,detail_page.html.twig)
- Properties before methods
- Constructor first, then public, protected, private methods in that order
- No single-line docblocks
- No
@returnfor void methods - Group annotations by type
twig-cs-fixer handles formatting (make linter-twig). Conventions it doesn't enforce:
- Modern HTML5 and Twig syntax
- Icons: FontAwesome 6.x names
- All user-facing text via
|transfilter (no hardcoded strings) - Keep translation logic in templates, not PHP (use
TranslatableInterface) - Reuse components from
templates/components/when available — seesrc/Twig/Component/AGENTS.mdbefore adding or changing a component - Accessibility:
aria-*attributes, semantic tags, labels
biome handles formatting (yarn ci). Conventions it doesn't enforce:
- ES6+ syntax
camelCasefor variables and functions
- Standard CSS only (no SCSS/LESS)
- Don't use nested rules — keep selectors flat
kebab-casefor class names- Bootstrap 5.3 classes and utilities
- Logical properties:
margin-block-endinstead ofmargin-bottom - Responsive design required; use only these Bootstrap breakpoints:
- Medium (md): ≥768px
- Large (lg): ≥992px
- Extra large (xl): ≥1200px
- Format: reStructuredText (.rst)
- Heading symbols:
=,-,~,.,"for levels 1-5 - Line length: 72-78 characters
- Code blocks: prefer
::over.. code-block:: php - Separate link text from URLs (no inline hyperlinks)
- Show config in order: YAML, XML, PHP (or Attributes)
- Code line limit: 85 chars (use
...for folded code) - Include
usestatements for referenced classes - Bash lines prefixed with
$ - Root directory:
your-project/ - Vendor name:
Acme - URLs:
example.com,example.org,example.net - Trailing slashes for directories, leading dots for extensions
- American English, second person (you)
- Gender-neutral (they/them)
- Use contractions (it's, don't, you're)
- Avoid: "just", "obviously", "easy", "simply"
- Realistic examples (no foo/bar placeholders)
- Write for non-native English speakers: use simple vocabulary, avoid idioms, and complex sentence structures