Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update database values with same PHP values using Doctrine type

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.

like image 877
Hidde Avatar asked Sep 30 '15 15:09

Hidde


Video Answer


2 Answers

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.

like image 80
mvorisek Avatar answered Oct 02 '22 15:10

mvorisek


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.

like image 25
Hidde Avatar answered Oct 02 '22 15:10

Hidde