Skip to content

Schema Hook Extenders

Use schema extenders when a package needs to add fields, tabs, sidebar components, or relation managers to a first-party admin resource without copying the resource schema.

Prefer the abstract base classes when you only need one hook:

  • Capell\Admin\Support\Schemas\AbstractPageSchemaExtender
  • Capell\Admin\Support\Schemas\AbstractSiteSchemaExtender
  • Capell\Admin\Support\Schemas\AbstractUserSchemaExtender

The base classes return empty components or the original array for hooks you do not override. If you implement an extender interface directly, you must implement every method on that interface.

Tag extenders from the package admin provider:

use Capell\Admin\Contracts\Extenders\PageSchemaExtender;
public function boot(): void
{
$this->app->tag([ExamplePageSchemaExtender::class], PageSchemaExtender::TAG);
}

When using an admin bridge, register through the bridge registrar:

$registrar->schemaExtender(ExamplePageSchemaExtender::class, PageSchemaExtender::TAG);

If the extender changes a cached admin surface, clear and rebuild the configurator cache:

Terminal window
php artisan capell:admin-clear-cache
php artisan capell:admin-cache-configurators

PageSchemaExtender::TAG targets the page edit resource.

MethodUse it for
extendTranslationComponentsForHook(Schema $schema, PageTranslationSchemaHookEnum $hook)Add fields around translated page title/content/meta fields.
extendSidebarComponents(Schema $schema)Add sidebar fields to the page editor.
extendTabs(Schema $schema, array $tabs)Add or modify top-level edit tabs.
extendRelationManagers(Model $record, array $relationManagers)Add relation managers for package-owned page relationships.

Translation hook values:

HookPosition
BeforeTitleBefore the title field.
AfterTitleAfter the title field.
AfterContentEditorAfter the main content editor.
AfterExtraContentAfter the extra content section.
BeforeSearchMetaBefore search/meta fields.
AfterSearchMetaAfter search/meta fields.

Example:

<?php
declare(strict_types=1);
namespace Vendor\Example\Admin;
use Capell\Admin\Enums\PageTranslationSchemaHookEnum;
use Capell\Admin\Support\Schemas\AbstractPageSchemaExtender;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
final class ExamplePageSchemaExtender extends AbstractPageSchemaExtender
{
public function extendTranslationComponentsForHook(Schema $schema, PageTranslationSchemaHookEnum $hook): array
{
return match ($hook) {
PageTranslationSchemaHookEnum::AfterTitle => [
TextInput::make('example_subtitle')
->label(__('capell-example::fields.subtitle'))
->maxLength(160),
],
default => [],
};
}
}

SiteSchemaExtender::TAG targets the site create/edit resource.

MethodUse it for
extendTranslationComponentsForHook(Schema $schema, PageTranslationSchemaHookEnum $hook)Add translated site fields around title/meta hooks.
extendSiteMetaDetailsComponents(Schema $schema, array $components)Add or position non-translated site meta fields.
extendCreateWizardComponentsForHook(Schema $schema, SiteCreateWizardHookEnum $hook)Add fields to the site creation wizard.
extendTabs(Schema $schema, array $tabs)Add or modify site edit tabs.
extendRelationManagers(Model $record, array $relationManagers)Add relation managers for site-owned package data.

Current site create wizard hooks:

HookPosition
PagesStepEndEnd of the pages step.

LayoutSchemaExtender::TAG targets layout edit pages.

MethodUse it for
extendTabs(Schema $schema, array $tabs)Add or modify layout edit tabs.
extendRelationManagers(Model $record, array $relationManagers)Add relation managers for layout-owned package data.

UserSchemaExtender::TAG targets the Capell user resource. For new packages, prefer a UserResourceBridge when you need user form fields, sidebar panels, or relation managers because bridges can be enabled conditionally per package context.

MethodUse it for
supports(UserSchemaContextData $context)Limit the extender to specific user models or form contexts.
extendComponentsForHook(Schema $schema, UserSchemaHookEnum $hook, UserSchemaContextData $context)Add fields around identity, credentials, roles, profile, or footer hooks.
extendSidebarComponents(Schema $schema, UserSchemaContextData $context)Add user edit sidebar components.
extendRelationManagers(Model $record, array $relationManagers, UserSchemaContextData $context)Add user relation managers.

User hook values:

HookPosition
BeforeIdentity / AfterIdentityAround identity fields.
BeforeCredentials / AfterCredentialsAround credential fields.
BeforeRoles / AfterRolesAround role and permission fields.
BeforeProfile / AfterProfileAround profile fields.
FooterEnd of the form.

Schema hooks are not the right extension point for every admin change.

NeedUse
Page/site/resource header actionPageHeaderActionExtender, SiteHeaderActionExtender, or ResourceHeaderActionExtender.
Page title/slug field action or after-label schemaPageTitleWithSlugInputExtender.
Page table columns, filters, bulk actions, or query changesPageTableExtender.
User table columns, filters, record actions, or toolbar actionsUserTableExtender.
Page edit form actions or header widgetsPageEditExtender.
Publish panel HTMLPublishPanelExtender.
Page/site export modal fields and optionsPageExportExtender.
Media edit header actionsMediaEditActionExtender.
Extensions page contentExtensionsPageExtender or ExtensionsPageActionRegistry.
Filament panel configurationAdminPanelExtender.

See Admin extensions for the broader package authoring map.

If a schema contribution is missing:

  1. Confirm the package admin provider is loaded.
  2. Confirm the extender is tagged with the matching ::TAG constant.
  3. If registered through a bridge, confirm CapellAdmin::bootAdminBridges($packageName) runs for the same package name.
  4. Run php artisan optimize:clear.
  5. Run php artisan capell:admin-clear-cache and php artisan capell:admin-cache-configurators.
  6. Check the exact target surface. Page, site, layout, user, title/slug, table, and header extenders use different tags.

Add a focused test for the resolver when you add an extender. The test should tag the extender, resolve the matching resolver, and assert that the expected component/action/relation manager is returned for the specific hook.