Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
data_dot() and data_has() helper function compliments to PHP framework Laravel. Supports stdClass, Collection, or Arrayable objects along with standard PHP arrays. The intention is to allow nested wildcard pattern matching on structured data.
{
"name": "your-project",
"autoload": {
"psr-4": {
"App\\": "app/"
},
"files": [
"app/helpers.php",
]
}
}
<?php
// app/helpers.php
// Why don't you let us order files, Gist?
use Illuminate\Support\Arr;
if (!function_exists('data_dot')) {
/**
* Flatten a multi-dimensional object with dots.
*
* @param array|ArrayAccess|object $object
* @param string $prepend
*
* @return array
*/
function data_dot($object, $prepend = '')
{
$results = [];
if (Arr::accessible($object)) {
$array = $object;
} elseif (is_object($object)) {
$array = get_object_vars($object);
} else {
$array = [];
}
foreach ($array as $key => $value) {
if (is_array($value) && !empty($value)) {
$results = array_merge($results, data_dot($value, $prepend . $key . '.'));
} elseif ($value instanceof \Illuminate\Support\Collection) {
$results = array_merge($results, data_dot($value->all(), $prepend . $key . '.'));
} elseif (is_object($value) && !empty($props = get_object_vars($value))) {
$results = array_merge($results, data_dot($props, $prepend . $key . '.'));
} else {
$results[$prepend . $key] = $value;
}
}
return $results;
}
}
if (!function_exists('data_has')) {
/**
* Find if there is an item in an array or object using "dot" notation.
*
* @param mixed $target
* @param string|array $keys
*
* @return bool
*/
function data_has($target, $keys)
{
if (is_null($keys)) {
return false;
}
$keys = (array) $keys;
if (!$target) {
return false;
}
if ($keys === []) {
return false;
}
foreach ($keys as $i => $key) {
$subKeyTarget = $target;
if (Arr::accessible($subKeyTarget) && Arr::exists($subKeyTarget, $key)) {
continue;
}
if (is_object($subKeyTarget) && Arr::exists(get_object_vars($subKeyTarget), $key)) {
continue;
}
foreach (explode('.', $key) as $segment) {
if ($segment === '*') {
if ($subKeyTarget instanceof Collection) {
$subKeyTarget = $subKeyTarget->all();
} elseif (!is_array($subKeyTarget)) {
return false;
}
if (empty($key)) {
return true;
}
return array_reduce($subKeyTarget, function ($present, $item) use ($keys, $i) {
return $present || data_has($item, array_slice($keys, $i + 1));
}, false);
}
if (Arr::accessible($subKeyTarget) && Arr::exists($subKeyTarget, $segment)) {
$subKeyTarget = $subKeyTarget[$segment];
} elseif (is_object($subKeyTarget) && isset($subKeyTarget->{$segment})) {
$subKeyTarget = $subKeyTarget->{$segment};
} else {
return false;
}
}
}
return true;
}
}
<?php
class HelpersTest extends TestCase
{
public function testDataDot()
{
$array = data_dot(['foo' => ['bar' => 'baz']]);
$this->assertEquals(['foo.bar' => 'baz'], $array);
$array = data_dot([]);
$this->assertEquals([], $array);
$array = data_dot(['foo' => []]);
$this->assertEquals(['foo' => []], $array);
$array = data_dot(['foo' => ['bar' => []]]);
$this->assertEquals(['foo.bar' => []], $array);
$array = data_dot((object) ['foo' => (object) ['bar' => 'baz']]);
$this->assertEquals(['foo.bar' => 'baz'], $array);
$object = data_dot(new stdClass);
$this->assertEquals([], $object);
$object = data_dot((object) ['foo' => []]);
$this->assertEquals(['foo' => []], $object);
$object = data_dot((object) ['foo' => (object) ['bar' => []]]);
$this->assertEquals(['foo.bar' => []], $object);
$object = data_dot((object) ['foo' => ['bar' => [$item = new stdClass]]]);
$this->assertEquals(['foo.bar.0' => $item], $object);
$collection = collect([1, 2]);
$this->assertEquals([1, 2], data_dot($collection));
$collection = collect(['zero' => 1, 'one' => 2]);
$this->assertEquals(['zero' => 1, 'one' => 2], data_dot($collection));
}
public function testDataHas()
{
$array = ['products.desk' => ['price' => 100]];
$this->assertTrue(data_has($array, 'products.desk'));
$array = ['products' => ['desk' => ['price' => 100]]];
$this->assertTrue(data_has($array, 'products.desk'));
$this->assertTrue(data_has($array, 'products.desk.price'));
$this->assertFalse(data_has($array, 'products.foo'));
$this->assertFalse(data_has($array, 'products.desk.foo'));
$array = ['foo' => null, 'bar' => ['baz' => null]];
$this->assertTrue(data_has($array, 'foo'));
$this->assertTrue(data_has($array, 'bar.baz'));
$array = new ArrayObject(['foo' => 10, 'bar' => new ArrayObject(['baz' => 10])]);
$this->assertTrue(data_has($array, 'foo'));
$this->assertTrue(data_has($array, 'bar'));
$this->assertTrue(data_has($array, 'bar.baz'));
$this->assertFalse(data_has($array, 'xxx'));
$this->assertFalse(data_has($array, 'xxx.yyy'));
$this->assertFalse(data_has($array, 'foo.xxx'));
$this->assertFalse(data_has($array, 'bar.xxx'));
$array = new ArrayObject(['foo' => null, 'bar' => new ArrayObject(['baz' => null])]);
$this->assertTrue(data_has($array, 'foo'));
$this->assertTrue(data_has($array, 'bar.baz'));
$array = ['foo', 'bar'];
$this->assertFalse(data_has($array, null));
$this->assertFalse(data_has(null, 'foo'));
$this->assertFalse(data_has(false, 'foo'));
$this->assertFalse(data_has(null, null));
$this->assertFalse(data_has([], null));
$array = ['products' => ['desk' => ['price' => 100]]];
$this->assertTrue(data_has($array, ['products.desk']));
$this->assertTrue(data_has($array, ['products.desk', 'products.desk.price']));
$this->assertTrue(data_has($array, ['products', 'products']));
$this->assertFalse(data_has($array, ['foo']));
$this->assertFalse(data_has($array, []));
$this->assertFalse(data_has($array, ['products.desk', 'products.price']));
$this->assertFalse(data_has([], [null]));
$this->assertFalse(data_has(null, [null]));
$object = (object) ['products.desk' => ['price' => 100]];
$this->assertTrue(data_has($object, 'products.desk'));
$object = (object) ['products' => ['desk' => ['price' => 100]]];
$this->assertTrue(data_has($object, 'products.desk'));
$this->assertTrue(data_has($object, 'products.desk.price'));
$this->assertFalse(data_has($object, 'products.foo'));
$this->assertFalse(data_has($object, 'products.desk.foo'));
$object = (object) ['foo' => null, 'bar' => ['baz' => null]];
$this->assertTrue(data_has($object, 'foo'));
$this->assertTrue(data_has($object, 'bar.baz'));
$collection = collect(['foo' => 10, 'bar' => new ArrayObject(['baz' => 10])]);
$this->assertTrue(data_has($collection, 'foo'));
$this->assertTrue(data_has($collection, 'bar'));
$this->assertTrue(data_has($collection, 'bar.baz'));
$this->assertFalse(data_has($collection, 'xxx'));
$this->assertFalse(data_has($collection, 'xxx.yyy'));
$this->assertFalse(data_has($collection, 'foo.xxx'));
$this->assertFalse(data_has($collection, 'bar.xxx'));
$collection = collect(['foo' => null, 'bar' => new ArrayObject(['baz' => null])]);
$this->assertTrue(data_has($collection, 'foo'));
$this->assertTrue(data_has($collection, 'bar.baz'));
$object = (object) ['foo', 'bar'];
$this->assertFalse(data_has($object, null));
$this->assertFalse(data_has(null, null));
$this->assertFalse(data_has(new stdClass, null));
$object = (object) ['products' => ['desk' => ['price' => 100]]];
$this->assertTrue(data_has($object, ['products.desk']));
$this->assertTrue(data_has($object, ['products.desk', 'products.desk.price']));
$this->assertTrue(data_has($object, ['products', 'products']));
$this->assertFalse(data_has($object, ['foo']));
$this->assertFalse(data_has($object, []));
$this->assertFalse(data_has($object, ['products.desk', 'products.price']));
$this->assertFalse(data_has([], [null]));
$this->assertFalse(data_has(null, [null]));
$object = (object) ['users' => ['name' => ['Taylor', 'Otwell']]];
$array = [(object) ['users' => [(object) ['name' => 'Taylor']]]];
$dottedArray = ['users.first_name' => 'Taylor', 'users.middle_name' => null];
$arrayAccess = new SupportTestArrayAccess(['price' => 56, 'user' => new SupportTestArrayAccess(['name' => 'John']), 'email' => null]);
$this->assertTrue(data_has($object, 'users.name.0'));
$this->assertTrue(data_has($array, '0.users.0.name'));
$this->assertFalse(data_has($array, '0.users.3'));
$this->assertTrue(data_has($dottedArray, ['users.first_name', 'users.middle_name']));
$this->assertFalse(data_has($dottedArray, ['users.first_name', 'users.last_name']));
$this->assertTrue(data_has($arrayAccess, 'price'));
$this->assertTrue(data_has($arrayAccess, 'user.name'));
$this->assertFalse(data_has($arrayAccess, 'foo'));
$this->assertFalse(data_has($arrayAccess, 'user.foo'));
$this->assertFalse(data_has($arrayAccess, 'email'));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment