divineomega/array_undot
array_undot expands dot-notation keys back into nested PHP arrays, the inverse of Laravel’s array_dot helper. Turn ['user.name' => 'Jane'] into ['user' => ['name' => 'Jane']] for configs, form data, and validation. Lightweight with a simple API.
array_undot is a single-purpose, framework-agnostic helper: it takes a flat array with dot-notation keys and expands it into a nested multi-dimensional array. It is the inverse of Laravel's array_dot() (and PHP's Arr::dot()).
Install:
composer require divineomega/array_undot
Requires Composer and a supported PHP version (check composer.json — the package is tiny and pinned to a modern PHP range). There is no service provider, no config file, and nothing to register in Laravel's config/app.php. The global function is loaded via Composer's files autoload, so require 'vendor/autoload.php' is all you need.
First use case — round-tripping a flattened config:
use DivineOmega\ArrayUndot\ArrayUndot;
$flat = [
'database.connections.mysql.host' => '127.0.0.1',
'database.connections.mysql.port' => 3306,
'database.connections.mysql.database' => 'app',
];
$nested = array_undot($flat);
// or, if you prefer to avoid the global function:
$nested = ArrayUndot::undot($flat);
// [
// 'database' => [
// 'connections' => [
// 'mysql' => ['host' => '127.0.0.1', 'port' => 3306, 'database' => 'app'],
// ],
// ],
// ]
Where to look first: the README and the one class in src/. There is essentially no API surface beyond undot() — read the source before relying on edge-case behaviour, because that is where the real specification lives.
1. Persist flattened config, rehydrate on read. The most common use: store settings in a single flat table/column with dot keys, then expand at boot.
$settings = Settings::pluck('value', 'key')->all(); // dotted keys
config(['app' => array_undot($settings)]);
2. Layer user overrides on top of defaults. Undot the override set first, then merge structurally — plain array_merge on dotted keys gives you the wrong result.
$merged = array_replace_recursive($defaults, array_undot($overrides));
3. Rebuilding payloads from form fields. Browsers and JSON-flattening tools happily send addresses.0.city / user.profile.bio. Undot gives you a request-shaped array.
$payload = array_undot($request->only(['name', 'addresses.0.city', 'addresses.0.zip']));
4. Database rows as paths. Rows of (path, value) — CMS page settings, feature flags, translation keys — map cleanly onto undot.
$tree = array_undot($rows->pluck('value', 'path')->all());
5. Cache round-trip. Flatten a big nested config with array_dot() before writing to Redis, array_undot() after reading. Keeps the cached format flat and greppable.
6. Wrap it, don't scatter it. Because it is a bare global function, define a thin service (ConfigExpander::expand(array $flat): array) around it in larger codebases. You get one seam to swap in a custom implementation, add validation, or log malformed input.
Integration tips:
data_get()/Arr::get() on the read side so the nesting depth stays an implementation detail.Conflicting keys are order-dependent. If the input contains both a parent and a child path, one silently wins:
array_undot(['a' => 1, 'a.b' => 2]); // 'a' becomes an array; scalar 1 is lost
Nothing warns you. If your input can contain both (e.g. a settings table with a "group default" row and per-key rows), filter or sort deterministically before calling.
Literal dots in keys cannot be escaped. A key that genuinely contains a . — a domain name, a file extension, a version string — will be split. array_dot() has the mirror problem, so it is not a bug in this package, but it will bite you on keys you don't control (user-defined translation keys, metric names).
Numeric segments need verification. Whether 'items.0.name' produces ['items' => [0 => ['name' => ...]]] (int key) or ['items' => ['0' => ...]] (string key) matters for assertEquals vs assertSame and for json_encode output. Check the source and pin it with a test; PHP's loose array-key coercion helps, but strict comparisons won't.
The round-trip is not always lossless. array_dot() on an empty nested array produces nothing, so array_undot(array_dot($x)) can drop empty branches. Don't use the pair to assert "unchanged data" in tests without accounting for that.
Trailing / leading dots. 'a.b.' or '.a' will create odd empty-string segments rather than throwing. Validate key format upstream if input is user-supplied.
No config, no extension points. There is nothing to publish, no separator option, no escaping strategy, no callback hook. If you need any of that, don't try to configure the package — write your own 20-line undot (or wrap this one and pre-process the keys). Given the LGPL-3.0 licence, vendoring/reimplementing for a closed-source product is worth a conversation with legal rather than an assumption.
Global function name collision. If another dependency also defines array_undot(), Composer's autoload order decides which one wins, and the failure is a fatal redeclare. Prefer ArrayUndot::undot() in libraries; use the global function only in application code.
Debugging. The fastest loop is dd(array_undot($flat)) on a two-key subset. To verify a suspected lossy round-trip, diff array_dot(array_undot($flat)) against your original $flat — the keys that disappear are your conflicts or empty branches.
Small package, low activity (24 stars). The upside: it is ~100 lines and easy to audit. The downside: no maintenance guarantees, sparse docs, edge cases documented only by the tests. Write a handful of characterization tests around the specific inputs your app produces (numeric keys, conflicting paths, dotted literals) so a future PHP or package change surfaces loudly instead of corrupting nested config at runtime.
How can I help you explore Laravel packages today?