dayploy/doctrine-extensions-bundle
Install the Bundle
composer require dayploy/doctrine-extensions-bundle
Add to config/bundles.php:
Dayploy\DoctrineExtensionsBundle\DayployDoctrineExtensionsBundle::class => ['all' => true],
Generate Encryption Key
./bin/console dayploy:doctrine-extensions:generate
Store the output in .env as DOCTRINE_ENCRYPTION_KEY.
Annotate an Entity
use Dayploy\DoctrineExtensionsBundle\Annotation\Cryptable;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\DBAL\Types\Types;
#[ORM\Entity]
class User
{
#[Cryptable(
nonceProperty: 'ssnNonce',
encryptedProperty: 'ssnEncrypted'
)]
private ?string $ssn = null;
#[ORM\Column(type: Types::BINARY, nullable: true)]
private ?string $ssnEncrypted = null;
#[ORM\Column(type: Types::BINARY, nullable: true)]
private ?string $ssnNonce = null;
}
Run Migrations
Add BINARY columns to your database schema via Laravel migrations.
Encrypt a sensitive field (e.g., ssn) in a User entity:
$user = new User();
$user->ssn = '123-45-6789'; // Automatically encrypted on persist
$entityManager->persist($user);
$entityManager->flush();
Retrieve the decrypted value:
$user = $entityManager->find(User::class, $id);
echo $user->ssn; // Decrypted transparently
Entity Design
@Cryptable annotation on string properties.nonceProperty and encryptedProperty as BINARY columns.Payment entity:
#[Cryptable(
nonceProperty: 'cardNonce',
encryptedProperty: 'cardEncrypted'
)]
private ?string $cardNumber = null;
#[ORM\Column(type: Types::BINARY, nullable: true)]
private ?string $cardEncrypted = null;
#[ORM\Column(type: Types::BINARY, nullable: true)]
private ?string $cardNonce = null;
Lifecycle Integration
prePersist, preUpdate, postLoad events.use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
class EncryptionLogger implements EventSubscriber
{
public function getSubscribedEvents()
{
return ['postLoad'];
}
public function postLoad(LifecycleEventArgs $args)
{
$entity = $args->getObject();
if ($entity instanceof User && $entity->ssn) {
// Log decryption event (e.g., to SIEM)
}
}
}
Hybrid Laravel/Doctrine
use Doctrine\DBAL\Connection;
$connection = new Connection([...]);
$stmt = $connection->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetchAssociative();
$decryptedSsn = $this->decrypt($user['ssn_encrypted'], $user['ssn_nonce']);
trait EncryptedAttributes
{
protected function decrypt(string $encrypted, string $nonce): string
{
// Implement or delegate to the bundle's logic
}
public function getSsnAttribute($value)
{
return $this->decrypt($this->ssnEncrypted, $this->ssnNonce);
}
}
Batch Operations
$users = $entityManager->createQuery('SELECT u FROM App\Entity\User')->getResult();
foreach ($users as $user) {
$user->ssn = '123-45-6789'; // Triggers encryption
$entityManager->flush();
$entityManager->clear(); // Reduce memory usage
}
Key Management
DOCTRINE_ENCRYPTION_KEY in Laravel’s config/services.php or use environment variables:
'encryption' => [
'key' => env('DOCTRINE_ENCRYPTION_KEY'),
],
./bin/console dayploy:doctrine-extensions:reencrypt --entity=User --property=ssn
Performance Optimization
$cacheKey = "user:{$user->id}:ssn";
if (!$cachedSsn = cache()->get($cacheKey)) {
$cachedSsn = $user->ssn;
cache()->put($cacheKey, $cachedSsn, now()->addMinutes(5));
}
Testing
$this->app['config']['services.encryption.key'] = 'test_key_123';
UPDATE users SET ssn = NULL).Multi-Tenancy
#[Cryptable(
nonceProperty: 'tenantTokenNonce',
encryptedProperty: 'tenantTokenEncrypted',
keyProperty: 'tenantId' // Custom key per tenant
)]
private ?string $apiToken = null;
Key Management
.env and restrict file permissions (chmod 600 .env).Schema Conflicts
BINARY columns may conflict with the bundle’s requirements.
Doctrine Event Order
$eventManager->addEventSubscriber(new EncryptionLogger(), 100); // High priority
Binary Data Handling
BINARY fields can fail.
#[ORM\ExcludeFromSerialization]
private ?string $ssnEncrypted = null;
Partial Updates
null may not clear the BINARY columns.
public function setSsn(?string $ssn): void
{
$this->ssn = $ssn;
if ($ssn === null) {
$this->ssnEncrypted = null;
$this->ssnNonce = null;
}
}
Performance Bottlenecks
find() or save() can slow down high-traffic endpoints.
$user = $entityManager->find(User::class, $id, ['ssnEncrypted', 'ssnNonce']);
Decryption Failures
Decryption failed or Invalid nonce errors..env.BINARY data in the database (use HEX() in MySQL to inspect).null when decrypting.Event Listener Conflicts
How can I help you explore Laravel packages today?