I am using a Doctrine2 type to encrypt database values. The type converts the PHP value internally to and from a database value by encrypting and decrypting it. This works perfectly due to the Doctrine2 types.
The encryption is stored as a base64 encoded string. Every encrypted string is prefixed by a fixed defined prefix. This means the database field contains both encrypted and decrypted values (this is required by external requirements), recognized by the prefix.
My wish is the following:
Assume I have an entity. I want to force the encryption or decryption of all properties of an entity using Doctrine. I do this by forcing the database value within the type to be stored in the encrypted or decrypted form.
However, when I call the method EntityManager::computeChangeSets
, the none of the properties of the entity is marked as changed. Of course the actual data (the PHP values) are not changed. However, the database values do (are supposed to) change.
How to accomplish this?
Some code of the Doctrine type:
<?php
use Doctrine\DBAL\Types\Type;
class EncryptedType extends Type {
private static $forceDecrypt = false;
// Encryption stuff, like encrypt () and decrypt ()
public function convertToPHPValue($value, AbstractPlatform $platform) {
if ($value === null) {
return null;
}
return $this -> decrypt($value, false);
}
public function convertToDatabaseValue($value, AbstractPlatform $platform) {
if ($value === null) {
return null;
}
if (self::$forceDecrypt) {
return (string) $value;
}
return $this -> encrypt((string) $value, false);
}
}
I have removed all unneeded code.
Isn't it a bug?
Workaround: If the entity is managed by the manager, then it has a state STATE_MANAGED
, when you will change it to STATE_NEW
and force update, this can solve your problem.
I have found an answer to my own question after many hours of diving into Doctrine code.
Below the outline of my solution is shown to help others.
First of all I created a simple value class, which can hold a PHP value of any type. It has a __toString()
method to convert it back to the original value.
Code:
class Value {
private $value;
public function __construct($value) {
$this -> value = $value;
}
public function __toString() {
return $this -> value;
}
}
The EncryptedType
class stays exactly the same. The point is the value of the entity. When a property of a class has to be force-updated in the database (and thus make use of the EncryptedType
class), it will be set in the following way:
foreach (self::$properties as $property) {
$value = $accessor -> getValue($entity, $property);
if ($value !== null) {
$accessor -> setValue($entity, $property, new Value($value));
}
}
($accessor
is a property accessor.)
Notice the new Value(...)
wrapper, which will make sure Doctrine notices the change in property value and makes a change in the database. Of course the value will be taken from the __toString()
method, which is exactly what we need.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With