dontdrinkandroot/doctrine-key-value-storage-bundle
Installation
composer require dontdrinkandroot/doctrine-key-value-storage-bundle
Add to config/bundles.php (Symfony):
return [
// ...
DontDrinkAndRoot\DoctrineKeyValueStorageBundle\DontDrinkAndRootDoctrineKeyValueStorageBundle::class => ['all' => true],
];
Entity Configuration
Define a Doctrine entity with the KeyValueStorage trait:
use DontDrinkAndRoot\DoctrineKeyValueStorageBundle\Entity\KeyValueStorage;
/**
* @ORM\Entity
*/
class UserSettings
{
use KeyValueStorage;
// Your entity fields...
}
First Use Case Store/retrieve a typed value in a Laravel-like context:
$settings = new UserSettings();
$settings->setString('theme', 'dark'); // Store as string
$settings->setInt('notifications', 5); // Store as integer
$settings->setBoolean('is_active', true);
// Later...
$theme = $settings->getString('theme'); // Returns 'dark'
$notifications = $settings->getInt('notifications'); // Returns 5
Dynamic Configuration Storage Use for user-specific or app-wide settings:
// In a service
public function updateUserPreferences(User $user, array $prefs) {
$settings = $user->getSettings(); // Assume relation to UserSettings
foreach ($prefs as $key => $value) {
$settings->setString($key, $value);
}
$entityManager->persist($settings);
}
Caching Layer Combine with Symfony Cache for performance:
$cache = $this->container->get('cache.app');
$key = 'user_prefs_' . $user->getId();
if (!$cache->has($key)) {
$settings = $entityManager->getRepository(UserSettings::class)->findOneBy(['user' => $user]);
$cache->set($key, $settings->getAll(), 3600);
}
Validation Hooks
Override set* methods to enforce business rules:
public function setInt($key, $value) {
if ($key === 'max_retries' && $value > 10) {
throw new \InvalidArgumentException('Max retries cannot exceed 10');
}
parent::setInt($key, $value);
}
prePersist/preUpdate to auto-generate keys or validate:
$eventManager->addEventListener(
KernelEvents::CONTROLLER,
[$this, 'onKernelController']
);
$qb = $entityManager->createQueryBuilder()
->select('u')
->from(UserSettings::class, 'u')
->where('u.getString("theme") = :theme')
->setParameter('theme', 'dark');
DoctrineMigrationsBundle to handle schema changes safely.Type Safety
getString() on a non-string key returns null, not a casted value.
Fix: Use getValue() + manual casting or add a getCastedValue($key, $type) helper.if (!is_int($value)) throw new \InvalidArgumentException('Must be integer');
Performance
getAll() loads all key-value pairs into memory.
Fix: Use findByKey() or limit keys in queries:
$qb->andWhere('u.key IN (:keys)')
->setParameter('keys', ['theme', 'notifications']);
Serialization
DateTime) may not serialize/deserialize correctly.
Fix: Implement __toString() or use setJson() for complex data:
$settings->setJson('preferences', ['last_login' => $user->getLastLogin()]);
Doctrine Lifecycle
$entityManager->refresh($settings);
$entityManager->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
getMetadata() to inspect entity state:
var_dump($settings->getMetadata()->getFieldValue('key_value_data'));
prePersist/preUpdate to debug key-value changes:
$event->getObject()->getKeyValueData(); // Inspect raw data
Custom Types
Extend the trait to add setFloat(), setArray(), etc.:
public function setFloat($key, $value) {
$this->keyValueData[$key] = ['type' => 'float', 'value' => $value];
}
Encryption
Override set* methods to encrypt sensitive data:
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
public function setString($key, $value) {
$this->keyValueData[$key] = [
'type' => 'string',
'value' => $this->encoder->encode($value)
];
}
TTL (Time-to-Live) Add expiration logic via Doctrine lifecycle callbacks:
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\PreUpdate
*/
public function checkExpiration() {
if ($this->expiry && $this->expiry < new \DateTime()) {
$this->keyValueData = [];
}
}
Bulk Operations Create a repository method for batch updates:
public function bulkUpdate(array $updates, User $user) {
$settings = $this->findOneBy(['user' => $user]);
foreach ($updates as $key => $value) {
$settings->setString($key, $value);
}
$this->getEntityManager()->flush();
}
How can I help you explore Laravel packages today?