Download the PHP package survos/field-bundle without Composer
On this page you can find all versions of the php package survos/field-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download survos/field-bundle
More information about survos/field-bundle
Files in survos/field-bundle
Package field-bundle
Short Description Universal field/property metadata attributes for search, grid, and AI — #[Field], Widget enum, and FieldReader.
License MIT
Informations about the package field-bundle
survos/field-bundle
Universal field/property metadata for Symfony — declare once, consume everywhere.
The problem
Property metadata is scattered across attributes with overlapping concerns:
| Attribute | Owner | Covers |
|---|---|---|
#[ApiProperty] |
api-platform | OpenAPI description, example |
#[With] |
symfony/ai | JSON Schema constraints for LLMs |
#[ORM\Column] |
doctrine | Storage type |
#[ApiFilter] |
api-platform | Server-side filter declaration |
None of them answer: how should this property be displayed and filtered in a grid, search panel, or UX-search widget?
The solution
#[Field] declares display and search behavior once, orthogonally to the other attributes:
Attribute lanes — no overlap
Installation
Attributes
The bundle provides five PHP attributes covering properties, entities, and controllers.
#[Field] — property / method level
Widget inference — when widget is null, FieldDescriptor::resolvedWidget() infers from PHP type:
| PHP type | Inferred widget |
|---|---|
bool |
Widget::Boolean |
int, float |
Widget::Range |
\DateTimeInterface |
Widget::Date |
| backed enum | Widget::Select |
string |
Widget::Text |
Widget is only inferred when filterable: true. Non-filterable fields return null.
Browsability — Widget::Select and Widget::Boolean are "browsable" (render as selectable lists in ColumnControl / SearchBuilder / facet panels). Widget::Range, Widget::Date, and Widget::Text are filterable but not browsable — they render as input controls.
#[EntityMeta] — class level
Class-level metadata for admin UI, dashboard cards, and menu auto-registration.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
icon |
string | null | UX icon name, e.g. 'mdi:building', 'tabler:user' |
iconClass |
string | null | CSS class for the icon, e.g. 'text-primary' |
order |
int | 100 | Position within the group (lower = first) |
group |
string | 'General' |
Section/submenu header in admin nav |
label |
string | null | Human-readable label; defaults to short class name |
description |
string | null | One-line description for dashboard cards |
adminBrowsable |
bool | true | Include in admin navbar and dashboard |
Discovered at compile time by EntityMetaPass, which scans all Doctrine entity directories.
Twig globals — every #[EntityMeta] entity is exposed as a Twig global keyed by APP_ENTITY_{SHORTNAME} (upper-snake of short class name). Use this to avoid class strings in templates:
#[RouteIdentity] — class level
Declares how an entity identifies itself in URLs. This is fundamental to Survos navigation:
entities generate their own route parameters with getRp(), controllers resolve typed entity
arguments from those same parameters, and templates link with path('route_name', entity.rp).
This replaces the legacy UNIQUE_PARAMETERS const pattern from survos/core-bundle and avoids
repeating #[MapEntity] mappings on every controller method.
Parameters:
| Parameter | Type | Description |
|---|---|---|
field |
string | Property name or getter to read (e.g. 'code' → $entity->code or $entity->getCode()) |
parents |
string[] | Property names of associations to walk for parent route params |
key |
string | Override the URL parameter key (defaults to {lcfirst(ShortName)}Id) |
RouteIdentityTrait implements getRp(), getUniqueIdentifiers(), and erp() for the entity. Pair with implements RouteParametersInterface from survos/core-bundle.
Navigation Contract
For every navigable Doctrine entity, use this pattern:
Then name the route parameter after the generated key. The default key is
{lcfirst(shortClassName)}Id, so Image becomes imageId, Item becomes itemId,
and GalleryImage becomes galleryImageId.
Templates should not rebuild route parameters manually:
For a custom route key, declare it on the entity and use that key in the route:
The entity is the single source of truth for route identity:
entity.rpgenerates URL parameters.RouteIdentityValueResolverresolves controller arguments.- Menus, labels, redirects, and templates all use the same contract.
- If the route parameter name does not match the identity key, typed entity resolution will not run.
Migration from old pattern:
#[RouteMeta] — method level
Metadata for individual controller actions. Powers sitemap generation, AI introspection, breadcrumbs, nav, and OpenAPI projection.
Key parameters:
| Parameter | Type | Description |
|---|---|---|
description |
string | Required. Dev-facing English prose. Used for AI, OpenAPI, dashboards. |
entity |
class-string | Primary entity this route operates on |
purpose |
Purpose | What the route does (List, Show, New, Edit, Delete, Export, Custom) |
audience |
Audience | Who it's for (Public, Authenticated, Admin, Api, Internal) |
sitemap |
bool | Include in sitemap.xml (defaults to true for Public routes) |
changefreq |
string | sitemap <changefreq>: always|daily|weekly|monthly|… |
priority |
float | sitemap <priority>: 0.0–1.0 |
tags |
string[] | Free-form labels: ['admin', 'export', 'beta', …] |
parents |
string[] | Route names for breadcrumb parents |
#[ControllerMeta] — class level
Class-level defaults for #[RouteMeta]. Avoids repeating entity:, audience:, and tags: on every action.
RouteMetaPass merges class-level ControllerMeta defaults under each method's #[RouteMeta]. The method always wins for any field it sets explicitly; ControllerMeta fills the gaps.
FieldReader — reading descriptors at runtime
FieldReader is the main service for consuming #[Field] metadata programmatically. Inject it anywhere:
FieldDescriptor properties
| Property | Type | Source |
|---|---|---|
name |
string | Property/method name |
type |
string | PHP type (e.g. 'string', 'int', 'App\Enum\Status') |
transKey |
?string | #[Field(transKey:)] or null |
description |
?string | #[With], #[ApiProperty], or null |
example |
mixed | #[With], #[ApiProperty], or null |
searchable |
bool | #[Field] or #[ApiFilter(SearchFilter)] |
sortable |
bool | #[Field] or #[ApiFilter(OrderFilter)] |
filterable |
bool | #[Field] or #[ApiFilter] |
widget |
?Widget | #[Field(widget:)] or inferred |
facet |
bool | #[Field(facet:)] |
visible |
bool | #[Field(visible:)] |
order |
int | #[Field(order:)] |
width |
?string | #[Field(width:)] |
format |
?string | #[Field(format:)] |
enum |
scalar[] | Backed enum cases, or #[With(enum:)] |
minimum |
int|float | #[With(minimum:)] or #[Range] constraint |
maximum |
int|float | #[With(maximum:)] or #[Range] constraint |
maxLength |
?int | #[Length(max:)] constraint |
pattern |
?string | #[Regex(pattern:)] constraint |
required |
bool | #[NotBlank] constraint |
isUrl |
bool | #[Url] constraint |
isEmail |
bool | #[Email] constraint |
Key methods:
Progressive enhancement sources
FieldReader enriches descriptors when optional packages are present:
| Source | Package | What it adds |
|---|---|---|
#[Field] |
(this bundle) | All display/search settings |
| Symfony validation | symfony/validator |
required, isUrl, isEmail, minimum, maximum, maxLength, pattern |
#[With] |
symfony/ai-platform |
description, example, enum, minimum, maximum |
#[ApiProperty] |
api-platform/core |
description, example |
#[ApiFilter] on class |
api-platform/core |
searchable, sortable, filterable (fallback when no #[Field]) |
#[MeiliIndex] on class |
survos/meili-bundle |
searchable, sortable, filterable (synthesized fallback) |
| PHP reflection | (always) | type, backed enum cases |
Fallback synthesis — properties with no #[Field] but referenced in #[ApiFilter] or #[MeiliIndex] get a synthesized descriptor so the grid still shows them correctly. Add #[Field] to take explicit control.
Widget mapping across consumers
Widget |
ColumnControl (api-grid) | Meilisearch (meili-bundle) | UX-Search |
|---|---|---|---|
Text |
search input |
searchable | SearchBox |
Select |
searchList dropdown |
RefinementList | RefinementList |
Range |
Min/Max number inputs | RangeSlider | RangeSlider |
Date |
(future) | NumericMenu | DateRangePicker |
Boolean |
searchList dropdown |
Toggle | ToggleRefinement |
Zero required dependencies
#[Field] and Widget have no external dependencies — just PHP 8.4. FieldReader enhances output progressively based on what packages are installed.
Consumers
| Bundle | What it uses |
|---|---|
survos/api-grid-bundle |
FieldReader::getDescriptors() → column sortable/searchable/browsable/width/widget |
survos/grid-bundle |
DataTables column config |
survos/meili-bundle |
Meilisearch searchable/filterable/sortable/facet index settings |
survos/inspection-bundle |
Unified FieldDescriptor DTO for Twig templates and admin tooling |
Further reading
docs/CONTROLLERS.md— Survos controller naming convention:XxxController(entity) vsXxxListController(collection). Covers#[RouteMeta],#[ControllerMeta],#[RouteIdentity], entity injection, and testing patterns.
All versions of field-bundle with dependencies
survos/atlas-bundle Version ^2.5
symfony/config Version ^8.1
symfony/dependency-injection Version ^8.1
symfony/http-kernel Version ^8.1
symfony/string Version ^8.1
survos/kit-bundle Version ^2.5