loophp/iterators
loophp/iterators adds “missing” PHP iterators and iterator aggregates for lazy, composable pipelines: caching rewindable generators, chunk/map/filter/reduce/sort/unique/concat/pack/unpack, plus typed, random, recursive, and resource-based iterables.
Installation:
composer require loophp/iterators
Add to composer.json if using Laravel's autoloader:
"autoload": {
"psr-4": {
"App\\": "app/",
"Loophp\\Iterators\\": "vendor/loophp/iterators/src/"
}
}
Run composer dump-autoload.
First Use Case:
Transform a Laravel collection into an iterable with MapIterableAggregate:
use Loophp\Iterators\MapIterableAggregate;
use Illuminate\Support\Collection;
$collection = collect([1, 2, 3]);
$iterator = new MapIterableAggregate($collection, fn($item) => $item * 2);
foreach ($iterator as $value) {
// Outputs: 2, 4, 6
}
Key Entry Points:
src/IterableIteratorAggregate.php: Base class for most iterators.src/MapIterableAggregate.php: For transforming data.src/FilterIterableAggregate.php: For filtering data.src/CachingIteratorAggregate.php: For caching generators.Data Transformation Pipelines: Chain iterators for complex operations:
$pipeline = new MapIterableAggregate(
new FilterIterableAggregate(
range(1, 10),
fn($item) => $item % 2 === 0
),
fn($item) => $item * 10
);
Laravel Collection Integration: Convert collections to iterators for custom logic:
$users = User::all();
$iterator = new MapIterableAggregate($users, fn($user) => $user->name);
Generator Handling: Cache generators for reusability:
$generator = function() {
yield 1; yield 2; yield 3;
};
$cached = new CachingIteratorAggregate($generator());
Recursive Data Processing: Flatten nested structures:
$tree = ['a' => ['b' => ['c']]];
$iterator = new RecursiveIterableAggregate($tree, fn($item) => $item ?? []);
Eloquent Query Results: Process query results as iterators:
$posts = Post::query()->get();
$titles = new MapIterableAggregate($posts, fn($post) => $post->title);
Service Provider Bootstrapping: Register iterators as Laravel bindings:
public function register()
{
$this->app->bind('custom.iterator', function() {
return new MapIterableAggregate([], fn($item) => $item);
});
}
Middleware for Request Data: Transform request data iteratively:
$requestData = new MapIterableAggregate(
$request->all(),
fn($value, $key) => strtolower($key)
);
Event Listeners: Process event payloads with iterators:
public function handle(OrderProcessed $event)
{
$items = new MapIterableAggregate(
$event->order->items,
fn($item) => $item->name
);
}
Memory Management:
CachingIteratorAggregate: Caches all keys/values in memory. Avoid with large datasets.RandomIterableAggregate: Requires a seed proportional to dataset size. Use CachingIteratorAggregate for repeated iterations.Generator State:
CachingIteratorAggregate for rewindable generators:
$generator = function() { yield 1; yield 2; };
$cached = new CachingIteratorAggregate($generator());
foreach ($cached as $item) {} // First pass
foreach ($cached as $item) {} // Second pass (works)
ClosureIterator:
// Bad: Heavy DB query per iteration
$iterator = new ClosureIterator(fn() => User::find(1));
RecursiveIterableAggregate:
$iterator = new RecursiveIterableAggregate(
['a' => ['b' => ['a']]],
fn($item) => $item // ❌ Infinite loop
);
Type Safety:
TypedIterableAggregate) enforce strict typing. Ensure input matches expected types.Iterator Validation:
Use iterator_to_array() to inspect contents:
$array = iterator_to_array(new MapIterableAggregate([1, 2, 3], fn($i) => $i * 2));
// Debug $array
Performance Profiling:
Benchmark iterators with microtime():
$start = microtime(true);
foreach ($iterator as $item) {}
$time = microtime(true) - $start;
Laravel Logging: Log iterator results for debugging:
foreach ($iterator as $item) {
\Log::debug('Iterator item', ['item' => $item]);
}
Custom Iterators:
Extend IterableIteratorAggregate for domain-specific logic:
class UserNameIterator extends IterableIteratorAggregate
{
public function __construct(private Collection $users) {}
public function getIterator(): Iterator
{
return new MapIterableAggregate(
$this->users,
fn($user) => $user->name
);
}
}
Laravel Service Providers: Bind iterators to Laravel's container:
$this->app->bind('user.names', function() {
return new MapIterableAggregate(
User::all(),
fn($user) => $user->name
);
});
Dynamic Iterator Factories: Create factories for reusable iterator patterns:
class IteratorFactory
{
public static function map(Collection $collection, callable $callback)
{
return new MapIterableAggregate($collection, $callback);
}
}
Integration with Laravel Collectors: Convert iterators to collections:
$collection = collect(iterator_to_array($iterator));
Eloquent Relationships: Iterators work with lazy-loaded relationships:
$users = User::with('posts')->get();
$posts = new MapIterableAggregate($users, fn($user) => $user->posts);
API Resources: Transform API resources iteratively:
$resources = new MapIterableAggregate(
User::all(),
fn($user) => new UserResource($user)
);
Queue Jobs: Process queue jobs with iterators:
public function handle()
{
$data = new MapIterableAggregate(
$this->data,
fn($item) => $item->process()
);
}
Blade Directives: Use iterators in Blade templates (via helper functions):
// app/Helpers/IteratorHelper.php
function mapIterable($items, $callback)
{
return new MapIterableAggregate($items, $callback);
}
@foreach(mapIterable($users, fn($user) => $user->name) as $name)
{{ $name }}
@endforeach
How can I help you explore Laravel packages today?