Doctrine Target Entity Resolver
softspring/doctrine-target-entity-resolver is a very small helper for reusable Symfony bundles.
Its job is simple: let a bundle work with interfaces such as UserInterface or AccountInterface, while the real Doctrine entity class is provided by the application.
This is useful when a bundle wants to stay reusable and does not want to hardcode a concrete entity class.
When To Use It
Use this component when your bundle:
- ships interfaces or abstract model contracts
- expects the application to provide the real Doctrine entity class
- needs Doctrine relations pointing to those interfaces
- wants to configure target entity resolution from container parameters
This is the pattern used across several Softspring bundles such as account-bundle, user-bundle, mailer-bundle, and CMS plugins.
What Problem It Solves
Doctrine relations need a concrete target entity.
Reusable bundles usually should not force the host application to use one specific entity class. Instead, the bundle can:
- define an interface
- let the application choose the implementing entity
- register that mapping during container compilation
This component is the helper that simplifies that last step.
Main Idea
The package exposes one base class:
AbstractResolveDoctrineTargetEntityPass
You extend it in your own bundle compiler pass and call setTargetEntityFromParameter() for each mapping you need.
Quick Example
Example compiler pass:
<?php
namespace App\DependencyInjection\Compiler;
use App\Model\UserInterface;
use Softspring\Component\DoctrineTargetEntityResolver\DependencyInjection\Compiler\AbstractResolveDoctrineTargetEntityPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ResolveDoctrineTargetEntityPass extends AbstractResolveDoctrineTargetEntityPass
{
protected function getEntityManagerName(ContainerBuilder $container): string
{
return 'default';
}
public function process(ContainerBuilder $container): void
{
$this->setTargetEntityFromParameter(
'app.user.class',
UserInterface::class,
$container,
true,
);
}
}
In this example:
app.user.classcontains the real entity classUserInterface::classis the interface used by the bundle- the compiler pass registers the target entity mapping in Doctrine
Configure The Entity Class Through Parameters
The common pattern is:
- expose a bundle parameter such as
sfs_user.user.class - let the application set its concrete class there
- resolve the interface to that class during compilation
That keeps the public integration point very simple for the application.
Required And Optional Mappings
setTargetEntityFromParameter() supports both required and optional mappings.
Use a required mapping when the bundle cannot work without that entity:
$this->setTargetEntityFromParameter('app.user.class', UserInterface::class, $container, true);
Use an optional mapping when that feature is only enabled in some applications:
$this->setTargetEntityFromParameter('app.history.class', HistoryInterface::class, $container, false);
If the parameter is missing and the mapping is optional, the helper skips it.
If the parameter is missing and the mapping is required, compilation fails early.
What The Helper Validates
Before registering the mapping, the helper checks that the configured class implements the expected interface.
That is important because it turns a broken configuration into an explicit container compilation error instead of a more confusing Doctrine failure later.
Typical Usage In A Bundle
This component usually sits next to:
- a bundle configuration parameter holding the entity class
- Doctrine mappings in the bundle that reference interfaces
- a compiler pass registered in the bundle class
That means the full integration flow is usually:
- bundle defines model interfaces
- application chooses concrete entities
- bundle stores those classes in parameters
- compiler pass resolves target entities
- Doctrine works with the concrete classes
Real Softspring Usage Pattern
In Softspring bundles, the pattern is usually this:
account-bundlemaps interfaces such as account or relation contracts to application entitiesuser-bundledoes the same for users, invitations, and access historymailer-bundleuses it for optional email history entities- CMS plugins use it when plugin model contracts are implemented by the project
That is the main value of this package: keeping that repeated compiler-pass logic in one small reusable helper.
How To Extend It Safely
When you build your own compiler pass on top of this component:
- keep one parameter per target entity mapping
- make required mappings explicit
- keep optional mappings only for genuinely optional features
- ensure your parameter points to a real class implementing the expected interface
- register the compiler pass in the bundle where the Doctrine mapping depends on it
This helper is intentionally small, so the safest extension strategy is to keep your own pass simple too.
What This Package Does Not Do
This component does not:
- create Doctrine mappings for you
- generate entities
- discover target entities automatically
- replace bundle configuration
It only helps wire interface-to-class target entity resolution into Doctrine's existing listener.
Practical Limits
Keep these limits in mind:
- the package assumes Doctrine resolve target entity support is already available in the host application
- it is a compile-time helper, not a runtime lookup service
- it helps only with target entity registration, not with repository, form, or controller integration
Summary
Choose doctrine-target-entity-resolver when your bundle wants to stay interface-based, but Doctrine still needs concrete entity classes from the application.