Skip to content

Under the Hood

Job Wrapping

Executables are not jobs themselves. When dispatched, the package wraps the executable in a real Laravel job class:

ScenarioJob class used
Standard dispatchExecutableJob
Unique jobExecutableUniqueJob (implements ShouldBeUnique)
Unique until processingExecutableUniqueUntilProcessingJob (implements ShouldBeUniqueUntilProcessing)
Sync executionExecutableSyncJob (not queueable)
Test mode (queueable)ExecutableJob with FakeJob attached
Test mode (sync)ExecutableSyncJob (not queueable)

The package selects the right class based on the resolved configuration. The wrapper jobs implement ShouldQueue and use the standard Laravel traits (InteractsWithQueue, Queueable, Batchable, SerializesModels). They are processed by the queue worker exactly like native jobs. Horizon, Pulse, and any queue monitoring tools see them as regular jobs.

Model Serialization

When a queued job runs, Eloquent models are re-fetched from the database. By default, any relations that were loaded at dispatch time are also re-queried. Laravel offers #[WithoutRelations] to prevent this on both the class and individual constructor properties:

php
// Laravel: class-level or per-property
#[WithoutRelations]
class ProcessPodcast implements ShouldQueue
{
    public function __construct(
        #[WithoutRelations] // Laravel supports this per-property, executables do not
        public Podcast $podcast,
    ) {}
}

Executables support #[WithoutRelations] at the class level only (not per-argument), but add two things Laravel doesn't have:

  1. A configure() hook: $config->withoutRelations()
  2. A global default via config/executable.php:
php
'serialize_models_with_relations' => false, // default: true (Laravel's default)

This disables relation re-loading for all executables at once, so each executable only runs the queries it actually needs.

Resolution order (first non-null wins):

  1. Per-executable: #[WithoutRelations] attribute, public bool $withoutRelations property, or $config->withoutRelations()
  2. Global: config('executable.serialize_models_with_relations')

Horizon Integration

ExecutableJob::tags() generates Horizon tags automatically from any Eloquent model or collection passed as an execute argument (e.g., App\Models\User:1, App\Models\Order:42). If the executable defines its own tags() method, that takes precedence.

ExecutableJob::displayName() returns the executable class name by default, or delegates to the executable's displayName() method if defined.

Middleware Resolution

Like Laravel, job middleware is resolved by merging two sources:

  1. The public array $middleware property on the executable
  2. The return value of the middleware() method on the executable

Both are combined and passed to the queue worker. The difference: the middleware() method receives execute-arguments by name, so middleware can depend on runtime input.