Skip to content

Addon Interfaces

Filament Twist provides several interfaces that addons can implement to extend functionality and integrate with the system.

HasMigration

Enable your addon to provide its own database migrations.

Interface Definition

php
<?php

namespace Twist\Contracts;

interface HasMigration
{
    public function pathMigrations(): string;
}

Implementation

php
<?php

namespace App\Addons\UserManagement;

use Twist\Base\BaseAddon;
use Twist\Contracts\HasMigration;

class UserManagementAddon extends BaseAddon implements HasMigration
{
    public function pathMigrations(): string
    {
        return __DIR__ . '/Migrations';
    }
}

Usage

When you implement this interface:

  • The twist:migrate command will discover and run your addon's migrations
  • Migrations are automatically included in tenant migration processes
  • Your migration path is added to Laravel's migration system

Migration Structure

Organize your migrations in the specified directory:

app/Addons/UserManagement/
├── Migrations/
│   ├── 2024_01_01_000000_create_users_table.php
│   ├── 2024_01_02_000000_create_roles_table.php
│   └── 2024_01_03_000000_add_permissions_to_roles.php
└── UserManagementAddon.php

HasRouteApi

Enable your addon to register API routes.

Interface Definition

php
<?php

namespace Twist\Contracts;

interface HasRouteApi
{
    public function pathRouteApi(): string;
}

Implementation

php
<?php

namespace App\Addons\UserManagement;

use Twist\Base\BaseAddon;
use Twist\Contracts\HasRouteApi;

class UserManagementAddon extends BaseAddon implements HasRouteApi
{
    public function pathRouteApi(): string
    {
        return __DIR__ . '/Routes/api.php';
    }
}

Usage

When you implement this interface:

  • Your API routes are automatically registered
  • Routes are included in the panel's API routing system
  • Routes respect panel-specific middleware and configuration

Route File Structure

Create your API routes file:

php
<?php
// app/Addons/UserManagement/Routes/api.php

use Illuminate\Support\Facades\Route;
use App\Addons\UserManagement\Controllers\UserApiController;

Route::prefix('users')->group(function () {
    Route::get('/', [UserApiController::class, 'index']);
    Route::post('/', [UserApiController::class, 'store']);
    Route::get('/{user}', [UserApiController::class, 'show']);
    Route::put('/{user}', [UserApiController::class, 'update']);
    Route::delete('/{user}', [UserApiController::class, 'destroy']);
});

HasDispatcher

Integrate with the Laravel Executor package for background processing.

Interface Definition

php
<?php

namespace Twist\Contracts;

interface HasDispatcher
{
    public function pathDispatchers(): string;
}

Implementation

php
<?php

namespace App\Addons\UserManagement;

use Twist\Base\BaseAddon;
use Twist\Contracts\HasDispatcher;

class UserManagementAddon extends BaseAddon implements HasDispatcher
{
    public function pathDispatchers(): string
    {
        return __DIR__ . '/Dispatchers';
    }
}

Usage

When you implement this interface:

  • Your dispatchers are automatically registered with the Executor system
  • Background processing capabilities are enabled for your addon
  • Dispatchers can be triggered via commands or events

Dispatcher Structure

Create dispatchers in the specified directory:

app/Addons/UserManagement/
├── Dispatchers/
│   ├── SendWelcomeEmailDispatcher.php
│   ├── ProcessUserImportDispatcher.php
│   └── CleanupInactiveUsersDispatcher.php
└── UserManagementAddon.php

Example dispatcher:

php
<?php

namespace App\Addons\UserManagement\Dispatchers;

use Pharaonic\Laravel\Executor\Dispatcher;

class SendWelcomeEmailDispatcher extends Dispatcher
{
    public function handle($userId)
    {
        $user = User::find($userId);
        // Send welcome email logic
    }
}

HasHooks

Register hooks in the application lifecycle.

Interface Definition

php
<?php

namespace Twist\Contracts;

interface HasHooks
{
    public function hooks(): void;
}

Implementation

php
<?php

namespace App\Addons\UserManagement;

use Twist\Base\BaseAddon;
use Twist\Contracts\HasHooks;

class UserManagementAddon extends BaseAddon implements HasHooks
{
    public function hooks(): void
    {
        // Register your hooks here
        add_action('user.created', [$this, 'onUserCreated']);
        add_action('user.updated', [$this, 'onUserUpdated']);
        add_filter('user.permissions', [$this, 'filterPermissions']);
    }

    public function onUserCreated($user)
    {
        // Handle user creation
        logger("User created: {$user->email}");
    }

    public function onUserUpdated($user)
    {
        // Handle user updates
        cache()->forget("user.{$user->id}");
    }

    public function filterPermissions($permissions, $user)
    {
        // Modify permissions based on addon logic
        return $permissions;
    }
}

Usage

When you implement this interface:

  • Your hooks are automatically registered during application boot
  • You can respond to application events and modify behavior
  • Hooks provide extensibility points for other addons

Multiple Interface Implementation

You can implement multiple interfaces in a single addon:

php
<?php

namespace App\Addons\UserManagement;

use Twist\Base\BaseAddon;
use Twist\Contracts\HasMigration;
use Twist\Contracts\HasRouteApi;
use Twist\Contracts\HasDispatcher;
use Twist\Contracts\HasHooks;
use Filament\Panel;

class UserManagementAddon extends BaseAddon implements 
    HasMigration, 
    HasRouteApi, 
    HasDispatcher, 
    HasHooks
{
    public function getId(): string
    {
        return 'user-management';
    }

    public function boot(Panel $panel): void
    {
        $panel->resources([
            Resources\UserResource::class,
            Resources\RoleResource::class,
        ]);

        $panel->pages([
            Pages\UserDashboard::class,
        ]);
    }

    public function pathMigrations(): string
    {
        return __DIR__ . '/Migrations';
    }

    public function pathRouteApi(): string
    {
        return __DIR__ . '/Routes/api.php';
    }

    public function pathDispatchers(): string
    {
        return __DIR__ . '/Dispatchers';
    }

    public function hooks(): void
    {
        add_action('user.created', [$this, 'onUserCreated']);
        add_action('user.deleted', [$this, 'onUserDeleted']);
    }

    public function onUserCreated($user)
    {
        // Trigger welcome email dispatcher
        execute('SendWelcomeEmailDispatcher', $user->id);
    }

    public function onUserDeleted($user)
    {
        // Cleanup user data
        execute('CleanupUserDataDispatcher', $user->id);
    }
}

Interface Concerns (Traits)

Twist provides helper traits for common interface implementations:

InteractsWithMigration

php
<?php

namespace Twist\Concerns;

trait InteractsWithMigration
{
    public function pathMigrations(): string
    {
        return $this->pathMigrations;
    }
}

Usage:

php
<?php

namespace App\Addons\UserManagement;

use Twist\Base\BaseAddon;
use Twist\Contracts\HasMigration;
use Twist\Concerns\InteractsWithMigration;

class UserManagementAddon extends BaseAddon implements HasMigration
{
    use InteractsWithMigration;

    protected string $pathMigrations = __DIR__ . '/Migrations';
}

InteractsWithRouteApi

php
<?php

namespace Twist\Concerns;

trait InteractsWithRouteApi
{
    public function pathRouteApi(): string
    {
        return $this->pathRouteApi;
    }
}

InteractsWithDispatcher

php
<?php

namespace Twist\Concerns;

trait InteractsWithDispatcher
{
    public function pathDispatchers(): string
    {
        return $this->pathDispatchers;
    }
}

Best Practices

  1. Single Responsibility: Implement only the interfaces your addon actually needs
  2. Error Handling: Ensure your interface methods handle errors gracefully
  3. Documentation: Document the purpose and behavior of your implementations
  4. Testing: Test each interface implementation thoroughly
  5. Performance: Consider the performance impact of hooks and dispatchers

Example: Complete Addon

Here's a complete example of an addon implementing all interfaces:

php
<?php

namespace App\Addons\BlogManagement;

use Twist\Base\BaseAddon;
use Twist\Contracts\{HasMigration, HasRouteApi, HasDispatcher, HasHooks};
use Filament\Panel;

class BlogManagementAddon extends BaseAddon implements 
    HasMigration, 
    HasRouteApi, 
    HasDispatcher, 
    HasHooks
{
    public function getId(): string
    {
        return 'blog-management';
    }

    public function boot(Panel $panel): void
    {
        $panel->resources([
            Resources\PostResource::class,
            Resources\CategoryResource::class,
        ]);
    }

    // HasMigration implementation
    public function pathMigrations(): string
    {
        return __DIR__ . '/Migrations';
    }

    // HasRouteApi implementation
    public function pathRouteApi(): string
    {
        return __DIR__ . '/Routes/api.php';
    }

    // HasDispatcher implementation
    public function pathDispatchers(): string
    {
        return __DIR__ . '/Dispatchers';
    }

    // HasHooks implementation
    public function hooks(): void
    {
        add_action('post.published', [$this, 'onPostPublished']);
        add_filter('post.content', [$this, 'filterPostContent']);
    }

    public function onPostPublished($post)
    {
        execute('NotifySubscribersDispatcher', $post->id);
    }

    public function filterPostContent($content, $post)
    {
        // Add reading time estimation
        $readingTime = ceil(str_word_count($content) / 200);
        return $content . "\n\n*Estimated reading time: {$readingTime} minutes*";
    }
}

Next Steps

Released under the MIT License.