studio/laravel-totem
Laravel Totem provides a Horizon-style dashboard to manage Laravel Scheduler jobs. Create, enable/disable, and edit scheduled Artisan commands without changing code. Includes migrations/assets, auth customization, and supports Laravel 11/12 on PHP 8.2+.
## Getting Started
### Minimal Steps to First Use
1. **Installation**
```bash
composer require always-open/laravel-totem:^13.0
php artisan migrate
php artisan totem:assets
config/totem.php file exists (published automatically).Define a Scheduled Command
In app/Console/Kernel.php, register a command in the schedule() method:
protected function schedule(Schedule $schedule)
{
$schedule->command('emails:send')->dailyAt('10:00');
}
php artisan schedule:list to confirm the command is registered.Access the Dashboard
Visit /totem (or your configured route) to see the UI. The dashboard will auto-detect all scheduled commands.
emails:send), and use the slider to enable/disable it without editing code.Registering Commands:
Ensure commands are defined in Kernel.php's schedule() method. Totem mirrors Laravel’s scheduler but adds a UI layer.
$schedule->command(new SendEmailsCommand())->hourly();
Grouping Commands:
Organize commands in the UI by prefixing them with a namespace or using tags (via ->name()):
$schedule->command('analytics:process')->name('Analytics')->daily();
config('app.env')) to conditionally schedule jobs:
if (app()->environment('production')) {
$schedule->command('backups:run')->weekly();
}
Schedule commands to run after events (e.g., scheduled:run):
$schedule->command('cleanup:logs')->after(function () {
return now()->startOfDay();
});
Offload commands to queues by default:
$schedule->command('process:invoices')->onOneServer()->withoutOverlapping();
Extend Views: Publish and override Totem’s Blade templates:
php artisan vendor:publish --tag=totem-views
resources/views/vendor/totem/... to add custom fields (e.g., job priority).Add Metadata:
Attach custom data to commands via ->name() or extend the Totem\Jobs\Job model:
$schedule->command('deploy:staging')->name('Deploy [Staging]')->description('Deploys to staging environment');
// Enable a job via API
$response = Http::post('/api/totem/jobs/enable', ['command' => 'emails:send']);
auth:sanctum).jobs table. Query via:
use Totem\Jobs\Job;
$job = Job::where('command', 'emails:send')->latest()->first();
Job model to log additional context:
protected static function booted()
{
static::created(function ($job) {
\Log::info("Totem job triggered: {$job->command}", $job->attributes);
});
}
Schedule::fake() and assert job triggers:
$schedule = $this->app->make(Schedule::class);
$schedule->fake();
$schedule->command('test:job')->shouldBeRun();
$this->visit('/totem')
->see('emails:send')
->press('Run Now')
->see('Job executed successfully');
Cron Configuration Mismatch
schedule:run periodically.* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
config('totem.cron_expression') to customize the cron schedule in Totem’s config.Overlapping Jobs
withoutOverlapping() may fail silently if the previous run isn’t marked as finished.public function handle()
{
try {
// Job logic
\Log::info("Job completed: " . now());
} catch (\Throwable $e) {
\Log::error("Job failed: " . $e->getMessage());
throw $e;
}
}
Permission Issues
403 if middleware isn’t configured.routes/totem.php:
Route::middleware(['auth', 'verified'])->group(function () {
// Totem routes
});
Timezone Confusion
config('app.timezone') and ensure Totem’s config respects it:
'timezone' => env('APP_TIMEZONE', 'UTC'),
Database Locking
DB::transaction() for bulk operations in custom Totem extensions.Job Not Appearing in UI?
Kernel.php.php artisan schedule:list for typos or missing schedules.php artisan schedule:clear-cache
UI Assets Not Loading
php artisan totem:assets and check public/vendor/totem/ for files.php artisan cache:clear
php artisan view:clear
Logs Missing
config/totem.php has 'log_enabled' => true.storage/logs/laravel.log for Totem-related entries.Custom Job Fields
Extend the Job model to add columns (e.g., priority, owner):
php artisan make:migration add_priority_to_jobs_table --table=jobs
protected $fillable = ['command', 'enabled', 'priority'];
Webhook Triggers Add a webhook endpoint to trigger jobs via HTTP:
Route::post('/totem/webhook/run', function (Request $request) {
$job = Totem\Jobs\Job::where('command', $request->command)->first();
if ($job) {
$job->runManually();
return response()->json(['status' => 'success']);
}
return response()->json(['error' => 'Job not found'], 404);
});
Slack/Teams Notifications Integrate with Totem’s job events to send alerts:
use Totem\Events\JobExecuted;
Event::listen(JobExecuted::class, function ($event) {
if ($event->job->failed()) {
// Send Slack notification
}
});
Bulk Actions Add a bulk toggle feature via a custom controller:
public
How can I help you explore Laravel packages today?