T
Tenanto
Documentation / Customization

Customization

Updated Jan 25, 2026

Customization Guide

This guide covers how to customize Tenanto for your specific needs.


Branding

Changing the Application Name

  1. Edit .env:

    APP_NAME="Your SaaS Name"
    
  2. Update config/app.php if needed:

    'name' => env('APP_NAME', 'Your SaaS Name'),
    

Changing Panel Branding

Admin Panel:

Edit app/Providers/Filament/AdminPanelProvider.php:

->brandName('Your Admin')
->brandLogo(asset('images/admin-logo.svg'))
->favicon(asset('images/favicon.ico'))
->colors([
    'primary' => Color::Indigo,
    'danger' => Color::Rose,
])

Tenant Panel:

Edit app/Providers/Filament/TenantPanelProvider.php:

->brandName(fn () => app(TenantManagerInterface::class)->current()?->name ?? 'App')
->colors([
    'primary' => Color::Blue,
])

Custom Logo

  1. Add your logo to public/images/
  2. Reference in panel providers:
    ->brandLogo(asset('images/your-logo.svg'))
    

Adding New Models

Step 1: Create Model

docker compose exec app php artisan make:model YourModel -mf

Step 2: Add Tenant Scope

Edit the model:

<?php

namespace App\Models;

use App\Domain\Tenancy\Traits\BelongsToTenant;
use Illuminate\Database\Eloquent\Model;

class YourModel extends Model
{
    use BelongsToTenant;

    protected $fillable = ['name', 'description'];
}

Step 3: Add Migration

Schema::create('your_models', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignId('tenant_id')->constrained()->cascadeOnDelete();
    $table->string('name');
    $table->text('description')->nullable();
    $table->timestamps();
    $table->softDeletes();

    // Important: composite index for tenant queries
    $table->index(['tenant_id', 'created_at']);
});

Step 4: Create Filament Resource

docker compose exec app php artisan make:filament-resource YourModel --generate

Move to tenant panel:

mv app/Filament/Resources/YourModelResource.php app/Filament/Tenant/Resources/

Update namespace:

namespace App\Filament\Tenant\Resources;

Step 5: Create Policy

docker compose exec app php artisan make:policy YourModelPolicy --model=YourModel

Edit to check tenant ownership:

public function view(User $user, YourModel $model): bool
{
    return $user->tenant_id === $model->tenant_id;
}

Adding API Endpoints

Step 1: Create Controller

docker compose exec app php artisan make:controller Api/V1/YourModelController --api

Step 2: Add Routes

Edit routes/api.php:

Route::middleware(['auth:sanctum', 'throttle:api'])->group(function () {
    Route::apiResource('your-models', YourModelController::class);
});

Step 3: Create Resources

docker compose exec app php artisan make:resource YourModelResource
docker compose exec app php artisan make:request StoreYourModelRequest
docker compose exec app php artisan make:request UpdateYourModelRequest

Modifying Billing Plans

Adding a New Plan

  1. Create product/price in Stripe Dashboard

  2. Add to .env:

    STRIPE_PRICE_STARTER=price_xxx
    
  3. Add to config/billing.php:

    'starter' => [
        'name' => 'Starter',
        'stripe_price_id' => env('STRIPE_PRICE_STARTER'),
        'features' => [
            'users' => 3,
            'teams' => 1,
            'projects' => 5,
            'api_access' => false,
            'custom_domain' => false,
            'priority_support' => false,
        ],
    ],
    
  4. Add enum case in app/Domain/Billing/Enums/SubscriptionPlan.php:

    case Starter = 'starter';
    

Changing Plan Features

Edit config/billing.php to modify:


Customizing the Dashboard

Adding Dashboard Widgets

Create a widget:

docker compose exec app php artisan make:filament-widget YourWidget

Move to tenant panel and implement:

<?php

namespace App\Filament\Tenant\Widgets;

use Filament\Widgets\StatsOverviewWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;

class YourWidget extends StatsOverviewWidget
{
    protected function getStats(): array
    {
        return [
            Stat::make('Total Items', YourModel::count())
                ->description('All items')
                ->icon('heroicon-o-cube'),
        ];
    }
}

Register in TenantPanelProvider.php:

->widgets([
    YourWidget::class,
])

Removing the Example Module

The Projects/Tasks module is provided as an example. To remove it:

# Remove migrations (before running migrate)
rm database/migrations/*_create_projects_table.php
rm database/migrations/*_create_tasks_table.php

# Remove domain code
rm -rf app/Domain/ExampleApp

# Remove Filament resources
rm -rf app/Filament/Tenant/Resources/ProjectResource*
rm -rf app/Filament/Tenant/Resources/TaskResource*
rm -rf app/Filament/Tenant/Widgets/*Tasks*
rm -rf app/Filament/Tenant/Widgets/*Project*

# Remove tests
rm -rf tests/Feature/ExampleApp
rm -rf tests/Unit/Domain/ExampleApp

# Remove API
rm -rf app/Http/Controllers/Api/V1/ProjectController.php
rm -rf app/Http/Controllers/Api/V1/TaskController.php
rm -rf app/Http/Resources/ProjectResource.php
rm -rf app/Http/Resources/TaskResource.php

# Edit routes/api.php to remove project/task routes

Adding Translations

Step 1: Add Language Files

Create files in lang/xx/ where xx is the language code:

mkdir -p lang/de
cp lang/en/*.php lang/de/

Step 2: Translate

Edit the files with translations:

// lang/de/auth.php
return [
    'login' => 'Anmelden',
    'logout' => 'Abmelden',
    // ...
];

Step 3: Configure

Add supported locales in .env:

APP_LOCALE=en
APP_AVAILABLE_LOCALES=en,de,fr

See i18n Guide for more details.


Custom Middleware

Creating Middleware

docker compose exec app php artisan make:middleware YourMiddleware

Registering for Tenant Panel

Edit app/Providers/Filament/TenantPanelProvider.php:

->middleware([
    // ... existing middleware
    YourMiddleware::class,
])

Custom Notifications

Creating a Notification

docker compose exec app php artisan make:notification YourNotification

Email Template

public function toMail($notifiable): MailMessage
{
    return (new MailMessage)
        ->subject('Your Subject')
        ->greeting('Hello!')
        ->line('Your notification message.')
        ->action('View', url('/'))
        ->line('Thank you!');
}

Testing Your Customizations

Running Tests

# All tests
docker compose exec app php artisan test

# Specific test
docker compose exec app php artisan test --filter=YourTest

# With coverage
docker compose exec app php artisan test --coverage

Adding Tests

docker compose exec app php artisan make:test YourModelTest

Best Practices

  1. Keep Core Intact - Avoid modifying core files directly. Use:

    • Extension points (Events, Observers, Middleware)
    • Service providers for registration
    • Configuration files for settings
  2. Maintain Tenant Isolation - Always use BelongsToTenant trait on tenant-scoped models

  3. Test Everything - Write tests for custom code, especially tenant isolation

  4. Document Changes - Keep track of customizations for upgrades

  5. Use Version Control - Commit changes with descriptive messages


Getting Help