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 Null Carbon Laravel Package

genealabs/laravel-null-carbon

NullCarbon implements the Null Object pattern for Carbon in Laravel. When a date is missing, it stands in safely so date methods return null instead of throwing errors or defaulting to now, keeping models, Blade views, and formatting logic from breaking on nullable timestamps.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and pin. composer require genealabs/laravel-null-carbon (pin the exact version — the project has a single maintainer and a mid-2024 last release). No service provider, migrations, or config files are required.

Read the source first (10 minutes). Two files matter:

  • src/NullCarbon.php — the null object. This is the source of truth for what format(), toDateTimeString(), toIso8601String(), jsonSerialize(), __toString(), and comparison helpers return. Everything else in your rollout depends on these answers.
  • The trait (in src/Traits/) — a one-line use on a model or base model. It's what swaps a null date read into a NullCarbon instance.

Apply to a single leaf model first. Add the trait to one low-risk model with an obviously-nullable date (last_seen_at, cancelled_at, published_at) and let the existing test suite plus a JSON-diff of that model's endpoints tell you whether the behavior is what you want. Example target:

class User extends Model
{
    use \GeneaLabs\NullCarbon\Traits\HasNullCarbon; // verify exact name in src/Traits

    protected $casts = ['last_seen_at' => 'datetime'];
}

First use case to try: a Blade view or API resource that formats an optional timestamp.

{{ $user->last_seen_at->format('M j, Y') }}     {{-- no ?? or ?-> needed --}}
{{ $user->last_seen_at->diffForHumans() }}       {{-- verify output in source first --}}

Write a test asserting the rendered/ serialized result for a null date before you trust it: $this->assertSame('', $user->fresh()->last_seen_at->format('Y-m-d')) or whatever the source confirms. That test becomes your regression guard.

Implementation Patterns

Base-model adoption (recommended for consistency). Apply the trait once on an abstract BaseModel extends Model, have every app model extend it, and treat "date attributes are never null in application code" as the house rule. This makes adoption uniform and removal a one-line revert if you abandon the package. Defer the base-model step until the single-model prototype and serialization diff pass.

Selective per-model adoption. If only a handful of models have nullable dates, apply the trait model-by-model. This keeps blast radius small but creates two null-handling regimes in the codebase — document which models are "null-carbonized" or new engineers will reintroduce ?-> guards.

Accessor / cast interaction. Keep 'field' => 'datetime' casts on the model; the trait is layered on top of Eloquent's date casting, not a replacement. If you use a custom Cast instead of the trait, confirm it delegates to NullCarbon — do not mix both approaches on the same attribute.

Blade and API resources. The high-value pattern is chained formatting in presentation layers: $model->due_at->format(...), ->toDateString(), ->diffForHumans(), ->isoFormat(). In JsonResource classes, prefer explicit transformation ($this->due_at ? $this->due_at->toIso8601String() : null) so the API contract stays unambiguous, rather than relying on NullCarbon::jsonSerialize().

Deliberate branching with isNull(). Where "this date is genuinely absent" drives business logic (scheduling, eligibility, "has it been cancelled?"), branch on the null-object sentinel rather than on is_null() or === null. Wrap this in a single helper on your base model so call sites are grep-able.

Testing pattern. Assert on three things for every adopted model: (1) formatting a null date renders the expected string/empty, (2) the JSON shape of an endpoint is unchanged from before adoption, and (3) instanceof Carbon still returns true so downstream type hints don't break. Treat any change to (2) as a breaking API change.

Migration / rollout sequence. Prototype on one leaf model → diff serialization → write the convention into a base-model trait → roll out read/display models → defer billing, entitlements, and compliance models until proven in production.

Gotchas and Tips

Reads only — queries are untouched. NullCarbon changes how a hydrated null date is presented, not how it is stored or queried. whereNotNull('published_at'), orderBy('published_at'), and where('cancelled_at', null) all behave exactly as before. If you expect the null object to participate in query building, you will be surprised.

Silent semantic drift is the real risk. A missing date that reads as a valid-looking NullCarbon can feed isPast(), diffInDays(), eligibility checks, or reminder jobs and return a plausible-but-wrong answer with no exception. For any field with financial, legal, compliance, or audit significance, keep explicit if ($model->date === null) checks and do not adopt the trait.

is_null() and === null silently change meaning. Existing null guards on adopted attributes become dead code or, worse, wrong logic. Audit every call site on the model before adopting: grep -rn "->published_at\|->cancelled_at" app/ resources/. These are silent behavior changes, not compile errors.

instanceof Carbon still returns true. NullCarbon extends Carbon, so type hints and instanceof checks pass — that is the point of the pattern, but it means you cannot use type checks to distinguish "real date" from "null object." Use the package's sentinel (verify its exact name in the source — likely isNull()) if you need to branch.

Verify serialization before shipping. A null attribute that previously serialized as JSON null may now serialize as "", "0000-00-00...", or be omitted depending on NullCarbon::jsonSerialize() / __toString(). Diff every endpoint that touches the model. Mobile clients, snapshot tests, and typed API consumers are the usual victims.

Comparison methods need verification. Confirm what eq(), lt(), isPast(), equalTo(), diffForHumans(), and toDateTimeString() return on a NullCarbon before relying on any of them in business logic. The method returning null is benign for a view; it is not benign inside an if.

CarbonImmutable and timezone. Carbon's immutability contract changes what "benign default" means. If your app uses CarbonImmutable or a non-default timezone, verify the null object behaves the way your tests expect before adopting — the June 2024 release predates recent Carbon majors.

Version compatibility is the top blocker. The package's composer.json constraints and its CI matrix against Laravel 9–12 / Carbon 2–3 / PHP 8.x are the gating question, not the feature set. If your target stack is unsupported, the choice reduces to (a) pin Laravel/Carbon, (b) fork under your org with widened constraints, or (c) replace with an in-house custom cast — all cheap, because the surface area is ~50–100 lines and MIT-licensed.

Dependency risk and exit plan. 56 stars, 0 dependents, single maintainer, last release mid-2024. Pin the exact version, isolate the integration behind a base model or trait, and register it in your dependency review with an explicit "fork or replace" trigger. Designate one internal owner who has read the source — that person can become the authority in under a day.

Debugging tip. When a null date behaves unexpectedly, put the source NullCarbon class next to the call site and read the exact method being invoked, rather than guessing. Every "why did this return X?" question in this package is answered by that one file.

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.
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
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums