DEV Community

Cover image for Handling Nested PHP Arrays Using DataBlock
Roberto B.
Roberto B.

Posted on • Edited on

Handling Nested PHP Arrays Using DataBlock

PHP DataBlock is designed to improve the developer experience when working with complex and deeply nested PHP arrays.

If you work with PHP long enough, you will eventually fight an array.

Not because arrays are bad, but because nested, dynamic arrays are everywhere:

  • API responses
  • Configuration files
  • Decoded JSON or YAML
  • Shared data structures with third-party libraries

And they usually come with the same problems:

  • Deep isset() chains
  • Missing keys causing subtle bugs
  • Manual type casting
  • Unclear defaults

This is where DataBlock comes in.

GitHub repo & installation:
https://github.com/Hi-Folks/data-block

What is DataBlock?

DataBlock is a small PHP library that wraps arrays into a safer, more expressive object called a Block.

The DataBlock PHP library doesn’t replace arrays; it improves how you access, read, and handle them.

With DataBlock, you get:

  • Dot-notation access to nested data
  • Typed getters (getInt(), getString(), etc.)
  • Safe defaults
  • Better readability
  • Easy data inspection and export

Let’s see why that matters.

Installing and using the Block class

To add DataBlock to your projects and start using the Block class with its methods and helpers, you can run the composer require command:

composer require hi-folks/data-block
Enter fullscreen mode Exit fullscreen mode

To support the development, you can "star" ⭐ the repository: https://github.com/Hi-Folks/data-block

Then, in your PHP files, you can import the HiFolks\DataType\Block Namespace:

use HiFolks\DataType\Block;
Enter fullscreen mode Exit fullscreen mode

The simple example dataset

For the examples included in this article, we are going to use a small dataset representing fruits:

use HiFolks\DataType\Block;

$fruitsArray = [
    "avocado" => [
        "name" => "Avocado",
        "fruit" => "🥑",
        "wikipedia" => "https://en.wikipedia.org/wiki/Avocado",
        "color" => "green",
        "rating" => 8,
    ],
    "apple" => [
        "name" => "Apple",
        "fruit" => "🍎",
        "wikipedia" => "https://en.wikipedia.org/wiki/Apple",
        "color" => "red",
        "rating" => 7,
    ],
    "banana" => [
        "name" => "Banana",
        "fruit" => "🍌",
        "wikipedia" => "https://en.wikipedia.org/wiki/Banana",
        "color" => "yellow",
        "rating" => 8.5,
    ],
    "cherry" => [
        "name" => "Cherry",
        "fruit" => "🍒",
        "wikipedia" => "https://en.wikipedia.org/wiki/Cherry",
        "color" => "red",
        "rating" => 9,
    ],
];

$data = Block::make($fruitsArray);
Enter fullscreen mode Exit fullscreen mode

Once the array is wrapped in a Block object, the focus shifts.
Instead of worrying about how the data is structured and whether every key exists, you can concentrate on what you want to read from it.

Safe access without isset()

Accessing nested arrays in plain PHP often ends up looking like this:

$color = $array['avocado']['color'] ?? null;
Enter fullscreen mode Exit fullscreen mode

This works, but it doesn’t scale very well.
As soon as the data becomes more complex or dynamic, these checks start spreading across the codebase, making it harder to read and maintain.

With DataBlock, accessing nested values becomes more direct and expressive:

$data->get("avocado.color");
Enter fullscreen mode Exit fullscreen mode

You can safely retrieve values using dot notation, even when the structure is deeply nested:

var_dump(
    $data->get("avocado.color"),
    $data->get("avocado.rating"),
    $data->get("banana.rating"),
    $data->get("apple.notexists"),
);
Enter fullscreen mode Exit fullscreen mode

Output:

string(5) "green"
int(8)
float(8.5)
NULL
Enter fullscreen mode Exit fullscreen mode

Missing keys are handled gracefully, without triggering warnings or notices.
There’s no need for defensive isset() checks or fallback logic scattered throughout the code, just clear, predictable access to the data you need.

Typed getters and defaults

Arrays, by nature, don’t express intent.
When you read a value from an array, it’s not always clear what type that value is supposed to be, or what should happen if it’s missing.

DataBlock makes this explicit.

Instead of retrieving a value and then casting or validating it later, DataBlock allows you to express your expectations directly at the point of access:

var_dump(
    $data->getInt("avocado.rating"),
    $data->getInt("banana.rating"),
    $data->getFloat("banana.rating"),
    $data->get("apple.notexists", "MY_DEFAULT"),
);
Enter fullscreen mode Exit fullscreen mode

Output:

int(8)
int(8)
float(8.5)
string(10) "MY_DEFAULT"
Enter fullscreen mode Exit fullscreen mode

Here, the method name clearly communicates the expected type.
A numeric value is automatically cast to an integer or a float, while missing fields can fall back to a sensible default, all in a single, readable expression.

Why this matters:

  • getInt() and getFloat() make your intent explicit and self-documenting
  • Type casting happens consistently and close to the data source
  • Default values are handled inline, without extra conditional logic

This approach is especially valuable when working with external or untrusted data, such as API responses, configuration files, or decoded JSON, where types and the presence of fields cannot always be guaranteed.

Working with subsets of data

In some cases, you don’t just need a single value; you want to work with an entire section of the dataset.
DataBlock allows you to retrieve a whole branch of the structure in a straightforward way:

$avocado = $data->get("avocado");
Enter fullscreen mode Exit fullscreen mode

In this case, you receive the underlying array:

[
  "name" => "Avocado",
  "fruit" => "🥑",
  "wikipedia" => "...",
  "color" => "green",
  "rating" => 8
]
Enter fullscreen mode Exit fullscreen mode

This is useful when you want to pass the data to existing code that expects a plain PHP array.

Alternatively, if you want to keep working within the DataBlock API, you can retrieve the same branch as a Block instance:

$avocadoBlock = $data->getBlock("avocado");
Enter fullscreen mode Exit fullscreen mode

By using the getBlock() method, you retain all the benefits of DataBlock, dot notation for nested access, typed getters, and the ability to inspect available keys, while focusing only on the portion of the data you’re interested in.

This makes it easy to navigate complex structures step by step, without losing context or switching mental models.

Discovering what’s inside your data

When dealing with dynamic or loosely defined data structures, simply knowing what keys are available can make a big difference.
DataBlock provides a simple way to inspect the structure of your dataset by listing its keys:

$data->keys();
Enter fullscreen mode Exit fullscreen mode

Result:

["avocado", "apple", "banana", "cherry"]
Enter fullscreen mode Exit fullscreen mode

This provides an immediate overview of the top-level elements, which is especially helpful when working with data from external sources.

The same approach also works for nested data. You can inspect the keys of a specific branch by retrieving it as a Block:

$data->getBlock("avocado")->keys();
Enter fullscreen mode Exit fullscreen mode

Result:

["name", "fruit", "wikipedia", "color", "rating"]
Enter fullscreen mode Exit fullscreen mode

Being able to explore the available keys at each level makes DataBlock particularly useful for debugging, data exploration, and building dynamic behaviors such as conditional rendering or automatic mappings.

Exporting data to YAML (and more)

Beyond reading and navigating data, DataBlock can also help with data representation and serialization.

In addition to JSON (method toJson()), DataBlock allows you to serialize your data into YAML, which is often easier to read and reason about, especially for configuration and inspection purposes:

echo $data->toYaml();
Enter fullscreen mode Exit fullscreen mode

Output:

avocado:
  name: Avocado
  fruit: 🥑
  wikipedia: https://en.wikipedia.org/wiki/Avocado
  color: green
  rating: 8
apple:
  name: Apple
  fruit: 🍎
  wikipedia: https://en.wikipedia.org/wiki/Apple
  color: red
  rating: 7
...
Enter fullscreen mode Exit fullscreen mode

Being able to convert a structured dataset into a human-readable or machine-readable format quickly is helpful in many everyday scenarios.
This includes generating configuration files, producing clear debug output, or sharing data with tools and systems that work naturally with YAML.

When should you use PHP DataBlock?

DataBlock is a great fit when:

  • You consume API responses.
  • You parse JSON/YAML
  • You deal with deeply nested arrays.
  • You want safer, clearer PHP code.

It doesn’t try to replace domain models; it simply makes raw data easier and safer to work with.

Arrays vs DataBlock vs DTOs: which one should you use?

When working with data in PHP, there are usually three common approaches:

  1. Plain PHP arrays
  2. DataBlock
  3. DTOs (Data Transfer Objects)

Each one has a purpose; the key is knowing when to use which.

1) Plain PHP Arrays

The default/first choice.
Arrays are flexible, fast, and built into the language.

$rating = $data['avocado']['rating'] ?? null;
Enter fullscreen mode Exit fullscreen mode

Pros

  • Native PHP feature
  • Zero dependencies
  • Very flexible

Cons

  • No type safety
  • Verbose access for nested data
  • Easy to introduce silent bugs
  • Hard to understand intent
  • Defensive code everywhere

Best for

  • Very small scripts
  • Simple, flat data
  • Performance-critical hot paths

Once data becomes nested or dynamic, arrays tend to leak complexity everywhere.

2) DataBlock

A safer abstraction over arrays.
DataBlock keeps the flexibility of arrays while adding structure and intent.

$rating = $data->getInt("avocado.rating");
Enter fullscreen mode Exit fullscreen mode

Pros

  • Safe nested access (no warnings)
  • Typed getters
  • Defaults built in
  • Highly readable
  • Great for untrusted or dynamic data

Cons

  • Still runtime-typed (not compile-time)
  • Not a domain model
  • Adds a small dependency

Best for

  • API responses
  • Configuration files
  • JSON / YAML parsing
  • Boundary layers (input/output)
  • Rapid prototyping without sacrificing safety

Think of DataBlock as a smart boundary object between raw data and your application logic.

3) DTOs (Data Transfer Objects)

The most explicit and strict option.
DTOs define exactly what your data looks like.

final class FruitDTO
{
    public function __construct(
        public string $name,
        public string $color,
        public float $rating
    ) {}
}
Enter fullscreen mode Exit fullscreen mode

Pros

  • Strong typing
  • IDE autocompletion
  • Self-documenting
  • Great for domain logic
  • Easier to refactor safely

Cons

  • Verbose
  • Requires mapping
  • Rigid structure
  • Overkill for dynamic data

Best for

  • Domain models
  • Business logic
  • Internal application state
  • Long-lived data structures

DTOs shine inside your application, where structure is stable, and rules matter.

The key difference is where you use them

Layer My opinionated recommendation
External input (API, JSON, YAML) DataBlock
Simple scripts Array
Domain logic DTO
Configuration DataBlock
Public interfaces DTO

Recap: why DataBlock exists

DataBlock doesn’t compete with DTOs; it complements them.

It lives in the uncomfortable middle ground where:

  • Data is messy
  • Structure is mostly known
  • Safety matters
  • Full modeling would be too heavy.

If arrays are too loose and DTOs are too strict, DataBlock is the pragmatic middle layer.

Final thoughts

PHP arrays are powerful, but they’re also easy to misuse.

DataBlock is designed to improve the developer experience when working with complex, nested, and structured datasets by providing a fluent and expressive way to access and handle data.

Less boilerplate, fewer bugs, more readable code.

Check it out on GitHub:
https://github.com/Hi-Folks/data-block

Top comments (3)

Collapse
 
xwero profile image
david duymelinck

The library looks like an http client and Laravel Collections had a child.
So for projects that are using Laravel, this library brings nothing new.
For the projects that use another framework and want the fluent API methods, they could add Collections, because most frameworks provide an http client.
What is left are projects that are only using libraries.

It is a well put together library, I think the features it brings are already solved with other solutions.

Collapse
 
robertobutti profile image
Roberto B.

Hi @xwero Thanks for the thoughtful comment! I actually agree with most of what you wrote.

I’ve never used DataBlock in a Laravel context myself, precisely because Laravel already provides excellent tools like Collections, Arr / Str helpers, and a very good HTTP client. In that ecosystem, DataBlock wouldn’t add much value, and adopting it wouldn’t really make sense.

Like most packages, DataBlock is very context-dependent.
The situations where I personally use it are quite different: plain PHP scripts, small tools built with Symfony Console, or lightweight projects where I don’t want to pull in a full framework just to get a fluent way of handling data.

In those cases, I often deal with:

  • deeply nested JSON responses
  • configuration files
  • YAML documents
  • structured arrays coming from external sources

DataBlock is not meant to compete with Laravel Collections.
Collections are great for working with lists of items, transformations, and pipelines. DataBlock focuses more narrowly on safe and expressive access to nested associative structures, typed getters, and inspection of arbitrary data trees.

In the article, I used a simple array-based example mainly to introduce the basic concepts and APIs that DataBlock provides. I’m aware that this can make the overlap with existing tools seem larger than it actually is. In future articles or examples, I plan to explore different scenarios that better reflect the contexts where I personally find DataBlock useful, such as handling nested JSON responses, configuration files, or YAML documents in small PHP tools or console applications.

So I see it less as “another Collection” and more as a small utility that sits at the boundary between raw data and application logic, especially outside of full-stack frameworks.

That said, you’re absolutely right: many of the problems it solves are already addressed by other tools, and adopting any library should always be a conscious decision based on the project’s needs and context.

Thanks again for the constructive feedback! Much appreciated 👍

Collapse
 
suckup_de profile image
Lars Moelleken • Edited

Interesting, I did something similar in the past and nowadays I would rename $data->getInt() into $data->getIntLax() (without a warning) and add a new $data->getInt() (with a php warning for missing keys) because it saved me so many times in debugging if I know what is broken. 😋