Anvil
Anvil - The mobile companion for Laravel Forge. Available now. Download for iOS

Mastering Laravel’s data_get(): Read Anything from Arrays, Objects, and Collections

If you’ve ever written a long chain of null checks to fetch a deeply nested value — foo['bar']['baz'] ?? null — you’ll love Laravel’s data_get() helper. It’s a tiny, dependable utility that safely retrieves values from arrays, objects, ArrayAccess implementations, and even nested structures using a simple “dot notation.”

6 min read - 7,272 views -

In this guide, you’ll learn every practical way to use data_get(): from the basics to advanced wildcard patterns, defaults, edge cases, and how it compares to alternatives like Arr::get() and the PHP nullsafe operator.

  • What is data_get()
  • Basic usage (dot notation, arrays, objects, collections)
  • Providing default values (including closures)
  • Wildcards with * and deep matching
  • Escaping dots in keys
  • Using data_get() with Collections
  • Comparing data_get() vs Arr::get() vs nullsafe operator
  • Common patterns and real-world examples
  • Edge cases and gotchas
  • Related helpers: data_set() and data_fill()

What is data_get()

Signature:

  • mixed data_get(mixed $target, string|array|null $key, mixed $default = null)

  • $target: The thing you’re reading from — array, object, ArrayAccess, or a mix.

  • $key: A dot-notated path, an array of segments, or null (which returns the whole target).

  • $default: Value to return if the path isn’t found. Can be a value or a closure.

Under the hood, data_get() walks your structure segment by segment. For objects, it reads public properties (and properties via magic __get). For arrays, it reads array keys. If at any point a segment doesn’t exist, it returns the default value.

Basic usage

Arrays

1$user = [
2 'name' => 'Taylor',
3 'contacts' => [
4 'email' => '[email protected]',
5 'social' => [
6 'twitter' => '@taylorotwell',
7 ],
8 ],
9];
10 
11data_get($user, 'contacts.email'); // '[email protected]'
12data_get($user, 'contacts.social.twitter'); // '@taylorotwell'

Objects

1class Profile {
2 public string $timezone = 'UTC';
3}
4 
5class User {
6 public Profile $profile;
7 public function __construct()
8 {
9 $this->profile = new Profile;
10 }
11}
12 
13$user = new User;
14 
15data_get($user, 'profile.timezone'); // 'UTC'

If your object implements ArrayAccess or uses magic getters, data_get() can still read the value.

Mixed arrays and objects

1$post = (object) [
2 'author' => [
3 'name' => 'Sam',
4 'links' => (object) ['website' => 'https://example.com'],
5 ],
6];
7 
8// data_get() happily traverses both arrays and objects.
9data_get($post, 'author.links.website'); // 'https://example.com'

When $key is null

1$data = ['a' => 1];
2data_get($data, null); // returns the entire $data value

Providing default values

If any segment along the path is missing, data_get() returns the default (third argument).

1data_get($user, 'contacts.phone', 'n/a'); // 'n/a'

You can compute the default lazily using a closure. Laravel will call the closure only if the value is missing:

1$data_get_default = data_get($user, 'contacts.sms', fn () => config('defaults.sms_number'));

This is helpful when the default is expensive to compute or you need environment configuration.

Wildcards with *

Wildcards let you fetch values from lists in one shot. A * segment means “for each element at this level, continue the lookup and return the results.”

One-level wildcard

1$users = [
2 ['name' => 'Ada', 'email' => '[email protected]'],
3 ['name' => 'Linus', 'email' => '[email protected]'],
4];
5 
6data_get($users, '*.email');

Deep wildcards

1$teams = [
2 ['name' => 'Alpha', 'members' => [['name' => 'A1'], ['name' => 'A2']]],
3 ['name' => 'Beta', 'members' => [['name' => 'B1']]],
4];
5 
6data_get($teams, '*.members.*.name');
7// [['A1', 'A2'], ['B1']]

Note: The shape of the returned value mirrors the wildcard levels. If you want a flat list, you can flatten after:

1collect(data_get($teams, '*.members.*.name'))->flatten()->all();
2// ['A1', 'A2', 'B1']

Wildcards with missing keys

If a key is missing in some items, those branches produce null (and may be omitted if subsequent traversal fails):

1$users = [
2 ['name' => 'Ada', 'contacts' => ['email' => '[email protected]']],
3 ['name' => 'Linus'],
4];
5 
6data_get($users, '*.contacts.email');
7// ['[email protected]', null]

Default values with wildcards

The default applies when the top-level traversal can’t find anything. For missing values within a wildcard branch, you’ll typically normalize after:

1$result = data_get($users, '*.contacts.phone');
2// [null, null]
3 
4$normalized = collect($result)->map(fn ($value) => $value ?? 'n/a')->all();
5// ['n/a', 'n/a']

Escaping dots in keys

If your array key literally contains a dot, escape it with a backslash (.).

1$settings = [
2 'cache' => [
3 'driver.name' => 'redis',
4 ],
5];
6 
7data_get($settings, 'cache.driver\.name'); // 'redis'

If your key string is already segmented, you can also pass an array of segments instead of a dot string:

1data_get($settings, ['cache', 'driver.name']); // equivalent

Using data_get() with Collections

Collections are iterable, but data_get() expects arrays/objects for traversal. The simplest approach is to call ->all() on the collection when it represents a list. For object-like collections (e.g., keyed by strings), data_get() works with the underlying array.

1$users = collect([
2 ['name' => 'Ada', 'email' => '[email protected]'],
3 ['name' => 'Linus', 'email' => '[email protected]'],
4]);
5 
6data_get($users->all(), '*.email');

For nested collections, convert the relevant layers with ->all() as needed, or map with data_get() per item:

1$teams = collect([
2 ['members' => collect([['name' => 'A1'], ['name' => 'A2']])],
3]);
4 
5$names = $teams->map(fn ($team) => data_get($team, 'members.*.name'));
6// $names: collect([['A1', 'A2']])

data_get() vs Arr::get() vs PHP’s nullsafe operator

  • data_get()

    • Works with arrays, objects, ArrayAccess, and mixes of these.
    • Supports wildcards (*), escaped dots, and closure defaults.
    • Great for traversing heterogeneous data (API responses, decoded JSON, Eloquent models, DTOs).
  • Arr::get()

    • Focused on arrays/ArrayAccess. No wildcard expansion.
    • If you only work with arrays and want a slightly smaller surface area, Arr::get() is fine.
  • PHP nullsafe operator (->?)

    • Reads object properties/methods, but doesn’t handle arrays or wildcards.
    • Great for straight object graphs; less helpful for mixed array/object structures.

Often, data_get() replaces nested null coalescing and complex conditionals in one readable call.

Common patterns and real-world examples

Reading API responses

1$response = Http::get('https://api.example.com/user')->json();
2 
3$name = data_get($response, 'profile.name');
4$firstPostTitle = data_get($response, 'posts.0.title');
5$allTagNames = collect(data_get($response, 'posts.*.tags.*.name'))->flatten()->all();

Reading Eloquent models safely

1// Given $order is an Eloquent model with nested relations
2$city = data_get($order, 'shippingAddress.city', 'Unknown');
3$lineItemsSkus = data_get($order, 'items.*.sku', []);

Transforming data for views

1$payload = [
2 'users' => [
3 ['name' => 'Ada', 'roles' => [['name' => 'admin'], ['name' => 'editor']]],
4 ['name' => 'Linus', 'roles' => [['name' => 'viewer']]],
5 ],
6];
7 
8$userRoles = collect(data_get($payload, 'users.*.roles.*.name'))
9 ->map(fn ($roles) => collect($roles)->sort()->values()->all())
10 ->all();
11// $userRoles => [['admin','editor'], ['viewer']]

Guards for optional config

1$dsn = data_get(config('services.sentry'), 'dsn');
2$retries = data_get(config('queue.connections.redis'), 'retry_after', 90);

Edge cases and gotchas

  • Non-existent intermediate segments short-circuit to default.
    • Example: data_get($user, 'profile.timezone', 'UTC') returns 'UTC' if profile is null or missing.
  • Wildcards preserve structure depth.
    • Use Collection::flatten() (or array flattening) if you need a single list.
  • Numeric indices are valid segments.
    • data_get($posts, '0.title') fetches the first item’s title.
  • Don’t confuse property access with method calls.
    • data_get() does not execute methods; it reads properties/ArrayAccess.
  • Escaping matters for literal dots in keys.
    • Use driver.name or pass segments as an array.

Related helpers: data_set() and data_fill()

While data_get() reads values, its siblings write values into nested structures.

  • data_set(&$target, $key, $value, $overwrite = true)
    • Sets a value at the given path, creating intermediate arrays/objects as needed.
    • Respects the overwrite flag.
  • data_fill(&$target, $key, $value)
    • Like data_set() but only sets the value if it is missing.

Example:

1$user = [];
2 
3data_set($user, 'profile.timezone', 'UTC');
4// $user = ['profile' => ['timezone' => 'UTC']]
5 
6$data = [];
7data_fill($data, 'a.b', 123);
8// $data = ['a' => ['b' => 123]]

Final tips

  • Prefer data_get() when input shape is uncertain (API, untrusted payloads, optional relations).
  • Use closure defaults for lazy or environment-based fallbacks.
  • Combine wildcards with Collections for powerful transformations.
  • Escape keys with dots using \. or supply key segments as an array.

With data_get(), your code becomes safer and more expressive, reducing the need for repetitive null checks and nested conditionals.

Read next

Introducing Laravel Authentication Log

Laravel Authentication Log is a package which tracks your user's authentication information such as login/logout time, IP, Browser, Location, etc. as well as sends out notifications via mail, slack, or sms for new devices and failed logins.

AR
1 min read - 12,293 views -