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

Component Then When Laravel Package

serendipity_hq/component-then-when

Serendipity HQ's Component Then When adds fluent then() and when() helpers to Laravel components, letting you chain conditional logic and post-processing directly in component definitions. Lightweight and easy to drop in, it means cleaner, more expressive code with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Start by installing it as a plain Composer library — it is a component, not a bundle, so there is no service registration to configure:

composer require serendipity_hq/component-then-when

Where to look first (in this order):

  1. README.md — the API surface is small, so the README plus the src/ tree will tell you almost everything.
  2. src/ — read the strategy/interval classes and the small value objects. This is where the retry math actually lives.
  3. tests/ — with a low-adoption component, the test suite is effectively the spec. Read the tests before trusting an edge case (zero attempts, huge attempt counts, negative intervals).
  4. composer.json — confirm the supported PHP range and whether it brings in any transitive dependencies you don't already have.

First use case — a "don't retry yet" gate. The one thing this library does is answer: "attempt N just failed, when should attempt N+1 happen?" Wire it into the simplest version of your retry flow first:

// Illustrative — confirm exact class/method names against src/ and README.
$nextAttemptAt = $retrySchedule->nextRetryAt(
    failedAt: new DateTimeImmutable('now'),
    attemptNumber: $job->attemptNumber(),
);

Persist $nextAttemptAt alongside the failure, then let a scheduler pick the job back up when that timestamp arrives. You now have fault-tolerant retries without sleep() calls and without inventing backoff math yourself.

Implementation Patterns

Persist the schedule, don't hold it in memory. Treat the computed timestamp as durable state on your job/attempt row (next_retry_at, attempt_count). Restarting a worker or a server must not lose the retry plan. A scheduler (Symfony Scheduler, Laravel's Schedule, cron, or a queue's available_at/delay) then does the waiting for you.

One place computes, many places consume. Wrap the component behind a thin service of your own — e.g. RetryScheduler — and inject that everywhere. This gives you:

  • a single seam to swap the strategy later,
  • a seam to inject a fixed clock in tests,
  • a place to add policy (max attempts, max delay, retryable-only errors) that sits around the library rather than inside it.

Pair it with an attempt ceiling. The library calculates when, not whether. Keep a separate maxAttempts check and a terminal "give up / dead-letter" path; otherwise a permanently failing job retries forever with an ever-growing interval.

Route by job class, not one global policy. Cheap, idempotent jobs (cache warmers) can use a short base interval; expensive or rate-limited ones (payments, third-party APIs) use a longer base and a hard cap. Give each job type a named strategy and select it at dispatch time.

Reschedule, never block. In queues, map the result to your transport's delayed delivery (e.g. Laravel release($delay) / $job->delay(...), Symfony Messenger DelayStamp) rather than sleeping inside a worker. Sleeping ties up a concurrency slot and defeats the point of a fault-tolerant system.

Test with a frozen clock. The most valuable tests are deterministic: fix "now", assert the exact DateTimeImmutable returned for attempt 1, 2, 3, and at the cap. Keep these as table-driven tests in your own suite so a dependency bump can't silently change your retry behavior.

Observability hook. Log (jobId, attemptNumber, nextRetryAt) at the point of calculation. When a job is stuck, this single log line tells you whether it was scheduled incorrectly or never picked up.

Gotchas and Tips

The library only does the math. It does not sleep, schedule, persist, or execute retries. Everything in the paragraph above about queues, clocks, and attempt ceilings is your responsibility. If you expected a full retry framework, look elsewhere (Symfony Messenger's retry strategy, Laravel's retry(), or a resilience library) and use this only for the interval computation.

Verify the release situation before pinning. The listed last release (2026-03-21) is in the future relative to normal cadence and the package has zero known dependents. Treat the version metadata as something to double-check on Packagist (composer show serendipity_hq/component-then-when) and pin a concrete tag rather than a floating ^ constraint until you trust the release stream.

DateTimeImmutable and timezones. Store and compare retry times as UTC, and prefer immutable date objects throughout. Mixing a mutable DateTime into a "when" calculation is the classic way to get a timestamp that quietly mutates during logging or formatting.

Watch integer vs. float intervals. Exponential backoff produces fractional seconds at higher attempt counts. Decide explicitly whether you round up (safe: retry slightly later) or truncate (risky: thundering herd). Cast at the boundary where you hand the value to your queue, not inside your domain code.

Guard against overflow and absurd waits. Without a cap, exponential growth reaches days or years surprisingly fast (attempt 20+). Always clamp to a maxDelay and stop retrying at maxAttempts; a retry-at timestamp three years out is effectively a dead job.

Thundering herd. If many jobs fail simultaneously, identical intervals mean they all retry simultaneously. If the component exposes a jitter/decorrelated strategy, use it; otherwise add a small random spread (±10–20%) in your wrapper. This is a policy decision, so keep it in your code, not buried in a strategy.

Extension point. If the built-in strategies don't fit (e.g. you need to read a Retry-After header from a 429), implement your own strategy behind your RetryScheduler seam rather than forking the package. The concept is small enough that wrapping is cheaper than patching a library with 2 stars.

Budget review time. With essentially no community usage, expect to read the source yourself for anything beyond the happy path. That's fine for a focused backoff calculator — but factor that reading time into the estimate, and don't build core business logic on undocumented behavior.

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.
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
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo