Weave Code
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

Doctrine Orm Batcher Bundle Laravel Package

setono/doctrine-orm-batcher-bundle

Symfony bundle that integrates Setono’s Doctrine ORM Batcher, making it easy to process large Doctrine queries in batches. Provides injectable services like BatcherFactoryInterface and QueryRebuilderInterface with autowiring support.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Bundle:

    composer require setono/doctrine-orm-batcher-bundle
    
  2. Enable the Bundle: Create a custom service provider in Laravel to register the bundle’s services (since Laravel lacks a bundles.php equivalent):

    // app/Providers/DoctrineBatcherServiceProvider.php
    namespace App\Providers;
    
    use Illuminate\Support\ServiceProvider;
    use Setono\DoctrineORMBatcher\Factory\BatcherFactory;
    
    class DoctrineBatcherServiceProvider extends ServiceProvider
    {
        public function register()
        {
            $this->app->singleton(
                \Setono\DoctrineORMBatcher\Factory\BatcherFactoryInterface::class,
                function ($app) {
                    return new BatcherFactory(
                        $app->make(\Doctrine\ORM\EntityManagerInterface::class)
                    );
                }
            );
        }
    }
    

    Register the provider in config/app.php:

    'providers' => [
        // ...
        App\Providers\DoctrineBatcherServiceProvider::class,
    ],
    
  3. Set Up Doctrine ORM: Install Doctrine dependencies and configure it in config/doctrine.php:

    composer require doctrine/dbal doctrine/orm
    

    Example config/doctrine.php:

    return [
        'dbal' => [
            'driver' => 'pdo_mysql',
            'url' => env('DATABASE_URL'),
        ],
        'orm' => [
            'entity_managers' => [
                'default' => [
                    'connection' => 'default',
                    'mappings' => [
                        ['type' => 'annotation', 'namespace' => 'App\Entity', 'path' => 'app/Entities'],
                    ],
                ],
            ],
        ],
    ];
    
  4. First Use Case: Batch delete inactive users (replace User with your Doctrine entity):

    use Setono\DoctrineORMBatcher\Factory\BatcherFactoryInterface;
    
    class UserService
    {
        public function __construct(
            private BatcherFactoryInterface $batcherFactory,
            private \Doctrine\ORM\EntityManagerInterface $em
        ) {}
    
        public function deleteInactiveUsers()
        {
            $dql = 'DELETE FROM App\Entity\User u WHERE u.active = :active';
            $batcher = $this->batcherFactory->createBatcher($this->em, $dql, ['active' => false]);
            $batcher->setBatchSize(100); // Process 100 records at a time
            $batcher->execute();
        }
    }
    

Implementation Patterns

Core Workflows

1. Batching Queries

Use the batcher for SELECT, INSERT, UPDATE, or DELETE operations:

// SELECT batching (fetch in chunks)
$dql = 'SELECT u FROM App\Entity\User u WHERE u.role = :role';
$batcher = $this->batcherFactory->createBatcher($this->em, $dql, ['role' => 'admin']);
$batcher->setBatchSize(50);

foreach ($batcher as $batch) {
    foreach ($batch as $user) {
        // Process each user in the batch
    }
}

// INSERT/UPDATE/DELETE batching
$dql = 'UPDATE App\Entity\User u SET u.active = :active WHERE u.id IN (:ids)';
$batcher = $this->batcherFactory->createBatcher($this->em, $dql, ['active' => true, 'ids' => []]);
$batcher->setBatchSize(200);

$ids = $this->em->createQuery('SELECT u.id FROM App\Entity\User u WHERE u.active = false')->getScalarResult();
$batcher->setParameter('ids', array_chunk($ids, 200));

$batcher->execute();

2. Query Rebuilding

Optimize complex queries for batching using QueryRebuilderInterface:

use Setono\DoctrineORMBatcher\Query\QueryRebuilderInterface;

class UserQueryRebuilder implements QueryRebuilderInterface
{
    public function rebuild(\Doctrine\ORM\QueryBuilder $qb, array $batch): \Doctrine\ORM\QueryBuilder
    {
        $ids = array_column($batch, 'id');
        return $qb
            ->where('u.id IN (:ids)')
            ->setParameter('ids', $ids);
    }
}

// Register the rebuilder (e.g., in a service)
$batcher = $this->batcherFactory->createBatcher($this->em, $dql);
$batcher->setQueryRebuilder(new UserQueryRebuilder());

3. Transaction Management

Control transaction boundaries per batch:

$batcher = $this->batcherFactory->createBatcher($this->em, $dql);
$batcher->setBatchSize(100);
$batcher->setFlushMode(\Doctrine\ORM\UnitOfWork::FLUSH_AFTER); // Flush after each batch
$batcher->execute();

4. Event Listeners

Hook into batch lifecycle events (e.g., pre/post batch processing):

use Setono\DoctrineORMBatcher\Event\BatchEvents;

$batcher = $this->batcherFactory->createBatcher($this->em, $dql);
$batcher->addListener(BatchEvents::PRE_BATCH, function ($event) {
    // Log or validate before processing a batch
});
$batcher->addListener(BatchEvents::POST_BATCH, function ($event) {
    // Cleanup or notify after a batch
});

Integration Tips

Laravel-Specific Adaptations

  1. Doctrine Entity Mapping: Ensure your entities are properly mapped for Doctrine. Example:

    // src/Entity/User.php
    namespace App\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    
    #[ORM\Entity]
    class User
    {
        #[ORM\Id, ORM\GeneratedValue, ORM\Column]
        private ?int $id = null;
    
        #[ORM\Column]
        private bool $active = false;
    }
    
  2. Hybrid Eloquent/Doctrine: Use Doctrine only for batch operations while keeping Eloquent for CRUD:

    // For CRUD (Eloquent):
    User::where('active', true)->get();
    
    // For batching (Doctrine):
    $dql = 'SELECT u FROM App\Entity\User u WHERE u.active = true';
    $batcher = $this->batcherFactory->createBatcher($this->em, $dql);
    
  3. Command-Line Batching: Integrate with Laravel Artisan commands for bulk operations:

    namespace App\Console\Commands;
    
    use Illuminate\Console\Command;
    use Setono\DoctrineORMBatcher\Factory\BatcherFactoryInterface;
    
    class BatchUpdateCommand extends Command
    {
        protected $signature = 'batch:update-users';
        protected $description = 'Batch update users';
    
        public function __construct(
            private BatcherFactoryInterface $batcherFactory,
            private \Doctrine\ORM\EntityManagerInterface $em
        ) {
            parent::__construct();
        }
    
        public function handle()
        {
            $dql = 'UPDATE App\Entity\User u SET u.last_login = :now WHERE u.last_login < :cutoff';
            $batcher = $this->batcherFactory->createBatcher($this->em, $dql, [
                'now' => new \DateTime(),
                'cutoff' => new \DateTime('-30 days'),
            ]);
            $batcher->setBatchSize(150);
            $batcher->execute();
            $this->info('Batch update completed!');
        }
    }
    
  4. Queue Integration: Offload batch jobs to Laravel queues for async processing:

    use Illuminate\Support\Facades\Queue;
    
    Queue::push(function () {
        $dql = 'SELECT u FROM App\Entity\User u WHERE u.role = :role';
        $batcher = $this->batcherFactory->createBatcher($this->em, $dql, ['role' => 'admin']);
        $batcher->setBatchSize(50);
    
        foreach ($batcher as $batch) {
            foreach ($batch as $user) {
                // Process user (e.g., send email, update external system)
            }
        }
    });
    

Gotchas and Tips

Pitfalls

  1. Doctrine vs. Eloquent Conflicts:

    • Issue: Doctrine’s EntityManager and Eloquent’s Manager may conflict if not properly isolated.
    • Fix: Use separate database connections or namespaces for Doctrine entities.
    • Tip: Prefix Doctrine entities with DoctrineEntity (e.g., DoctrineEntity\User) to avoid collisions.
  2. Query Rebuilding Complexity:

    • Issue: Rebuilding complex DQL queries (e.g., with `JOIN
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity