Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lazy Json Laravel Package

cerbero/lazy-json

Lazy JSON is a lightweight PHP package for memory-efficient parsing of large JSON documents. Instead of decoding an entire payload at once, it lets you traverse JSON lazily, reducing memory usage and making big files or streams easier to process.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer:

composer require cerbero/lazy-json

In Laravel the service provider and Json facade are auto-discovered. In a non-Laravel app, either bootstrap the facade manually or call the underlying Cerbero\LazyJson\LazyJson class directly.

The first and most common use case is turning a large JSON export into a LazyCollection so you can process records one at a time instead of json_decode-ing the whole file into memory:

use Cerbero\LazyJson\Facades\Json;

Json::fromFile(storage_path('app/exports/orders.json'))
    ->map(fn (array $order) => $order['id'])
    ->take(10)
    ->all();

Where to look first:

  1. README.md — the source methods and pointer syntax.
  2. src/LazyJson.php — the static fromFile, fromUrl, fromStream, fromString entry points.
  3. src/Facades/Json.php and the service provider — what boots in Laravel.
  4. Under the hood it delegates to json-machine, so familiarity with that package pays off for edge cases.

Your first real task is almost always: pick a source, optionally pass a JSON Pointer to drill into the array you care about, and iterate.

Implementation Patterns

Pick the cheapest source for the job. All four entry points return a LazyCollection, so downstream code is identical regardless of origin:

Json::fromFile('/data/users.json');
Json::fromUrl('https://example.com/feed.json');
Json::fromStream(fopen('php://stdin', 'r'));
Json::fromString($json);

Target a nested array with a pointer. Don't stream the whole envelope when you only need one branch. Pointers use JSON Pointer syntax (RFC 6901, leading slash, array indexes as integers):

Json::fromFile('response.json', '/data/items')
    ->each(fn (array $item) => process($item));

This skips the header metadata and any sibling arrays, which is where the biggest CPU/memory wins come from on huge API dumps.

Stream into the database in batches. Avoid one insert per row; use chunk so you keep backpressure and stay inside memory limits:

Json::fromFile('orders.json', '/orders')
    ->chunk(1000)
    ->each(fn ($chunk) => Order::insert($chunk->values()->all()));

For upserts or per-row logic where you still want batching semantics, use ->each on the chunk instead of insert.

Fan out into queues or jobs. Because iteration is lazy, you can dispatch without loading the file:

Json::fromFile('import.json', '/rows')
    ->each(fn (array $row) => ImportRow::dispatch($row));

Pair this with a rate-limited queue connection when the file has millions of rows.

Compose with normal LazyCollection operators. map, filter, flatMap, skip, take, tap, takeUntil, and chunk all work as expected — but treat anything that materializes the collection (all, collect, toArray, remember) as a deliberate last step.

Add progress reporting in Artisan commands. tap between the source and the sink is the cleanest hook:

$bar = $this->output->createProgressBar();
Json::fromFile($path, '/rows')
    ->tap(fn () => $bar->advance())
    ->each(fn ($row) => import($row));
$bar->finish();

Test with fromString instead of fixtures on disk. It keeps tests hermetic and lets you exercise pointer edge cases (empty arrays, missing keys, mixed object/array structures) without touching the filesystem.

Prefer fromStream when you need HTTP control. fromUrl is fine for simple cases, but if you need custom headers, timeouts, or retries, open the stream yourself with Guzzle/Laravel Http and hand the resource to fromStream.

Gotchas and Tips

  • The stream is single-pass. A LazyCollection looks reusable, but the underlying generator/stream is consumed after the first full iteration. If you need the data twice, recreate the collection from the source (cheap for files) or cache explicitly with remember() and accept the memory cost.
  • count() and other terminal calls consume the data. Calling ->count() and then iterating will yield nothing on the second pass. If you need a count, do it in the same pass or from the source metadata.
  • Pointers fail quietly. A typo in a pointer often yields an empty collection rather than an exception. Validate pointer paths against a small sample in tests before running against production exports.
  • Encoding is strict. UTF-8 BOMs, UTF-16 files, and invalid byte sequences can trigger parser errors or garbage output. Normalize input (strip BOM, convert encoding) before handing it to fromStream if your sources are heterogeneous.
  • Malformed JSON throws at the parser layer. Wrap iteration in try/catch and expect JsonMachine syntax/invalid-argument exceptions rather than something specific to this package. Log the source and the pointer when catching — they're the two things you'll need to reproduce.
  • JSON objects vs arrays behave differently. When a pointer resolves to an object, the yielded items are the object's values in key order, not a keyed map. If you need keys, either iterate one level up or post-process with json_decode on individual records.
  • Watch the memory escape hatches. ->all(), ->collect(), ->toArray(), and ->remember() all defeat the purpose. A dd($collection) on a multi-GB file will also try to render everything — use ->take(5)->all() while debugging.
  • URL sources need timeouts and error handling. fromUrl uses PHP stream wrappers, so network stalls look like hangs. For production, wrap Guzzle with explicit timeouts and feed fromStream, and always catch for HTTP 4xx/5xx bodies being parsed as JSON.
  • No config file to tune. Behavior is entirely driven by source and pointer. If you need a custom decoder, chunked source, or non-standard traversal, drop down to json-machine directly (JsonMachine::fromFile(..., new ExtJsonDecoder(true))) — this package is a thin convenience layer over it.
  • Pin the version. With a small dependent ecosystem, treat upgrades as potentially breaking and lock to a known-good minor in composer.json.
  • Isolate the dependency. Wrap calls behind a small importer service so you can swap in json-machine or a custom parser later without touching call sites.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings