Reference
Upgrading
Migration guidance for provider-first setup, typed schema nodes, and the evolving beta API.
Before upgrading
- read
CHANGELOG.md - compare the contracts reference
- confirm whether
schema_versionchanged
Upgrade checklist
- Review whether your host app should move from explicit registration to provider-based
->discover(). - Keep explicit
resource()andpage()registration only for overrides or non-standard directories. - Review whether legacy resource arrays should move to the typed resource DSL, including purpose-built form fields for relations, dates, uploads, rich text, and passwords.
- Re-run host-app validation from
examples/host-app/README.md. - Verify custom extensions against new contracts.
- Re-test protected panel routes and JSON payload consumers.
Discovery coexistence
Older setups may still use config/flashboard.php discovery arrays:
<?php
'discovery' => [
'resources' => [
App\Flashboard\UsersResource::class,
],
'pages' => [
App\Flashboard\ReviewQueuePage::class,
],
],
This remains supported as a fallback and compatibility layer.
The new primary DX is provider-first configuration:
<?php
$this->panelConfig()
->path('panel')
->discover();
Coexistence rules:
- fallback config arrays are still read
- inline
Flashboard::configure()values are merged as a compatibility layer - provider configuration is merged last and wins over inline and fallback config
- explicit
resource()andpage()registrations are deduplicated with discovered classes withoutDiscovery()disables only auto-discovery, not explicit or fallback registrations
Provider-first direction
New host applications should prefer a generated panel provider:
php artisan flashboard:make-provider
Use inline Flashboard::configure() only when you need a transitional path in an existing host app.
Typed resource DSL migration
The preferred public API for resources is now the typed DSL:
<?php
use Pepperfm\Flashboard\Contracts\Tables\TableContract;
use Pepperfm\Flashboard\Core\Tables\Columns\BadgeColumn;
use Pepperfm\Flashboard\Core\Tables\Columns\TextColumn;
public static function table(TableContract $table): TableContract
{
return $table->columns([
TextColumn::make('id', 'ID')->sortable(),
BadgeColumn::make('status', 'Status')->searchable(),
]);
}
Legacy arrays still work:
<?php
use Pepperfm\Flashboard\Contracts\Tables\TableContract;
public static function table(TableContract $table): TableContract
{
return $table->columns([
['key' => 'id', 'label' => 'ID', 'sortable' => true],
['key' => 'status', 'label' => 'Status', 'searchable' => true],
]);
}
Migration rules:
- typed schema nodes are the recommended authoring style for new resources
- keyed typed schema nodes accept an optional label as the second
make()argument; preferTextInput::make('name', 'Name')overTextInput::make('name')->label('Name')for static labels - legacy arrays remain valid as compatibility input
- runtime payloads are normalized from both styles into the same schema contract
- prefer purpose-built form fields over renderer overrides for relations, dates, uploads, rich text, and passwords
- replace one-record FK selects with
BelongsTo::make('related_id', 'Related')when the model has an EloquentbelongsTorelation; pass the relationship as the thirdmake()argument or callrelationship()when the FK name does not match the relation method - replace form-level many-to-many arrays with
BelongsToMany::make('tags', 'Tags')when the model has an EloquentbelongsToManyrelation; pass the relationship as the thirdmake()argument or callrelationship()when the field key does not match the relation method - replace inverse read-only relation badges with
HasOne::make('profile', 'Profile')orHasMany::make('items', 'Items')when operators should manage related records from the parent resource; enable mutation modes explicitly withattachable(),detachable(),replaceable(), orsyncable() - move one-off eager loading or option scoping for relation fields/managers to
modifyQueryUsing(fn (Builder $query): Builder => ...),modifyRecordsQueryUsing(...), ormodifyAttachOptionsQueryUsing(...); every callback must return the modified EloquentBuilder - keep legacy
RelationDefinition::make(...)only for read-only compatibility payloads; new relation-manager UIs expecttype=has_oneortype=has_manyplus protected nested route URLs - review nullable FK behavior before enabling
detachable(),replaceable(), orsyncable()because inverse managers clear or move related records but never delete them - JSON/form consumers should handle
relation_selectandrelation_multi_selectrenderers and their lazy option payload keys before relying on rawselectbehavior for related records relation_multi_selectedit payloads exposeselected_options, submit arrays of scalar related keys, and sync pivot membership only; pivot attributes and related-record CRUD still belong in custom flows or inverse managers- if an edit form previously hydrated password or file columns as text, migrate those fields to
PasswordInputorFileUpload; edit state now intentionally keeps those values empty and exposes only safe file metadata detail()remains supported andinfolist()is the concept-aligned alias for new resource classesphp artisan flashboard:make-resourcegenerates typed definitions and resource-level edit/delete row actions by default
Breaking-change classes
- contract rename
- route naming change
- payload shape change
- policy mapping behavior change
- builder API signature change