Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Totem Laravel Package

studio/laravel-totem

Manage your Laravel schedule from a clean dashboard. Register console commands as scheduled tasks, enable or disable them on the fly without touching code, and control dashboard access with simple auth callbacks. Requires PHP 8.2+ and Laravel 11 or 12.

View on GitHub
Deep Wiki
Context7

Getting Started

Totem replaces hard-coded $schedule->command(...) calls with database-backed tasks you can enable, disable, and re-time from a dashboard at /totem. The mental model is: the cron entry runs once a minute and Totem decides what to execute — so the package is only useful once you have the standard Laravel scheduler wired up first.

Minimum viable setup:

composer require studio/laravel-totem
php artisan migrate
php artisan totem:assets

Then the non-negotiable server-side line (Totem does not add this for you):

* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

Where to look first, in order:

  1. config/totem.php — publish the package config and read it end to end before touching the UI. This is where the command filter, middleware, and cache settings live.
  2. App\Providers\AppServiceProvider::boot() — this is where you gate the dashboard (Totem::auth(...)). Do this before you deploy, not after.
  3. The totem_tasks / totem_task_results tables — understanding what a "task" row actually is (command + parameters + cron-ish frequency + is_active) clarifies most of the package's behaviour.
  4. php artisan schedule:list — your fastest sanity check that Totem's tasks are actually registered with Laravel's scheduler.

Your first use case should be the boring one: pick a single existing Artisan command (a report generator, a cache warmer, a sync job), recreate it in the dashboard with a simple frequency, save it as active, and watch it fire. Confirm the result row appears with output and duration before you migrate anything else. Keep the original $schedule->command() line commented out in routes/console.php or Kernel until you trust it — do not run both, or you double-execute.

Environment note: by default the dashboard is only reachable in local. In staging or production it will 404/403 until you set the auth callback. Plan the auth story (a gate, an allowlist of admin emails, or a dedicated middleware) as part of installation, not as a follow-up ticket.

Implementation Patterns

Gate the dashboard once, centrally. The idiomatic setup is a single closure in AppServiceProvider::boot():

use Studio\Totem\Totem;

Totem::auth(function ($request) {
    return $request->user()?->isAdmin() ?? false;
});

Because this is a single global callback, resist the temptation to stuff per-page logic in it — put role checks in a gate and call the gate here. For anything richer than a boolean (IP allowlists, basic auth, SSO-only), set TOTEM_WEB_MIDDLEWARE in .env and add your own middleware rather than fighting the closure.

Treat the dashboard as a config surface with a code-shaped backup. The core value proposition is that non-developers (ops, support, marketing) can disable a noisy job at 2am without a deploy. That only works if you establish a convention: Totem owns scheduling (frequency, on/off, parameters), code owns behaviour. Keep Artisan commands small, deterministic, and self-logging, and never let the task definition become the only record of what a job does.

For teams that need reproducibility, seed tasks in a seeder or migration against the Totem models so a fresh environment comes up with the intended task list, then let the UI own toggling. This is the pragmatic hybrid: declarative baseline, operational overrides.

Keep commands discoverable but bounded. Every Artisan command is exposed in the Create/Edit dropdown, including framework internals and third-party package commands. Two levers:

// config/totem.php
'artisan' => [
    'command_filter' => ['reports:*', 'sync:customers'],
    'whitelist' => true,
],

Use a whitelist for production installations so the dropdown only shows the dozen commands your team actually schedules. fnmatch syntax means * and ? work but ** semantics don't — reports:* is fine, reports:*:daily relies on fnmatch's single-segment behaviour and is easy to get subtly wrong. Flip whitelist to false only when the filter list is genuinely short and stable; a blacklist rots as new commands are added.

The complementary pattern is protected $hidden = true; on internal commands (destructive migrations, debug helpers) so they never appear as options at all. Do this in code, not just in the filter — it survives config drift.

Parameters are the integration point with your existing commands. Totem passes a raw string through to the console invocation, so design commands to accept arguments and options cleanly:

customer_id=42 --dry-run --queue='high priority'

This is the place where most real usage lives: one scheduled command, different parameter sets per tenant/environment, individually toggleable. It also means your command's signature is a public contract — renaming or reordering arguments silently breaks dashboard tasks. If a command is scheduled via Totem in production, treat signature changes as breaking changes and grep for the task definition first.

Notifications instead of log-watching. Totem can notify on completion. Enable mail out of the box; add laravel/slack-notification-channel (or Vonage) if the team lives in Slack. Route notifications to a channel, not to individual inboxes — the point is that a failed nightly job is visible to whoever is on call, not to whoever happened to configure it.

Use results for triage, not for auditing. The task detail page with output and duration is genuinely the best part of the package for day-to-day debugging — it's the difference between "the sync ran" and "the sync emitted a 40-second timeout warning." But treat it as an operational window, not a durable audit trail; if you need history, ship the output to your own log/observability stack from inside the command.

Gotchas and Tips

Version alignment is mandatory, and the matrix is the source of truth. Totem 11.x covers Laravel 11 and 12 with PHP 8.2+. The changelog contains a v12.0.2 entry while the README matrix still points Laravel 12 at Totem 11.x — check the release that actually matches your Laravel version rather than assuming the highest tag is the right one, and pin the constraint in composer.json. This package bumps majors in lockstep with Laravel, so a Laravel upgrade is implicitly a Totem upgrade; add it to the upgrade checklist alongside the framework.

Never install v8.4.1. It is explicitly marked as containing an infinite loop bug and superseded by v8.4.2. On modern Laravel this is ancient history, but it's the canonical example of why you read release notes for this package before bumping.

Republish assets on every upgrade. php artisan totem:assets after composer update is not optional — the Vue front-end is published into public/, so stale assets produce blank pages, missing buttons, or a task-output popup that renders nothing. There is a documented bug class here: v12.0.2 fixed an empty output popup caused by @json() quoting colliding with HTML attributes (switched to Js::from()). If you see a mysteriously empty output panel, it's an asset/publish problem, not a data problem.

Cache inconsistency between UI and worker servers is the sneaky production failure. If your web servers and queue/scheduler servers use different Redis instances, a task enabled in the UI may not be seen by the scheduler. Set TOTEM_CACHE_STORE to a store shared by all servers:

TOTEM_CACHE_STORE=redis-shared

Setting it to array disables persistence entirely — useful for a single-box setup or for eliminating cache as a suspect during debugging (each request hits the DB, so behaviour becomes predictable and slow). When a task "won't turn off," this is the first thing to test.

Table name collisions are real in mature apps. Totem's tables use generic names; if tasks or task_results already exist in your schema, set TOTEM_TABLE_PREFIX before running php artisan migrate, not after. Changing the prefix later leaves orphaned tables and breaks the published models' expectations.

Route model binding conflicts. v11.1.1 specifically made route bindings more unique to avoid clashes with host-app bindings. If you have a wildcard or catch-all route, or your own {task} binding, verify /totem still resolves after upgrades — a 404 or a "model not found" on a Totem URL is usually a binding collision, not a missing route.

Migrating down on SQLite. Rollback support for SQLite only landed in v11.1.3. If you develop on SQLite and need migrate:rollback, be on at least that version, or accept that rollback will fail and plan to rebuild the DB instead.

Don't expect full $schedule fluency from UI-created tasks. Laravel's fluent API gives you withoutOverlapping(), onOneServer(), runInBackground(), when()/skip() — these are code-level constructs, and a dashboard task has no place to express most of them. If a job genuinely must not overlap (long-running imports, anything hitting an external rate limit), enforce it inside the command (a cache lock at the top of handle()) rather than assuming the scheduler handles it. This is the single most common source of surprise in day-to-day use.

Watch the results table. Task output and duration rows accumulate with every run. There's no obvious built-in pruning documented, so schedule your own cleanup against the results table early — a five-minute job running every minute generates 1,440 rows/day on its own. Check config/totem.php for a retention setting in your installed version before writing custom code.

Debugging checklist when a task doesn't run, in order: is the cron entry present on that server; does php artisan schedule:list show the task; is the task is_active; is the cache store shared (see above); does a manual php artisan <command> <params> from the project root work. That sequence resolves almost every "Totem is broken" report, and step five is the one people skip — Totem can only faithfully invoke a command that already works from the CLI.

Extension points are thin, by design. This is a UI wrapper, not a job framework. If you find yourself wanting custom task types, conditional execution, or chained jobs, the right move is to keep that orchestration in code (a Laravel job, a command with Bus::chain) and let Totem schedule the single entry point. Fighting the package's model usually means you've outgrown it for that particular job — and the maturity signal here (small maintainer pool, version jumps tied to Laravel majors) argues for keeping your dependency surface on it as small as your use cases allow.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai