Laravel's resource() Method: Simplify Streams
Handling stream resources in Laravel has always been a little bit mystical and complex, especially when working with external services.
This complexity often leads to verbose, hard-to-read code that's prone to errors and can slow down development. I've long sought a more elegant solution.
I recently proposed a new feature for the Laravel framework that I believe will make working with stream resources much more straightforward. Let's dive into the details of the new resource()
method.
The Problem: Verbose Stream Handling
If you've ever worked with stream resources in Laravel, especially when dealing with external services or large datasets, you've probably found the process a bit verbose. It often requires multiple steps and can lead to boilerplate code cluttering up your elegant Laravel applications.
The Solution: Enter resource()
To address this, I've proposed adding a new resource()
method to the Response
class. This method allows you to directly obtain a PHP stream resource from the response body, significantly simplifying workflows that involve stream operations.
Here's what the implementation looks like:
use GuzzleHttp\Psr7\StreamWrapper;
public function resource()
{
return StreamWrapper::getResource($this->response->getBody());
}
This method leverages the GuzzleHttp\Psr7\StreamWrapper
that comes bundled with the guzzlehttp/psr7
package, which is already a requirement of the framework via guzzlehttp/guzzle
.
Real-World Usage Examples
Let's look at how this new method simplifies common tasks:
Streaming a resource to a Storage disk
Let's say you need to download an image from an external URL and store it in a S3 bucket. Here's how you might do it before and after the resource()
method, without loading the data into memory:
use GuzzleHttp\Psr7\StreamWrapper;
$response = Http::get('https://picsum.photos/200/300');
// Before
Storage::disk('s3')->writeStream(
'thumbnail.png',
StreamWrapper::getResource(
$response->toPsrResponse()->getBody()
),
);
// After
Storage::disk('s3')->writeStream(
'thumbnail.png',
$response->resource(),
);
Reading a large JSON stream
Here's my favorite way to work with JSON data from external APIs, using the halaxa/json-machine
package to iterate over JSON objects without loading the entire dataset into memory:
use GuzzleHttp\Psr7\StreamWrapper;
use JsonMachine\Items;
$response = Http::get('https://jsonplaceholder.typicode.com/todos/1');
// Before
$phpStream = StreamWrapper::getResource($response->toPsrResponse()->getBody());
foreach (Items::fromStream($phpStream) as $key => $value) {
echo "Key: {$value}. Value: {$value}.".PHP_EOL;
}
// After
foreach (Items::fromStream($response->resource()) as $key => $value) {
echo "Key: {$value}. Value: {$value}.".PHP_EOL;
}
I've written a detailed article titled "Working with large JSON responses as streams". It shows you how to efficiently handle large JSON responses using the new resource()
method and a custom lazy()
macro. This macro transforms the JSON resource into a LazyCollection
, making it easier to work with stream data. Here's a quick example of how you can use the macro to cast stream data to a LazyCollection
of DTOs:
$response = Http::get('https://api.example.com/large-data');
$items = $response->lazy('/results/items')
->map(fn (array $item): ItemData => ItemData::fromArray($item));
Wrapping Up
The pull request (PR #52412) just got merged into the framework. This new method will make working with streams in Laravel even more enjoyable and efficient, and I'm excited for the community to start using it.
Want to learn more? Check out these pull requests:
- PR #52412 - Add resource() method to Response
- PR #52410 - Add ResponseInterface mixin to Illuminate\Http\Client\Response
Stay tuned for updates, and as always, happy coding!