dayploy/doctrine-extensions-bundle
composer require dayploy/doctrine-extensions-bundle
./bin/console dayploy:doctrine-extensions:generate
Add the generated key to .env:
DOCTRINE_ENCRYPTION_KEY=your_generated_key_here
config/bundles.php):
Dayploy\DoctrineExtensionsBundle\DayployDoctrineExtensionsBundle::class => ['all' => true],
use Doctrine\ORM\Mapping as ORM;
use Dayploy\DoctrineExtensionsBundle\Annotation\Cryptable;
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;
}
use Doctrine\DBAL\Connection;
$connection = new Connection(['url' => 'mysql://user:pass@localhost/db']);
$connection->getSchemaManager()->createTable('users', function ($table) {
$table->addColumn('ssn_encrypted', 'binary', ['length' => 255, 'nullable' => true]);
$table->addColumn('ssn_nonce', 'binary', ['length' => 255, 'nullable' => true]);
});
use Illuminate\Support\Facades\Crypt;
trait Encryptable {
protected static function encrypt($value) {
$nonce = random_bytes(16);
$encrypted = openssl_encrypt($value, 'AES-256-CBC', env('DOCTRINE_ENCRYPTION_KEY'), 0, $nonce);
return ['nonce' => $nonce, 'encrypted' => $encrypted];
}
protected static function decrypt($nonce, $encrypted) {
return openssl_decrypt($encrypted, 'AES-256-CBC', env('DOCTRINE_ENCRYPTION_KEY'), 0, $nonce);
}
}
class User extends Model {
use Encryptable;
protected $ssnEncrypted;
protected $ssnNonce;
public function setSsnAttribute($value) {
$data = self::encrypt($value);
$this->ssnEncrypted = $data['encrypted'];
$this->ssnNonce = $data['nonce'];
}
public function getSsnAttribute() {
return self::decrypt($this->ssnNonce, $this->ssnEncrypted);
}
}
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;
$entityManager->getEventManager()->addEventListener(
Events::prePersist,
function ($entity, LifecycleEventArgs $args) {
if ($entity instanceof User && $entity->ssn) {
$data = self::encrypt($entity->ssn);
$entity->ssnEncrypted = $data['encrypted'];
$entity->ssnNonce = $data['nonce'];
$entity->ssn = null; // Clear plaintext
}
}
);
#[Cryptable(
nonceProperty: 'creditCardNonce',
encryptedProperty: 'creditCardEncrypted',
fields: ['credit_card_number'] // Only encrypt this field
)]
Schema::table('payments', function (Blueprint $table) {
$table->binary('credit_card_nonce')->nullable()->after('credit_card_number');
$table->binary('credit_card_encrypted')->nullable()->after('credit_card_nonce');
});
config/doctrine.php:
'encryption' => [
'key' => env('DOCTRINE_ENCRYPTION_KEY'),
'rotation' => [
'schedule' => '0 0 1 * *', // Monthly rotation
'backup_key' => env('DOCTRINE_ENCRYPTION_KEY_BACKUP'),
],
],
./bin/console doctrine:query "UPDATE users SET ssn_encrypted = ENCRYPT(ssn_encrypted, :new_key)" --param=new_key:env(DOCTRINE_ENCRYPTION_KEY_NEW)
$entityManager->getEventManager()->addEventListener(
Events::postLoad,
function ($entity, LifecycleEventArgs $args) {
if ($entity instanceof User && $entity->ssnEncrypted) {
Log::info('Decrypted SSN for user ' . $entity->id, ['action' => 'ssn_access']);
}
}
);
Key Management:
.env violates 12-factor principles.
config or integrate with AWS KMS/HashiCorp Vault.keys/backup.key.enc).
openssl enc -aes-256-cbc -salt -in keys/backup.key -out keys/backup.key.enc
Binary Column Quirks:
BINARY vs. VARBINARY may cause schema conflicts.
VARBINARY(255) for flexibility.Doctrine Event Conflicts:
prePersist may override each other.
$eventManager->addEventListener(Events::prePersist, [$listener, 'encrypt'], 100);
Performance Bottlenecks:
save() can slow down bulk operations.
$users = User::whereNull('ssn_encrypted')->get();
foreach ($users as $user) {
$data = self::encrypt($user->ssn);
$user->ssnEncrypted = $data['encrypted'];
$user->ssnNonce = $data['nonce'];
$user->save();
}
Data Corruption:
phpMyAdmin) can corrupt BINARY columns.
Laravel Caching Issues:
$this->cache()->forget("user_{$this->id}_ssn");
Decryption Failures:
openssl_decrypt() returns false.var_dump(
hex2bin($entity->ssnNonce),
$entity->ssnEncrypted,
env('DOCTRINE_ENCRYPTION_KEY')
);
.env).BINARY data (verify with HEX() in SQL).Event Listener Not Triggering:
dd() in the listener to confirmHow can I help you explore Laravel packages today?