Build plugins
plugins/PackageName/
├── plugin.json
├── PackageNamePlugin.php
├── Http/{Controllers,Requests}/
├── Models/
├── Policies/
├── database/migrations/
├── resources/views/{dashboard,admin,site,widgets}/
└── routes/web.php
Composer maps Awn\Plugins\ to plugins/.
interface SitePlugin
{
public function key(): PluginKey;
public function settingsRules(): array;
public function normalizeSettings(array $settings): array;
public function render(
PluginSlot $slot,
PluginRenderContext $context,
array $settings,
): ?View;
}
Optional contracts are:
ContributesDashboardandContributesDashboardWidgetsfor one organization's admin.ContributesSuperAdminPagesandContributesSuperAdminWidgetsfor global platform operators.ContributesPagesandSitePlugin::render()for the public site and active theme.SeedsPluginDatafor install-time package seeders.
None is mandatory. A storage-only plugin may implement no UI contribution; a theme add-on may render one slot; a business package may use every surface.
Registration
Add one PluginKey case plus driver/directory arms, matching manifest, driver, existing routes/web.php, first-party views, and package data assets. All plugin controller routes must use plugin.{slug}..
Keep settings and management URLs separate. /charity/{organization}/plugins/{slug} belongs to the platform settings controller; package dashboards should use /plugins/{slug}/manage/.... PluginDefinitionValidator rejects contributed dashboards that collide with the settings URL.
Match route availability to each contribution: requiresEnabled: true uses plugin.available:{slug}; requiresEnabled: false uses plugin.available:{slug},installed. Validation rejects mismatches.
Tenant tables need ownership FK and tenant-leading constraints; models use BelongsToOrganization and guard ownership.
Lifecycle
php artisan awn:plugin:install {slug}
php artisan awn:plugin:uninstall {slug}
php artisan awn:plugin:uninstall {slug} --purge
Install validates definitions, runs package migrations/declared seeders, synchronizes catalog, marks installed/active, and clears affected caches. Normal uninstall globally deactivates and preserves assignments/tables. Purge confirms, deletes all assignments, reverses package migrations, and marks installed/active false; it keeps the catalog row.
Organization configure is separate. Enable validates installation/activity, compatibility, and CMS collisions, then normalizes/stores settings. Disable preserves data and records disabled_at.
Choose the next guide
- Organization dashboard surfaces: tenant management submenu and widgets.
- Super Admin pages and widgets: global read-only or operational platform tools.
- Public pages and theme integration: navigation, dynamic pages, slots, preview, and actions.
- Build a theme: create the public presentation package that renders plugin capabilities.
For a complex plugin, create tenant models and reversible migrations first, then add only the surfaces the product needs. Test cross-tenant IDs, private files, direct authorization, state transitions, preview/publication, global deactivation, and purge.