Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Key Value Store Laravel Package

doctrine/key-value-store

Doctrine Key Value Store provides a lightweight Doctrine-style mapper for NoSQL key-value backends. Use simple @Entity/@Id annotations, schema-less values mapped to objects, and a stripped-down object manager with events. Drivers include Redis, DynamoDB, MongoDB, CouchDB and more.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require doctrine/key-value-store
    
  2. Define a Key-Value Entity Annotate a class with @KeyValue\Entity and mark at least one property as @KeyValue\Id.

    use Doctrine\KeyValueStore\Mapping\Annotations as KeyValue;
    
    /**
     * @KeyValue\Entity(storageName="user_sessions")
     */
    class UserSession
    {
        /** @KeyValue\Id */
        private $sessionId;
    
        private $userId;
        private $expiresAt;
    }
    
  3. Configure the EntityManager Choose a storage backend (e.g., Redis, Doctrine Cache, or DBAL) and initialize the EntityManager.

    use Doctrine\KeyValueStore\EntityManager;
    use Doctrine\KeyValueStore\Configuration;
    use Doctrine\KeyValueStore\Storage\RedisStorage;
    use Doctrine\KeyValueStore\Mapping\AnnotationDriver;
    use Doctrine\Common\Annotations\AnnotationReader;
    
    $redis = new \Redis();
    $redis->connect('127.0.0.1', 6379);
    
    $storage = new RedisStorage($redis);
    $reader = new AnnotationReader();
    $metadataDriver = new AnnotationDriver($reader);
    
    $config = new Configuration();
    $config->setMetadataDriverImpl($metadataDriver);
    
    $entityManager = new EntityManager($storage, $config);
    
  4. First Use Case: CRUD Operations

    // Create
    $session = new UserSession();
    $session->setSessionId('abc123');
    $session->setUserId(1);
    $session->setExpiresAt(new \DateTime('+1 hour'));
    $entityManager->persist($session);
    $entityManager->flush();
    
    // Read
    $session = $entityManager->find('UserSession', ['sessionId' => 'abc123']);
    
    // Update
    $session->setExpiresAt(new \DateTime('+2 hours'));
    $entityManager->flush();
    
    // Delete
    $entityManager->remove($session);
    $entityManager->flush();
    

Implementation Patterns

Core Workflows

  1. Schema-less Data Handling Use the package to store unstructured data (e.g., caching, session storage, or temporary objects) without defining a rigid schema.

    /**
     * @KeyValue\Entity(storageName="temp_data")
     */
    class TempData
    {
        /** @KeyValue\Id */
        private $key;
    
        private $data; // Can be any serializable value (array, object, etc.)
    }
    
  2. Multi-Key Entities Leverage composite keys for hierarchical or relational data (e.g., user_id + feature_id for feature flags).

    /**
     * @KeyValue\Entity(storageName="feature_flags")
     */
    class FeatureFlag
    {
        /** @KeyValue\Id */
        private $userId;
    
        /** @KeyValue\Id */
        private $featureId;
    
        private $isEnabled;
    }
    
  3. Event-Driven Extensions Use the KeyValueStoreEventListener to hook into lifecycle events (e.g., log changes or trigger actions on postPersist).

    use Doctrine\KeyValueStore\Event\KeyValueStoreEventArgs;
    use Doctrine\KeyValueStore\Event\KeyValueStoreEvents;
    
    $entityManager->getEventManager()->addEventListener(
        KeyValueStoreEvents::postPersist,
        function (KeyValueStoreEventArgs $args) {
            // Custom logic (e.g., analytics, notifications)
        }
    );
    
  4. Hybrid Storage with Doctrine ORM Embed key-value entities within ORM entities using @KeyValue\Embedded (if supported by the backend).

    use Doctrine\ORM\Mapping as ORM;
    use Doctrine\KeyValueStore\Mapping\Annotations as KeyValue;
    
    /**
     * @ORM\Entity
     */
    class UserProfile
    {
        /** @ORM\Id */
        private $id;
    
        /** @KeyValue\Embedded */
        private $preferences; // Instance of a KeyValue entity
    }
    

Integration Tips

  1. Backend-Specific Optimizations

    • Redis: Use for high-performance caching or real-time data.
      $storage = new RedisStorage($redis, 'prefix_');
      
    • DBAL: Ideal for persistence in relational databases (e.g., PostgreSQL).
      $storage = new DBALStorage($conn, 'kv_store', 'id', 'data');
      
    • Azure Table: For serverless or cloud-native applications.
      $storage = new AzureSdkTableStorage($tableService, 'Entity');
      
  2. Bulk Operations Use EntityManager::findBy() for querying multiple entities at once.

    $flags = $entityManager->findBy('FeatureFlag', ['userId' => 1]);
    
  3. Metadata Caching Improve performance by caching metadata (e.g., with Doctrine\Common\Cache\ApcuCache).

    $config->setMetadataCache(new ApcuCache());
    
  4. Testing Mock the Storage interface for unit tests:

    $mockStorage = $this->createMock(Storage::class);
    $entityManager = new EntityManager($mockStorage, $config);
    

Gotchas and Tips

Pitfalls

  1. Serialization Limits

    • Some backends (e.g., Redis) have size limits for serialized data. Avoid storing large objects directly.
    • Workaround: Offload large data to files or another storage system and store only references (e.g., paths or IDs) in the key-value store.
  2. Composite Key Quirks

    • Not all backends support composite keys natively. For example, Doctrine Cache Storage may require manual key concatenation.
    • Workaround: Use a unique string representation (e.g., JSON-encoded arrays) for composite keys.
      $key = json_encode(['campaign' => '1234', 'recipient' => 'user@example.com']);
      
  3. Transaction Isolation

    • Key-value stores are often non-transactional. Use flush() judiciously to avoid partial writes.
    • Workaround: Wrap operations in a try-catch block and handle rollbacks manually.
  4. Metadata Driver Conflicts

    • Mixing annotation, XML, or YAML drivers may cause conflicts if not configured correctly.
    • Tip: Stick to one driver per project unless you have a specific need for multi-format support.
  5. Deprecated Backends

    • Some storages (e.g., CouchDB, Riak) are implemented but may lack maintenance. Test thoroughly before production use.

Debugging Tips

  1. Enable Logging Configure the EntityManager to log operations:

    $entityManager->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
    
  2. Inspect Raw Storage For debugging, bypass the EntityManager and interact directly with the storage (e.g., Redis CLI or DBAL queries).

  3. Validate Annotations Use the MetadataFactory to check if your entities are correctly mapped:

    $metadata = $entityManager->getMetadataFactory()->getMetadataFor('Response');
    
  4. Handle Serialization Errors Ensure all properties are serializable. Use __serialize()/__unserialize() for custom logic:

    class UserSession implements \Serializable
    {
        public function serialize()
        {
            return serialize([
                'sessionId' => $this->sessionId,
                'userId' => $this->userId,
                'expiresAt' => $this->expiresAt->getTimestamp(),
            ]);
        }
    
        public function unserialize($data)
        {
            $data = unserialize($data);
            $this->sessionId = $data['sessionId'];
            $this->userId = $data['userId'];
            $this->expiresAt = new \DateTime();
            $this->expiresAt->setTimestamp($data['expiresAt']);
        }
    }
    

Extension Points

  1. Custom Storage Backend Implement the Doctrine\KeyValueStore\Storage\Storage interface for unsupported backends (e.g., Memcached, Etcd).

    class CustomStorage implements Storage
    {
        public function find($className, $key)
        {
            // Custom logic to fetch data
        }
    
        public function persist($entity)
        {
            // Custom logic to save data
        }
    
        // Implement other required methods...
    }
    
  2. Event Subscribers Extend functionality with subscribers for events like preFlush or postRemove.

    $eventManager->addEventSubscriber(new class implements EventSubscriber {
        public function getSubscribedEvents()
        {
            return [
                KeyValueStoreEvents::preFlush => 'onPreFlush',
            ];
        }
    
        public function
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity