smartlabs/sonata-inline-relation-bundle
Smartlabs Sonata Inline Relation Bundle adds inline relation management to Sonata Admin, letting editors create and edit related entities directly within parent forms for faster, smoother admin workflows.
Prerequisites matter here before anything else: despite the task framing, this is a Symfony bundle for Sonata Admin, not a Laravel package. There is no service provider auto-discovery, no config/ publish step in the Laravel sense. Laravel developers will need to translate concepts: bundles are registered in config/bundles.php, configuration lives in config/packages/sonata_inline_relation.yaml, and dependency injection replaces facades.
Minimum steps:
composer require smartlabs/sonata-inline-relation-bundle (PHP ^8.2).config/bundles.php.sonata_inline_relation root key (new in this release). Defaults for max_search_results, sortable, add_allowed, remove_allowed live here.Admin class, add a form field of the bundle's inline-relation type. Two options are now required: relation_field and owner_field. relation_field may be explicitly null for direct entity mapping.number field, notes via text, a status via select).Where to look first:
relation_field: null vs a property path) are the single most confusing part.ConfigurationTest — read it as living documentation of valid bundle-level config keys and their defaults.Field mapping. Always set relation_field and owner_field explicitly. Use relation_field: null when the pivot entity maps directly to the related entity (no join entity in between); use a property path when a join entity carries the pivot data. Getting this wrong is the most common source of empty grids.
Pivot field types. Use the new text, number (with min/max/step), and select (with choices) types instead of hand-rolled form fields. For radio-type pivot fields, sorting is now generic — any radio field sorts true entries first, so you no longer need the historically hardcoded field name. Set default per pivot field so newly added rows arrive pre-populated.
Search tuning. Set max_search_results at either the field level (per-relation override) or the bundle level (global default). For complex search needs, provide a search_query_builder callable that returns a QueryBuilder — use this for tenant scoping, soft-delete filters, or joins the default builder can't express.
Events over hacks. Listen to InlineRelationAddEvent, InlineRelationRemoveEvent, InlineRelationUpdateEvent, and InlineRelationReorderEvent (all implement InlineRelationEventInterface) for side effects: audit logs, cache invalidation, denormalized counter updates, notifications. This replaces the old pattern of subclassing the controller or stuffing logic into the entity.
Display and UX. Use label_property to show a friendlier label than __toString() (issue #29). Use editable_pivot_fields to render specific pivot controls read-only (#37). Set confirm_remove: false to skip the confirmation dialog entirely (#34). Enforce min_relations/max_relations — the badges display automatically.
i18n. Override the shipped XLIFF files (EN and DE) rather than editing vendor code. All JS strings now come from translated data-* attributes, so translating the XLIFF is sufficient — no JS patching required (a big improvement over prior versions).
Example app as a template. Copy the example app's bundle config and Admin class into a scratch project when adding a new relation type; it is kept in lockstep with the release.
relation_field and owner_field have no implicit defaults. A missing relation_field is a config error, not a silent fallback. Validate early — a failing ConfigurationTest-style boot error is much cheaper than debugging an empty grid.relation_field: null is meaningful, not "unset". Omitting the key and setting it to null are different intents in the docs. Be explicit; use null only for direct entity mapping.sonata_inline_relation. Bundle-level defaults only apply when the field doesn't override them. Check precedence: field-level max_search_results > bundle-level > internal default. The same precedence applies to sortable, add_allowed, and remove_allowed.window.confirm(). If you have JS that patched confirm() or listened for the browser dialog, it will silently stop working. Ensure Bootstrap's JS bundle is loaded in the admin layout.DOMContentLoaded may run too early. Use the bundle's documented init hook or MutationObserver-style waits — do not assume the widget exists at document-ready.pivotField whitelist (#39). Requests specifying a pivotField not in the allowed set are rejected. If you add a custom or derived pivot field, register it in the field configuration; otherwise add/update/reorder calls will 400 and the console/network tab is where you'll see it.editable_pivot_fields read-only rendering. Confirm that read-only controls still submit their values (or are correctly excluded) on update — the visual change does not imply the same POST payload across versions. Test the reorder-then-update flow specifically.min_relations/max_relations. These are UX guards with visual badges, not necessarily DB constraints. If min > max is misconfigured, the UI can leave you unable to remove down to the minimum or add up to maximum — add server-side validation of your own if data integrity matters.kernel.terminate/queued work for heavy tasks.ConfigurationTest expectations) → XLIFF translations → frontend JS (deferred Select2 and Bootstrap modal wiring). Most "nothing loads" reports resolve at step 2 or 3, not in the JS.search_query_builder for queries, event listeners for side effects, label_property for labels, XLIFF overrides for strings. Avoid subclassing the bundle's controller; the controller now enforces a whitelist and its internals are the most likely thing to change in the next patch.How can I help you explore Laravel packages today?