Closed as not planned
Description
Description
Symfony does not currently support an easy way to register more mapping types on DiscriminatorMap. This is an important feature for modularity.
Currently a programmer must:
- Create a compiler pass to replace first argument of
LoaderChain
andSerializerCacheWarmer
services. - Replace whole
ClassDiscriminatorMapping
object.
There is a simple change to solve the current not friendly way:
- Add
serializer.loader
tag to register custom loaders. - Add
addType(string $type, string $class)
method toClassDiscriminatorMapping
class. This point is not important as the first. - Remove
internal
annotation fromClassMetadataInterface
. Is there a reason to mark this class as internal?
For example, Doctrine allows you to dynamically register more types via loadClassMetadata
event and ClassMetadataInfo::addDiscriminatorMapClass
method.
Example
Before
class SerializerLoadersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
$chainLoader = $container->getDefinition('serializer.mapping.chain_loader');
$serializerLoaders = $chainLoader->getArgument(0);
$serializerLoaders[] = new Definition(DiscriminatorMappingLoader::class, [
$container->getParameter('module_entity_mapping'), // Define app mapping in configuration
]);
$chainLoader->replaceArgument(0, $serializerLoaders);
$container->getDefinition('serializer.mapping.cache_warmer')->replaceArgument(0, $serializerLoaders);
}
}
class DiscriminatorMappingLoader implements LoaderInterface
{
/**
* @param array<string, class-string> $appMapping Mapping from configuration
*/
public function __construct(private array $appMapping)
{
}
public function loadClassMetadata(ClassMetadataInterface $classMetadata): bool
{
if (ModuleEntity::class === $classMetadata->getName()) {
$mapping = $classMetadata->getClassDiscriminatorMapping();
$classMetadata->setClassDiscriminatorMapping(
new ClassDiscriminatorMapping(
$mapping->getTypeProperty(),
array_merge($this->appMapping, $mapping->getTypesMapping())
)
);
return true;
}
return false;
}
}
After
return function(ContainerConfigurator $container) {
$container->services()->set(DiscriminatorMappingLoader::class)
->arg(0, abstract_arg('From configuration'))
->tag('serializer.loader', ['priority' => 4096])
;
};
class DiscriminatorMappingLoader implements LoaderInterface
{
/**
* @param array<string, class-string> $appMapping Mapping from configuration
*/
public function __construct(private array $appMapping)
{
}
public function loadClassMetadata(ClassMetadataInterface $classMetadata): bool
{
if (ModuleEntity::class === $classMetadata->getName()) {
$classMetadata->getClassDiscriminatorMapping()->addTypes($this->appMapping);
// or iterate over appMapping and call addType($type, $class)
return true;
}
return false;
}
}