Customization Guide
This guide covers how to customize Tenanto for your specific needs.
Branding
Changing the Application Name
-
Edit
.env:APP_NAME="Your SaaS Name" -
Update
config/app.phpif 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
- Add your logo to
public/images/ - 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
-
Create product/price in Stripe Dashboard
-
Add to
.env:STRIPE_PRICE_STARTER=price_xxx -
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, ], ], -
Add enum case in
app/Domain/Billing/Enums/SubscriptionPlan.php:case Starter = 'starter';
Changing Plan Features
Edit config/billing.php to modify:
- User limits
- Team limits
- Project limits
- Feature flags
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
-
Keep Core Intact - Avoid modifying core files directly. Use:
- Extension points (Events, Observers, Middleware)
- Service providers for registration
- Configuration files for settings
-
Maintain Tenant Isolation - Always use
BelongsToTenanttrait on tenant-scoped models -
Test Everything - Write tests for custom code, especially tenant isolation
-
Document Changes - Keep track of customizations for upgrades
-
Use Version Control - Commit changes with descriptive messages
Getting Help
- Check Troubleshooting for common issues
- Review technical documentation
- Contact [email protected] for assistance