optional

Access properties and methods on a possibly-null value without raising an error.

Signature
optional(mixed $value = null, ?callable $callback = null): mixed
  • Added in Laravel 5.5

Parameters

ParameterTypeDescription
$valueoptionalmixed= nullThe value that may be null. Property reads and method calls on the returned Optional are forwarded to it.
$callbackoptional?callable= nullInvoked with the value only when the value is not null. When supplied, the helper returns the callback's result instead of an Optional.

Return value

mixedWith no callback, an Illuminate\Support\Optional wrapping the value. With a callback, the callback's result when the value is not null, and null when it is.

optional() wraps a value in an Illuminate\Support\Optional object. Reading a property or calling a method on that wrapper forwards to the underlying value if it exists, and returns null if the value was null. It also implements ArrayAccess, so optional($maybeNull)['key'] is safe too.

The closure form

Passing a callback as the second argument changes the return type entirely: you get the callback's result when the value is not null, and plain null when it is. This is the form to use when you want a transformation rather than a chain.

Does PHP's ?-> replace it?

Largely, yes. Since PHP 8 the nullsafe operator handles $user?->address?->street natively, and it is faster and clearer. optional() still earns its place in three spots: Blade templates written before 8.0 conventions settled, values that need ArrayAccess, and objects using __get where the wrapper's forwarding behaviour is wanted.

Only one hop is protected

The wrapper returns a raw null, not another Optional. So optional($a)->b is safe but optional($a)->b->c is not — the second arrow dereferences a plain null and warns. Chain with ?-> after the first hop, or use data_get() for a whole path.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Render a related model's attribute in a view when the relation may not be loaded.
  • Transform a nullable lookup result in one expression without an if statement.
  • Read an attribute off a value that PHP's nullsafe operator cannot reach, such as an ArrayAccess object.

Frequently asked questions

Should I use optional() or PHP's nullsafe ?-> operator?

Prefer ?-> in PHP 8 code — it is native, faster, and chains correctly through multiple levels. Keep optional() for Blade templates where it is idiomatic, for ArrayAccess values, and for the closure form, which has no operator equivalent.

Why does optional($x)->a->b still throw an error?

Because optional() protects only the first hop — it returns the raw underlying value, which is a plain null, and the second arrow then dereferences that null. Use data_get($x, 'a.b') or $x?->a?->b for a full path.

Is optional(null) equal to null?

No, it is an Illuminate\Support\Optional instance, so a === null or is_null() check against it is always false. Only the result of reading a property or calling a method on it is null.

More examples

The one-hop limit

Optional::__get returns the raw value, so the protection stops after one hop. data_get() and the nullsafe operator both handle the whole path, which is why they are the better choice for anything deeper than a single attribute.

Example · php
<?php

$company = null;

// Safe - one property read.
var_dump(optional($company)->name);
// NULL

// NOT safe - the second arrow dereferences a plain null.
// var_dump(optional($company)->address->city);
// Warning: Attempt to read property "city" on null

// Use a full path instead:
var_dump(data_get($company, 'address.city'));
// NULL

// Or PHP's own operator, which chains properly:
var_dump($company?->address?->city);
// NULL

In a Blade view

This is the setting where optional() still reads best: an unloaded or absent relation renders as a fallback instead of throwing during template compilation. Note the ?-> after created_at, needed because Optional only covers the first hop. Needs a booted application.

Example · php
<?php

// resources/views/users/show.blade.php
?>
<p>Street: {{ optional($user->address)->street ?? 'Not provided' }}</p>
<p>Joined: {{ optional($user->profile)->created_at?->toFormattedDateString() }}</p>

{{-- The closure form keeps the formatting logic out of the template --}}
<p>Plan: {{ optional($user->subscription, fn ($s) => strtoupper($s->plan_name)) ?? 'Free' }}</p>

Closure form versus an if statement

With the callback supplied, the return value is the callback's result rather than an Optional, so the function's ?string return type holds. This collapses a four-line null guard into one expression.

Example · php
<?php

function initials(?object $user): ?string
{
    return optional($user, function (object $user): string {
        return collect(explode(' ', $user->name))
            ->map(fn (string $part) => strtoupper($part[0]))
            ->implode('.');
    });
}

$taylor = new stdClass;
$taylor->name = 'Taylor Otwell';

var_dump(initials($taylor)); // string(3) "T.O"
var_dump(initials(null));    // NULL

Discussion

  • Be the first to comment on this lesson.