Skip to content

Demonstrate #[AsDatabaseType]#5

Open
GromNaN wants to merge 7 commits intomainfrom
doctrine-type-service
Open

Demonstrate #[AsDatabaseType]#5
GromNaN wants to merge 7 commits intomainfrom
doctrine-type-service

Conversation

@GromNaN
Copy link
Copy Markdown
Owner

@GromNaN GromNaN commented Apr 3, 2026

Demonstrates the new #[AsDatabaseType] attribute from DoctrineBundle that registers DBAL types as Symfony services with a per-connection TypeRegistry.

  • Adds EncryptedEmailType: a custom DBAL type that stores User::$email encrypted in the database using deterministic AES-256-GCM
  • The encryption key is provided via the APP_EMAIL_ENCRYPTION_KEY environment variable, injected into the type via #[Autowire(env: 'APP_EMAIL_ENCRYPTION_KEY')]
  • The key is generated automatically in .env.local on composer install via bin/generate-env-keys
  • The type is registered automatically via #[AsDatabaseType(name: EncryptedEmailType::NAME)] — no doctrine.dbal.types config needed
  • All 61 tests pass (including 8 new unit tests for the type)

Warning

This is for demonstration purposes only. Production-grade field-level encryption requires more advanced techniques for key management (key rotation, envelope encryption, HSM/KMS integration, etc.). This example intentionally keeps it simple to focus on the #[AsDatabaseType] feature.

Important

Key rotation requires reloading fixtures. The encryption key is generated once on composer install. If you regenerate it (e.g. by deleting .env.local), you must reload fixtures to re-encrypt the database with the new key:

php bin/console doctrine:fixtures:load

The test database (data/database_test.sqlite) uses the fixed zero key from .env.test. Regenerate it after changing fixtures or encryption logic:

php bin/console doctrine:fixtures:load --env=test

How it works

use Doctrine\Bundle\DoctrineBundle\Attribute\AsDatabaseType;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

#[AsDatabaseType(name: EncryptedEmailType::NAME)]
final class EncryptedEmailType extends Type
{
    public const string NAME = 'encrypted_email';

    public function __construct(
        #[Autowire(env: 'APP_EMAIL_ENCRYPTION_KEY')]
        string $emailEncryptionKey,
    ) { ... }
}
#[ORM\Column(type: EncryptedEmailType::NAME, unique: true)]
private ?string $email = null;

Dependencies

This demo depends on PRs in 3 repositories:

Demonstrates the new #[AsDatabaseType] attribute from DoctrineBundle
that registers DBAL types as Symfony services with per-connection TypeRegistry,
without touching the global static type registry.
@GromNaN GromNaN changed the title Demonstrate #[AsDatabaseType] with a Base64EmailType Demonstrate #[AsDatabaseType] Apr 3, 2026
GromNaN added 4 commits April 3, 2026 14:16
…Type

Uses a key from APP_EMAIL_ENCRYPTION_KEY env var (generated automatically
on composer install via bin/generate-env-keys if missing).

The nonce is derived deterministically from the key and plaintext so the
same email always produces the same ciphertext, allowing DB lookups to work.
The test database must be encrypted with the key from .env.test
(APP_EMAIL_ENCRYPTION_KEY=000...000) so that CI can decrypt emails
and authenticate users in functional tests.
Note that fixtures must be reloaded when the encryption key changes,
and how to regenerate the test database.
Using 'now + Ns' tied comment dates to the fixture-load instant
and stored them in the local timezone, causing DESC-order failures
on UTC CI runners (fixture comments appeared newer than the test
comment, which was created in UTC).

Anchor comment dates to the post's publishedAt instead, so they
are always deterministic, well in the past, and ordered correctly
regardless of the server timezone.
@GromNaN GromNaN force-pushed the doctrine-type-service branch from d3f009c to f0f1c64 Compare April 3, 2026 12:51
GromNaN added 2 commits April 3, 2026 15:04
HMAC-SHA256 is the correct construction here: the key is the cryptographic
secret and the message is the plaintext. A plain hash(key || message) is
susceptible to length-extension attacks and does not use the key as intended.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant