/dot

Use dot notation to access nested array keys and/or object properties

Primary LanguagePHPMIT LicenseMIT

Dot

Travis (.com) Latest Stable Version PHP Version Support License Total Downloads

Dot allows you to get & set array keys or object properties using dot notation.

Installing

composer require noj/dot

Usage

First construct a new Dot instance:

$dot = new Dot($data);
$dot = Dot::from($data); // alternative

All the examples are using the following data structure unless otherwise specified:

$data = [
    'groups' => [
        (object)[
            'name' => 'group1',
            'items' => [
                [
                    'name' => 'item1',
                    'rare' => false,
                ],
                [
                    'name' => 'item3',
                    'rare' => true,
                ],
            ]
        ],
        (object)[
            'name' => 'group2',
            'items' => []
        ],
        (object)[
            'name' => 'group3',
            'items' => [
                [
                    'name' => 'item2',
                    'rare' => true,
                ],
            ]
        ],
    ]
];

Methods

Dot::count(string $path): int

Count the number of items at a given path.

$dot->count('groups.0.items'); // 2
$dot->count('groups.*.items'); // 3

Dot::find(string $path, mixed $equals): Dot

Find items that pass the given truth test.

// find where property === value
$dot->find('groups.*.items.*.rare', true)->get();
/*
[
    ['name' => 'item3', 'rare' => true],
    ['name' => 'item2', 'rare' => true],
]
*/

// pass a callback for custom comparisons 
$dot->find('groups.*.items.*.name', function (string $name) {
    return $name === 'item2' || $name === 'item3';
})->get(); // returns same as above

// leave off the property to receive the whole item
$dot->find('groups.*.items.*', function (array $item) {
    return $item['name'] === 'item3' && $item['rare'];
})->get();

Dot::first(string $path, mixed $equals): Dot

Find the first item that passes the given truth test.

$dot->first('groups.*.items.*.rare', true)->get(); // ['name' => 'item3', 'rare' => true]

$dot->first('groups.*.items.*', function (array $item) {
    return $item['rare'] === true;
})->get(); // same as above

Dot::get(null|int|string $path): mixed

Access nested array keys and object properties using dot syntax:

$dot->get('groups.0.items.1.name'); // 'item3'

Dot safely returns null if the key or property doesn't exist:

$dot->get('groups.3.items.1.name'); // null

You can use a wildcard * to pluck values from multiple paths:

$dot->get('groups.*.items.*.name'); // ['item1', 'item3', 'item2']

$dot->get('groups.*.items'); /* [
    ['name' => 'item1', 'rare' => false],
    ['name' => 'item3', 'rare' => true],
    ['name' => 'item2', 'rare' => true],
] */

You can call functions using the @ prefix:

$data = [
    'foo' => new class {
        public function getBar() {
            return ['bar' => 'value'];
        }
    }
];

(new Dot($data))->get('foo.@getBar.bar'); // 'value'

If no argument is passed it will return the underlying data:

$dot->get() === $data; // true

Dot::has(string $path): bool

Returns true if path exists, false otherwise:

$dot->has('groups.0.items.1.name'); // true

Dot::push(string $path, mixed $value): Dot

Push a value onto an existing array:

$dot->push('groups.0.items', ['name' => 'another item']);

// supports wildcards
$dot->push('groups.*.items', ['name' => 'another item']);

Dot::set(array|string $paths, mixed $value): void

You can set nested values using the same syntax:

$dot->set('groups.2.items.0.name', 'a different name');
echo $data['groups'][2]->items[0]['name']; // 'a different name'

Set nested keys from multiple paths using a wildcard *:

$dot->set('groups.*.items.*.name', 'set all to same name');

Keys will be created if they don't already exist:

$dot->set('groups.0.items.2.name', 'a new item');

By default, set will initialise missing values as empty arrays. To indicate that something should be an object use the -> delimiter:

$dot->set('groups.3->items.2.name', 'a new item');

You can set multiple values at once by passing an array:

$dot->set([
    'groups.0.items.1.name' => 'something',
    'groups.2.items.0.name' => 'something else',
]):

You can call a method:

$data = [
    'foo' => new class {
        public $bars = [];
        public function addBar($bar) {
            $this->bar[] = $bar;
        }
    }
];

$dot = new Dot($data);
$dot->set('foo.@addBar', 'value');
echo $data['foo']->bars; // ['value']

Or call a method for each value of an array:

$dot->set('foo.@addBar*', ['value1', 'value2']);
echo $data['foo']->bars; // ['value1', 'value2']

Non-chained versions

All methods have non-chained versions of themselves as a standalone function, i.e:

// instead of
$filtered = \Noj\Dot\Dot::from($data)
    ->find('groups.*.items.*.rare', true)
    ->get();

// you can do
$filtered = \Noj\Dot\find($data, 'groups.*.items.*.rare', true');