Installation:
composer require appsco/component-firi
Note: Due to outdated Symfony 2.x dependencies, ensure your project can tolerate these constraints (or fork the package for Symfony 5+ compatibility).
First Use Case: Render a filtered collection of Doctrine entities with custom rendering logic.
use AppSco\Component\Firi\FilterItemRendererIterator;
$entities = $entityManager->getRepository(Entity::class)->findAll();
$iterator = new FilterItemRendererIterator(
new ArrayIterator($entities),
new \CallbackFilterIterator($entities, fn($item) => $item->isActive()),
new \CallbackFilterIterator($entities, fn($item) => $item->getPriority() > 0)
);
foreach ($iterator as $renderedItem) {
// $renderedItem is the processed output (e.g., array, string, or custom object)
}
Key Classes:
FilterItemRendererIterator: Core class combining filtering and rendering.FilterItemRenderer: Base renderer (extend for custom logic).FilterItemRendererFactory: Factory for creating renderers (if available in newer versions).Where to Look First:
src/AppSco/Component/Firi/FilterItemRendererIterator.php (core logic).tests/ (if available) for usage examples.Input:
ArrayIterator, Doctrine\ORM\QueryBuilder result).FilterIterator implementations (e.g., CallbackFilterIterator, custom filters).Processing:
$rawData = $entityManager->createQueryBuilder()
->select('e')
->from(Entity::class, 'e')
->getQuery()
->iterate();
$filteredIterator = new \CallbackFilterIterator($rawData, fn($item) => $item['isActive']);
$renderer = new CustomRenderer(); // Extend FilterItemRenderer
$firi = new FilterItemRendererIterator($filteredIterator, $renderer);
Output:
$firi to get rendered items (e.g., arrays, strings, or transformed objects).class CustomRenderer extends FilterItemRenderer {
public function render($item) {
return [
'id' => $item->getId(),
'name' => $item->getName(),
'formatted' => $this->formatValue($item->getValue()),
];
}
}
Doctrine ORM:
Use QueryBuilder->iterate() to avoid loading all entities into memory at once.
$query = $entityManager->createQueryBuilder()
->select('e')
->from(Entity::class, 'e')
->where('e.isActive = :active')
->setParameter('active', true)
->iterate();
Symfony Dependency Injection: Bind the factory/iterator as a service (if using Symfony 2.x):
services:
firi.iterator:
class: AppSco\Component\Firi\FilterItemRendererIterator
arguments:
- '@filter_iterator'
- '@renderer'
Custom Filters: Create reusable filter classes:
class ActiveFilterIterator extends FilterIterator {
public function accept() {
return $this->current()->isActive();
}
}
Batch Processing:
Combine with IteratorIterator for lazy loading:
$iterator = new IteratorIterator(
new FilterItemRendererIterator(
$rawData,
new ActiveFilterIterator($rawData),
new CustomRenderer()
)
);
Symfony 2.x Dependencies:
symfony/options-resolver to ^5.0).symfony/polyfill.spatie/array-to-object for rendering.Memory Leaks:
iterate() in Doctrine ORM returns a lazy iterator, but chaining multiple FilterIterators can load data prematurely.QueryBuilder->where()) before passing to FilterItemRendererIterator.Renderer Assumptions:
FilterItemRenderer may assume certain methods (e.g., render()). Override carefully:
class MyRenderer extends FilterItemRenderer {
public function render($item) {
if (!$item instanceof MyEntity) {
throw new \InvalidArgumentException('Expected MyEntity');
}
return $item->toArray();
}
}
No Built-in Factory:
$firi = new FilterItemRendererIterator($filteredIterator, $renderer);
class FiriFactory {
public static function create($data, RendererInterface $renderer) {
return new FilterItemRendererIterator(
new ArrayIterator($data),
$renderer
);
}
}
Iterator State:
iterator_to_array() to inspect intermediate states:
$filtered = iterator_to_array(new CallbackFilterIterator($rawData, fn($item) => true));
var_dump($filtered);
Renderer Output:
foreach ($firi as $item) {
\Log::debug('Rendered:', ['item' => $item]);
}
Performance:
memory_get_usage() to check for unexpected loading:
$start = memory_get_usage();
foreach ($firi as $item) {}
$end = memory_get_usage();
\Log::info('Memory used:', ['bytes' => $end - $start]);
Custom Renderers:
FilterItemRenderer to add logic:
class JsonRenderer extends FilterItemRenderer {
public function render($item) {
return json_encode($item->toArray());
}
}
Filter Composition:
$iterator = new FilterItemRendererIterator(
new CallbackFilterIterator($data, fn($item) => $item->isActive()),
new CallbackFilterIterator($data, fn($item) => $item->isPublished()),
new CustomRenderer()
);
Event Dispatching:
$dispatcher->addListener('firi.render', function ($event) {
\Log::info('Rendering item', ['item' => $event->getItem()]);
});
Caching Rendered Output:
Renderer subclass:
class CachedRenderer extends FilterItemRenderer {
private $cache = [];
public function render($item) {
if (!isset($this->cache[$item->getId()])) {
$this->cache[$item->getId()] = $this->process($item);
}
return $this->cache[$item->getId()];
}
}
How can I help you explore Laravel packages today?