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 Actions Laravel Package

dragon-code/laravel-actions

dragon-code/laravel-actions is a lightweight Laravel package for encapsulating business logic in reusable action classes. It provides a simple API to define, resolve, and execute actions, helping keep controllers and models thin while making workflows easier to test and maintain.

View on GitHub
Deep Wiki
Context7

Getting Started

The first thing to sort out is the naming: the package is published as dragon-code/laravel-actions, but the repository and the description point at Laravel Deploy Operations (TheDragonCode/laravel-deploy-operations). Do not confuse it with lorisleiva/laravel-actions (the action-class pattern) — this one is about running one-off operations during a deployment, tracked in a database table so each operation executes exactly once per environment.

Minimal path from zero to a working operation:

  1. Install it and confirm which package actually landed:
    composer require dragon-code/laravel-actions --dev
    composer show | grep dragon-code
    
    You should see the deploy-operations package. If you got something else, stop and re-check before writing any code.
  2. Publish config and the tracking migration, then migrate:
    php artisan vendor:publish --provider="DragonCode\LaravelDeployOperations\ServiceProvider"
    php artisan migrate
    
  3. Discover the real command surface — this is the "look here first" step, because command names have shifted across releases:
    php artisan list | grep -i deploy
    
    Expect the family to be deploy:make (scaffold), deploy:run (execute pending), and typically a status/rollback variant. Check whether deploy:run advertises --isolated — 7.3.0 fixed its presence semantics to match Laravel core, so it is now a real option worth using (see Gotchas).
  4. Generate your first operation:
    php artisan deploy:make warm_product_cache
    
  5. Open the generated class, read the base class it extends, and note whether the contract is run() or up()/down(). That determines whether you get reversibility for free. Also check whether operations can be queued (the base class exposes queue-related props in recent versions).

First use case: the classic one that motivates the package — a cache/settings refresh that must happen after the new code is live and after migrations, but must not re-run on every deploy. Write a single operation that flushes the config/route/view caches and warms the pages that matter, run it against staging, then wire deploy:run into the deploy script.

Implementation Patterns

CI/CD hook, not a manual step. The value of this package collapses if someone has to remember to run it. Put it at the end of the deploy pipeline, after composer install, after php artisan migrate --force:

php artisan migrate --force
php artisan deploy:run --force        # verify the exact non-interactive flag in your version
php artisan queue:restart

The ordering matters: operations frequently read schema or config that only exists post-migration.

One operation per concern. Because the package records execution, an operation is effectively a named, permanently-retired unit of work. Keep them small and single-purpose — backfill_order_totals, migrate_legacy_slugs, enable_new_billing_flag. A monolithic "do everything" operation becomes un-retryable the moment step 4 of 9 fails.

Data backfills in chunks. Operations are the right home for the work that is too slow or too risky for a migration:

Order::query()->whereNull('total_cents')
    ->chunkById(500, fn ($orders) => /* recompute */);

Chunking keeps memory flat and lets you re-run the residual set by writing a follow-up operation rather than editing the executed one.

Long work: dispatch a job, don't grind in the deploy. Recent releases make queued operations a first-class path. Rather than chunking a multi-million-row backfill inside the deploy step, have the operation dispatch a job (or mark the operation itself as queued, if your version's generated stub supports it) and keep the artisan call short. Since 7.3.0 you can also swap the job class the package dispatches via the queue.job config key — that is the extension point for wrapping the job with your own base class (tenant context, tags, custom retry policy) without forking the package.

Uniqueness locks for queued work. 7.3.0 added a configurable uniqueness-lock duration for queued operation jobs. Practical consequence: a queued operation will not be enqueued twice while its lock is held, and the lock has a TTL. Tune the duration to longer than your worst-case job runtime, or the lock expires mid-flight and a second copy slips through. Read the published config for the exact key name, since it is new in this release.

Deployment chores as operations. storage:link, search-index rebuilds, S3 permission fixes, feature-flag flips, permission-table seeding — anything that is a state transition rather than a schema change. This keeps migrations/ honest and reviewable, and stops the "migration 2023_…_fix_data_again.php" graveyard.

Environment-scoped writes. Anything that touches production data should assert its environment rather than trusting the deploy config:

if (app()->isProduction() && ! $confirmed) { return; }

Testing pattern. Operations are hard to test as operations (they're skipped once recorded), but the body should delegate to a regular service/action class that you unit-test normally. Treat the operation class as a thin, timestamped entry point — that also keeps it readable in a git log. If the operation is queued, test the dispatched job directly instead.

Rollback posture. If your version exposes a down()/rollback path, use it only for reversible things (cache flips, flag toggles). Do not write a down() for a data backfill that overwrote values; that is false comfort.

Gotchas and Tips

  • The dev-dependency trap. --dev is the documented install, but composer install --no-dev in production removes the package — and if your deploy script then calls php artisan deploy:run, the build fails with "command not found." Either install it in require, or ensure the deploy ordering runs operations from a build that still has dev dependencies. This is the single most common way this package bites people. The queued-operation path makes this worse: a job class that extends a package class will fail to unserialize in a worker lacking the package.
  • Single-runner assumption. The tracking table makes operations once-per-database, not once-per-server. On a multi-node deploy (load-balanced, rolling releases), run deploy:run on exactly one node, or you'll get concurrent execution and duplicate work. Use a deploy lock or a dedicated "deploy" job in your pipeline. As of 7.3.0 the --isolated flag now follows Laravel core presence semantics, so it is a viable second line of defence — but it relies on the cache lock store being shared across nodes, so on a single-node file cache it buys you nothing.
  • No automatic transaction. Assume an operation that fails halfway has partially applied. Write bodies to be re-runnable/idempotent, and accept that a half-finished operation may need a manual fix plus a follow-up operation — the failed record's state in the tracking table determines whether it re-runs, so inspect that table first when debugging. For queued operations, remember the "recorded" moment and the "job finished" moment may be far apart; decide which one you treat as done.
  • Long operations vs. timeouts. Backfills over millions of rows will blow past typical CI job timeouts and HTTP-style deploy limits. Chunk aggressively, or move the heavy lifting to a queued job that the operation merely dispatches — and if you do queue it, set the uniqueness lock duration (new in 7.3.0) above your job's worst-case runtime.
  • Name/identity confusion. Given the laravel-actions vs laravel-deploy-operations mismatch, pin the version and verify the installed package name in composer.lock. Also worth noting: 243 stars, zero dependents, and a release date listed in the future — treat this as a small, single-maintainer package. Read the source (it's compact) before betting a critical deploy pipeline on it, and consider wrapping the call so you can swap it out.
  • php artisan list before coding. Command signatures and config keys have moved between majors — and 7.3.0 added new queue.* config keys, so a config file published on an older version won't have them. Re-publish or diff the config after upgrading; array_merge defaults usually save you, but a stale cached config won't.
  • Config caching. If you config:cache as part of the deploy, publish customisations before that step, and remember the operations directory path is config-driven — a wrong path silently yields "no pending operations," which reads exactly like success. The same applies to queue.job: a stale cached config silently dispatches the package's default job instead of yours.
  • Upgrading to 7.3.0. No removals or deprecations in this release, so it should be a drop-in. The only behavioural change is the --isolated fix — if you had worked around the old broken presence check (e.g. passing --isolated=1 to force truthiness), drop the workaround and re-test your deploy command, since the option now parses the way Laravel core does.
  • Extension points. The operation base class, the tracking model, the service provider's config, and now the dispatched job class (queue.job) are the practical hooks: subclass the base to add environment guards or logging, swap the job class to add your own middleware/tags/retries, and keep a thin custom artisan command that wraps deploy:run if you need extra pre/post behaviour (Slack notifications, sentry breadcrumbs) without forking.
  • Debugging checklist when an operation "didn't run": (1) is the package installed in this environment, (2) does the tracking table have a row for it, (3) is the file inside the configured operations path and autoloaded, (4) is config:cache serving a stale path, (5) for queued operations, has the job actually been picked up (check the queue connection and the uniqueness lock).
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels