Exploring Real-World APIs with DataBlock
Source: Dev.to
What we’ll use
- GitHub API – repository statistics
- Packagist API – download statistics and popular packages
- Raw
composer.jsonfiles from GitHub
All requests will be made with Symfony HTTP Client, letting DataBlock wrap the responses.
Required packages
composer require hi-folks/data-block symfony/http-client
- DataBlock – completely framework‑agnostic; works with plain PHP, Symfony, or any other setup.
- Symfony HTTP Client – implements
Symfony\Contracts\HttpClient\HttpClientInterface.
Fetching JSON with DataBlock
DataBlock provides a convenient helper for HTTP JSON sources:
Block::fromHttpJsonUrl(string $url, HttpClientInterface $client);
Why this design matters
- Interchangeable client – any implementation of
HttpClientInterfaceworks (Symfony HttpClient, mocked clients, custom implementations). - Testable code – you can inject a fake client.
- Decoupled – no hard‑coded dependency on a concrete client.
- Framework‑agnostic – DataBlock stays independent of Symfony.
Creating the HTTP client
use HiFolks\DataType\Block;
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create([
'headers' => [
'User-Agent' => 'PHP DataBlock',
'Accept' => 'application/json',
],
]);
You can now pass this client directly to DataBlock:
Block::fromHttpJsonUrl($url, $httpClient);
The concrete example: Symfony vs. Laravel
We’ll query two public ecosystems:
| Source | Endpoint |
|---|---|
| GitHub – Symfony repo | https://api.github.com/repos/symfony/symfony |
| GitHub – Laravel repo | https://api.github.com/repos/laravel/framework |
| Packagist – Symfony stats | https://packagist.org/packages/symfony/symfony/stats.json |
| Packagist – Laravel stats | https://packagist.org/packages/laravel/framework/stats.json |
Each endpoint returns a large JSON document with many nested fields – perfect for demonstrating DataBlock.
Fetching the data
// GitHub data
$symfonyGithub = Block::fromHttpJsonUrl(
'https://api.github.com/repos/symfony/symfony',
$httpClient,
);
$laravelGithub = Block::fromHttpJsonUrl(
'https://api.github.com/repos/laravel/framework',
$httpClient,
);
// Packagist stats
$symfonyStats = Block::fromHttpJsonUrl(
'https://packagist.org/packages/symfony/symfony/stats.json',
$httpClient,
);
$laravelStats = Block::fromHttpJsonUrl(
'https://packagist.org/packages/laravel/framework/stats.json',
$httpClient,
);
From this point on we never touch raw arrays – DataBlock handles all navigation and casting.
Displaying GitHub statistics
// Symfony
echo "Symfony GitHub:\n";
echo "Stars: " . $symfonyGithub->getInt('stargazers_count') . "\n";
echo "Forks: " . $symfonyGithub->getInt('forks_count') . "\n";
echo "Open issues: " . $symfonyGithub->getInt('open_issues_count') . "\n";
echo "Main language: " .
$symfonyGithub->getString('language', 'unknown') . "\n";
// Laravel
echo "Laravel GitHub:\n";
echo "Stars: " . $laravelGithub->getInt('stargazers_count') . "\n";
echo "Forks: " . $laravelGithub->getInt('forks_count') . "\n";
echo "Open issues: " . $laravelGithub->getInt('open_issues_count') . "\n";
echo "Main language: " .
$laravelGithub->getString('language', 'unknown') . "\n";
No isset() checks, no PHP warnings, no manual casting – just clear intent.
Displaying Packagist download statistics
Packagist’s JSON nests download numbers under a downloads object. With plain arrays you’d need multiple isset() checks:
$data['downloads']['total'];
$data['downloads']['monthly'];
$data['downloads']['daily'];
DataBlock lets us use dot notation:
// Symfony downloads
echo "Symfony downloads:\n";
echo "Total: " . $symfonyStats->getInt('downloads.total') . "\n";
echo "Monthly: " . $symfonyStats->getInt('downloads.monthly') . "\n";
echo "Daily: " . $symfonyStats->getInt('downloads.daily') . "\n";
// Laravel downloads
echo "Laravel downloads:\n";
echo "Total: " . $laravelStats->getInt('downloads.total') . "\n";
echo "Monthly: " . $laravelStats->getInt('downloads.monthly') . "\n";
echo "Daily: " . $laravelStats->getInt('downloads.daily') . "\n";
Again, no fragile isset() scaffolding – DataBlock guarantees safe access and proper type casting.
Takeaways
- DataBlock abstracts away the boiler‑plate of navigating deeply nested JSON structures.
- Symfony HttpClient provides a standards‑compliant, PSR‑compatible HTTP layer that works seamlessly with DataBlock.
- By separating transport (the HTTP client) from data handling (DataBlock), your code stays clean, testable, and framework‑agnostic.
Feel free to swap the Symfony client for any other implementation of HttpClientInterface; the rest of the example will work unchanged. Happy coding!
Working with JSON via Dot‑Notation
echo $elStats->getInt("downloads.daily") . "\n";
What’s nice here is that the shape of the JSON almost disappears from the code.
You don’t have to care how deeply nested the structure is; you simply describe the path to the value you want.
- Dot notation turns a deeply‑nested document into something that feels flat, readable, and easy to reason about, while still remaining safe and type‑aware.
Fetching composer.json Files Directly from GitHub
$symfonyComposer = Block::fromHttpJsonUrl(
"https://raw.githubusercontent.com/symfony/symfony/master/composer.json",
$httpClient,
);
$laravelComposer = Block::fromHttpJsonUrl(
"https://raw.githubusercontent.com/laravel/framework/master/composer.json",
$httpClient,
);
Getting Typed (string) Values with getString()
echo "Symfony PHP requirement: " .
$symfonyComposer->getString("require.php") .
"\n";
echo "Laravel PHP requirement: " .
$laravelComposer->getString("require.php") .
"\n";
Exploring Dependencies
$symfonyRequires = $symfonyComposer->getBlock("require");
$laravelRequires = $laravelComposer->getBlock("require");
print_r($symfonyRequires->keys());
print_r($laravelRequires->keys());
This is structured document exploration, not just plain array access.
Working with Collections of Structured Items
The Packagist endpoint we’re using returns a list of the 100 most popular packages, i.e. a nested dataset rather than a single object.
$url = "https://packagist.org/explore/popular.json?per_page=100";
$popularPackages = Block::fromHttpJsonUrl($url, $httpClient)
->getBlock("packages");
Selecting Packages Related to Symfony and Laravel
$symfonyPopularPackages = $popularPackages
->where("name", Operator::LIKE, "symfony")
->orderBy("favers", "desc");
$laravelPopularPackages = $popularPackages
->where("name", Operator::LIKE, "laravel")
->orderBy("favers", "desc");
where() Method
Filters a dataset based on a condition:
->where(field, operator, value)
Example
->where("name", Operator::LIKE, "symfony")
Keeps only the items where the name field contains the word “symfony”.
Each item in $popularPackages is itself a structured object (a package entry), and DataBlock applies the condition to all of them.
Operator Enum
Defines how the comparison should be performed:
| Enum | Meaning |
|---|---|
Operator::EQUAL | Equality |
Operator::NOT_EQUAL | Inequality |
Operator::GREATER_THAN | Greater than |
Operator::LESS_THAN | Less than |
Operator::LIKE | String‑contains (used here) |
| …and others… |
Using an enum instead of raw strings makes the API:
- Safer – no typos
- More explicit – intent is clear
- Easier to discover – IDE autocomplete works
orderBy() Method
After filtering, sort the result:
->orderBy("favers", "desc")
Sorts the remaining items by the favers field in descending order, so the most‑starred packages appear first.
Because the result of where() and orderBy() is still a Block, you can continue using all the same tools: counting items, accessing nested fields, and safely reading values.
Inspecting the Results
echo "Popular Packages:\n";
echo "Symfony: " . $symfonyPopularPackages->count() . PHP_EOL;
echo " High Favers: " .
$symfonyPopularPackages->getString("0.name", "") .
" - " .
$symfonyPopularPackages->getInt("0.favers", 0) .
PHP_EOL;
echo "Laravel: " . $laravelPopularPackages->count() . PHP_EOL;
echo " High Favers: " .
$laravelPopularPackages->getString("0.name", "") .
" - " .
$laravelPopularPackages->getInt("0.favers", 0) .
PHP_EOL;
Recap
At this point we have:
- Filtered real datasets
- Sorted them by meaningful fields
- Counted and inspected the results
- Navigated deeply nested structures
—all without ever touching raw arrays.
Why Use DataBlock?
- Works with local arrays/files and remote JSON/YAML (API responses, external documents).
- Once wrapped in a
DataBlock, you can:- Access deeply nested values safely
- Filter datasets
- Sort and inspect results
- Extract only the parts you actually care about
All this without worrying about missing keys, broken structures, or manual casting.
DataBlockdoesn’t try to replace DTOs or business logic. Its job is straightforward and focused: make structured data—wherever it comes from—easier and safer to explore, query, and consume.
References
- PHP DataBlock GitHub repository –
- Laravel News article –
- Previous article – Handling Nested PHP Arrays Using DataBlock –