Bridge with doctrine/persistence

Refer to the official documentation on Doctrine’s website.

This bridge provides SQL database querying mechanisms, whenever you need to read/write from it.
Along with utility classes for memory management in jobs execution.

Object item writer

The writer will persist every items on the appropriate ObjectManager.
It expect that items are objects.
Objects can belong to different ObjectManager instances, only the encountered ones will be flushed.

ObjectManager->flush() is called every time the ItemJob reaches the batch size.

<?php

declare(strict_types=1);

use Doctrine\Persistence\ManagerRegistry;
use Yokai\Batch\Bridge\Doctrine\Persistence\ObjectWriter;

/** @var ManagerRegistry $managerRegistry */

new ObjectWriter(
    doctrine: $managerRegistry,
);

Object registry util

Imagine that in an ItemJob you need to find objects from a database.

<?php

declare(strict_types=1);

use App\Entity\Product;
use Doctrine\Persistence\ObjectRepository;
use Yokai\Batch\Job\Item\ItemProcessorInterface;

final class DenormalizeProductProcessor implements ItemProcessorInterface
{
    public function __construct(
        private ObjectRepository $repository,
    ) {
    }

    /**
     * @param array<string, mixed> $item
     */
    public function process(mixed $item): Product
    {
        $product = $this->repository->findOneBy(['sku' => $item['sku']]);

        $product ??= new Product($item['sku']);

        $product->setName($item['name']);
        $product->setPrice($item['price']);
        //...

        return $product;
    }
}
The problem here is that every time you will call findOneBy, you will have to query the database. The object might already be in
Doctrine’s memory, so it won’t be hydrated twice, but the query will be done every time.
The role of the ObjectRegistry is to remember found objects identities, and query these objects with it instead.
<?php

declare(strict_types=1);

use App\Entity\Product;
-use Doctrine\Persistence\ObjectRepository;
+use Yokai\Batch\Bridge\Doctrine\Persistence\ObjectRegistry;
use Yokai\Batch\Job\Item\ItemProcessorInterface;

final class DenormalizeProductProcessor implements ItemProcessorInterface
{
    public function __construct(
-        private ObjectRepository $repository,
+        private ObjectRegistry $registry,
    ) {
    }

    /**
     * @param array<string, mixed> $item
     */
    public function process(mixed $item): Product
    {
-        $product = $this->repository->findOneBy(['sku' => $item['sku']]);
+        $product = $this->registry->findOneBy(Product::class, ['sku' => $item['sku']]);

        $product ??= new Product($item['sku']);

        $product->setName($item['name']);
        $product->setPrice($item['price']);
        //...

        return $product;
    }
}
The first time, the query will hit the database, and the object identity will be remembered in the registry.
Everytime after that, the registry will call Doctrine\Persistence\ObjectManager::find instead.
If the object is still in Doctrine’s memory, it will be returned directly.
Otherwise, the query will be the fastest possible because it will use the object identity.