lendable/doctrine-extensions-bundle
Lendable's Doctrine Extensions Bundle integrates common Doctrine behavioral extensions into Symfony applications, providing configuration and service wiring for features like timestamps, slugs, trees, and soft deletes with minimal setup.
This bundle swaps Doctrine's default RepositoryFactory for a container-aware one, so repository classes can be declared as Symfony services and receive extra constructor arguments (loggers, custom connections, query services, etc.) instead of only EntityManagerInterface + ClassMetadata.
Minimal steps for a legacy Symfony 2.x/3.x app:
composer require lendable/doctrine-extensions-bundle
Register the bundle (kernel era):
// app/AppKernel.php
$bundles = [
// ...
new Lendable\DoctrineExtensionsBundle\LendableDoctrineExtensionsBundle(),
];
If it ships a config node, enable the container repository factory in config.yml/config_dev.yml; if not, the bundle's compiler pass typically wires its own factory into doctrine.orm.repository_factory automatically.
First use case — a repository that needs a collaborator:
final class InvoiceRepository extends EntityRepository
{
private $clock;
public function __construct(EntityManagerInterface $em, ClassMetadata $class, Clock $clock)
{
parent::__construct($em, $class);
$this->clock = $clock;
}
public function findOverdue(): array
{
// now $this->clock is available, no service locator hacks
}
}
Then register it as a service (public, so the factory can pull it from the container) and reference it by class/id. Look first at the bundle's DependencyInjection/ folder (compiler pass + extension) and its Resources/config/ services file — that's where the repository factory wiring and the service tag/id convention live. The README's example is the fastest confirmation of the tag name and constructor ordering your version expects.
Service-backed repositories with dependencies. The core workflow is: define your repository as a normal Symfony service, tag it (usually doctrine.repository_service or whatever the bundle's pass consumes), and let the factory return that service. Callers keep using $em->getRepository(Invoice::class) or, once the container factory is in place, inject repositories directly.
Direct injection in your own services. Because they're real services, you can autowire repositories into handlers/controllers:
final class OverdueInvoicesHandler
{
public function __construct(private InvoiceRepository $invoices) {}
public function __invoke(OverdueInvoicesQuery $q): array
{
return $this->invoices->findOverdue();
}
}
Move query logic out of entities/controllers. The whole point of a container-aware factory is to let you push query-building into repositories or dedicated query objects that depend on other services, keeping controllers thin.
Tag-by-mapping convention. If the bundle derives repository services from Doctrine's mapping configuration (repositoryClass in @ORM\Entity), make sure your entity's repositoryClass matches the service class exactly, then expose that class as a service. Mismatches are the most common integration failure.
Testing hook. Unit tests can now new InvoiceRepository($em, $metadata, $fakeClock) or mock the repository service entirely, instead of always going through a live EntityManager. That's the biggest day-to-day win: your repositories become plain objects.
Integration tips. Keep repository constructors small and side-effect free (the factory instantiates via the container, so circular dependencies surface as container errors, not runtime surprises). Prefer type-hinted extra args over string parameters so the resolver can match them.
doctrine.repository_service), and Symfony's DI + Doctrine's RepositoryFactory handle extra constructor args out of the box. On Symfony 3.4+/4+, use the native mechanism and delete this bundle.getRepository() quietly constructs the repository reflectively and your extra args are null or a TypeError. Symptom: dependency works in one env, fails in another. Verify wiring with app/console debug:container --tag=<repo tag> and check the actual factory class with debug:container doctrine.orm.repository_factory.EntityManagerInterface, ClassMetadata, then your extras. Getting this wrong produces confusing "too few arguments" errors from deep inside Doctrine.doctrine.orm.repository_factory — but budget for doing this yourself, since upstream support is gone.How can I help you explore Laravel packages today?