Update #[AsDbalType] attribute to register DBAL types as Symfony services - #2221
Update #[AsDbalType] attribute to register DBAL types as Symfony services#2221GromNaN wants to merge 9 commits into
#[AsDbalType] attribute to register DBAL types as Symfony services#2221Conversation
#[AsDatabaseType] attribute to register DBAL types as Symfony services
99af197 to
1db2468
Compare
#[AsDatabaseType] attribute to register DBAL types as Symfony services#[AsDbalType] attribute to register DBAL types as Symfony services
|
To be rebased after #2197 |
|
@GromNaN Do you want to continue your work here? |
|
I'll finish the PRs on DBAL and ORM before polishing this PR on the Bundle. In August. |
fd11d62 to
626dbc6
Compare
|
Hi! I was a bit confused by the PR description seeing as the attribute already exists from the other PR linked above - could we maybe update it to explain why this PR is still needed or what's it trying to solve? PS: I saw some documentation in that other PR which is quite good, but interestingly the only relevant reference in a Google search for "AsDbalType" brings me to this PR. |
#[AsDbalType] attribute to register DBAL types as Symfony services#[AsDbalType] attribute to register DBAL types as Symfony services
|
@uuf6429 Sorry for the confusion. This PR was created before the merge, but was not merged because it requires upstream work on DBAL and ORM. Thanks for pointing the inconsistence, I updated the title and description to reflect the current state. |
Extends the #[AsDbalType] attribute with a connection restriction argument and merges the TypeRegistry injection logic into RegisterDbalTypePass. When DBAL >= 4.5 (setTypeRegistry available): - Builds a per-connection TypeRegistry populated via a ServiceLocator - Types are resolved lazily on first use, enabling constructor DI - Connection restriction (AsDbalType::$connection) is honoured - Config-based types (doctrine.dbal.types) are included as inline definitions - TypeRegistry is injected into both DBAL and ORM configurations When DBAL < 4.5 (fallback): - Falls back to the existing global type registry via doctrine.dbal.connection_factory.types (no DI, no per-connection restriction)
DBAL now requires the map whenever a container is injected, since it no longer derives type names from ServiceProviderInterface::getProvidedServices(). The locator is keyed by type name, so the map is an identity map.
Stub the connection TypeRegistry in the data collector test and restore the kernel exception handler in the compiler pass test to avoid a risky test. Rename the AsDbalType attribute test and add Money type/entity fixtures exercising ORM entity mapping.
On the fallback path, DBAL instantiates types with "new $class()", so dependency injection is not available. Reject types whose constructor has mandatory arguments and mark their definition as container.excluded since the type is instantiated by DBAL rather than used as a service. Add coverage for the exclusion, the required-argument rejection and for dependency injection into type services on DBAL >= 4.5.
DBAL 4.5 renamed the Configuration accessors to get/setTypeProvider(), typed against the new TypeProvider interface. The version guard has to follow the rename: method_exists() on the old name would silently take the DBAL < 4.5 branch, leaving per-connection registries unwired with no error. DoctrineDataCollectorTest can now stub TypeProvider instead of instantiating a real TypeRegistry, which the final class made impossible.
- Wrap the constructor-bearing test fixture in a version guard, since Doctrine\DBAL\Types\Type::__construct() is final before DBAL 4.5 - Type assertTypeRegistered()'s class-string argument and narrow getRegistry()'s return value - Make the MoneyEntity fixture properties public - Ignore the DBAL >= 4.5 only getTypeProvider()/TypeProvider references, tuned to the stable DBAL that CI analyses
9cae0b0 to
22c0de0
Compare
| continue; | ||
| } | ||
|
|
||
| $services[$tag['type_name'] ?? $tag['type'] ?? $id] = new Reference($id); |
There was a problem hiding this comment.
why supporting 2 different attribute names for the type name ?
There was a problem hiding this comment.
Good point. I liked type better for the explicit tag configuration (shorter, reads nicely), but type_name is already the key shipped in 3.3.0, so keeping it avoids a BC break for anyone tagging a service by hand. I dropped the extra fallback and kept type_name as the only key.
The 'type' tag attribute added on this branch was never released; 3.3.0 shipped 'type_name'. Drop the duplicate 'type' fallback and keep 'type_name' as the sole key. The type name still defaults to the class name, like Symfony's Autoconfigure. Also refine the AsDbalType docblock and version requirements.
Update the
#[AsDbalType]PHP attribute and aRegisterDbalTypePasscompiler pass to register Doctrine DBAL types as Symfony services with a per-connectionTypeRegistry, without touching the global static type registry.The PR #2197 that added this attribute only supports injection of class names into the configuration. This PR requires DBAL and ORM changes to inject services.
How it works
#[AsDbalType(name: 'money')]attribute tags the service withdoctrine.dbal.typeRegisterDbalTypePassbuilds aTypeRegistryper connection, populated with:doctrine.dbal.types) as inline definitionsServiceLocatorTypeRegistryis wired to both the DBALConfigurationand the ORMConfigurationof each associated entity manager#[AsDbalType(name: 'money', connection: 'reporting')]Because types are registered as services and resolved lazily, they support full dependency injection: a type class can declare constructor arguments and receive autowired or explicitly configured dependencies.
Example
Backward compatibility (DBAL < 4.5)
Per-connection
TypeRegistryinjection requires DBAL >= 4.5. On older versions, the compiler pass falls back to registering the types in the global type registry throughdoctrine.dbal.connection_factory.types. This fallback has two limitations, both enforced at compile time:new $class(), so a type whose constructor has mandatory arguments is rejected with a clear error message.In this fallback, the type service definition is marked
container.excludedsince the type is instantiated by DBAL rather than consumed as a service.Dependencies
This PR depends on changes in DBAL and ORM to support per-connection
TypeRegistry:TypeRegistrytoConfigurationand use it in all internal type resolution dbal#7342Type::*calls with instance-basedTypeRegistrylookups orm#12421Demo
A working example using
#[AsDbalType]to store emails as base64 in the database is available at: GromNaN/symfony-demo#5