Weave 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

Doctrine Extensions Bundle Laravel Package

dayploy/doctrine-extensions-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Bundle

    composer require dayploy/doctrine-extensions-bundle
    

    Add to config/bundles.php:

    Dayploy\DoctrineExtensionsBundle\DayployDoctrineExtensionsBundle::class => ['all' => true],
    
  2. Generate Encryption Key

    ./bin/console dayploy:doctrine-extensions:generate
    

    Store the output in .env as DOCTRINE_ENCRYPTION_KEY.

  3. Annotate an Entity

    use Dayploy\DoctrineExtensionsBundle\Annotation\Cryptable;
    use Doctrine\ORM\Mapping as ORM;
    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;
    }
    
  4. Run Migrations Add BINARY columns to your database schema via Laravel migrations.

First Use Case

Encrypt a sensitive field (e.g., ssn) in a User entity:

$user = new User();
$user->ssn = '123-45-6789'; // Automatically encrypted on persist
$entityManager->persist($user);
$entityManager->flush();

Retrieve the decrypted value:

$user = $entityManager->find(User::class, $id);
echo $user->ssn; // Decrypted transparently

Implementation Patterns

Core Workflow

  1. Entity Design

    • Use the @Cryptable annotation on string properties.
    • Define nonceProperty and encryptedProperty as BINARY columns.
    • Example for a Payment entity:
      #[Cryptable(
          nonceProperty: 'cardNonce',
          encryptedProperty: 'cardEncrypted'
      )]
      private ?string $cardNumber = null;
      
      #[ORM\Column(type: Types::BINARY, nullable: true)]
      private ?string $cardEncrypted = null;
      
      #[ORM\Column(type: Types::BINARY, nullable: true)]
      private ?string $cardNonce = null;
      
  2. Lifecycle Integration

    • The bundle hooks into Doctrine’s prePersist, preUpdate, postLoad events.
    • Custom Logic: Extend via Doctrine event subscribers:
      use Doctrine\Common\EventSubscriber;
      use Doctrine\ORM\Event\LifecycleEventArgs;
      
      class EncryptionLogger implements EventSubscriber
      {
          public function getSubscribedEvents()
          {
              return ['postLoad'];
          }
      
          public function postLoad(LifecycleEventArgs $args)
          {
              $entity = $args->getObject();
              if ($entity instanceof User && $entity->ssn) {
                  // Log decryption event (e.g., to SIEM)
              }
          }
      }
      
  3. Hybrid Laravel/Doctrine

    • For Laravel apps, use Doctrine DBAL for encrypted fields:
      use Doctrine\DBAL\Connection;
      
      $connection = new Connection([...]);
      $stmt = $connection->prepare("SELECT * FROM users WHERE id = ?");
      $stmt->execute([$id]);
      $user = $stmt->fetchAssociative();
      $decryptedSsn = $this->decrypt($user['ssn_encrypted'], $user['ssn_nonce']);
      
    • Trait for Eloquent:
      trait EncryptedAttributes
      {
          protected function decrypt(string $encrypted, string $nonce): string
          {
              // Implement or delegate to the bundle's logic
          }
      
          public function getSsnAttribute($value)
          {
              return $this->decrypt($this->ssnEncrypted, $this->ssnNonce);
          }
      }
      
  4. Batch Operations

    • For bulk encryption (e.g., backfilling existing data):
      $users = $entityManager->createQuery('SELECT u FROM App\Entity\User')->getResult();
      foreach ($users as $user) {
          $user->ssn = '123-45-6789'; // Triggers encryption
          $entityManager->flush();
          $entityManager->clear(); // Reduce memory usage
      }
      

Integration Tips

  • Key Management

    • Store DOCTRINE_ENCRYPTION_KEY in Laravel’s config/services.php or use environment variables:
      'encryption' => [
          'key' => env('DOCTRINE_ENCRYPTION_KEY'),
      ],
      
    • Rotate keys periodically (e.g., quarterly) and re-encrypt data:
      ./bin/console dayploy:doctrine-extensions:reencrypt --entity=User --property=ssn
      
  • Performance Optimization

    • Cache decrypted values in Redis with a short TTL (e.g., 5 minutes):
      $cacheKey = "user:{$user->id}:ssn";
      if (!$cachedSsn = cache()->get($cacheKey)) {
          $cachedSsn = $user->ssn;
          cache()->put($cacheKey, $cachedSsn, now()->addMinutes(5));
      }
      
  • Testing

    • Mock the encryption key in tests:
      $this->app['config']['services.encryption.key'] = 'test_key_123';
      
    • Test edge cases:
      • Null values.
      • Concurrent writes.
      • Partial updates (e.g., UPDATE users SET ssn = NULL).
  • Multi-Tenancy

    • Use tenant-specific encryption keys:
      #[Cryptable(
          nonceProperty: 'tenantTokenNonce',
          encryptedProperty: 'tenantTokenEncrypted',
          keyProperty: 'tenantId' // Custom key per tenant
      )]
      private ?string $apiToken = null;
      

Gotchas and Tips

Pitfalls

  1. Key Management

    • Pitfall: Losing the encryption key results in permanent data loss.
      • Fix: Store backup keys in a secure vault (e.g., AWS KMS, HashiCorp Vault) and document recovery procedures.
    • Pitfall: Hardcoding keys in config files.
      • Fix: Use Laravel’s .env and restrict file permissions (chmod 600 .env).
  2. Schema Conflicts

    • Pitfall: Existing BINARY columns may conflict with the bundle’s requirements.
      • Fix: Rename conflicting columns or use a migration to back up data before adding new columns.
  3. Doctrine Event Order

    • Pitfall: Custom event subscribers may interfere with the bundle’s lifecycle events.
      • Fix: Ensure your subscribers run after the bundle’s listeners:
        $eventManager->addEventSubscriber(new EncryptionLogger(), 100); // High priority
        
  4. Binary Data Handling

    • Pitfall: Serializing/deserializing entities with BINARY fields can fail.
      • Fix: Exclude encrypted fields from serialization:
        #[ORM\ExcludeFromSerialization]
        private ?string $ssnEncrypted = null;
        
  5. Partial Updates

    • Pitfall: Setting a cryptable field to null may not clear the BINARY columns.
      • Fix: Override the setter:
        public function setSsn(?string $ssn): void
        {
            $this->ssn = $ssn;
            if ($ssn === null) {
                $this->ssnEncrypted = null;
                $this->ssnNonce = null;
            }
        }
        
  6. Performance Bottlenecks

    • Pitfall: Encryption/decryption during every find() or save() can slow down high-traffic endpoints.
      • Fix: Use selective hydration (load only encrypted fields when needed):
        $user = $entityManager->find(User::class, $id, ['ssnEncrypted', 'ssnNonce']);
        

Debugging

  1. Decryption Failures

    • Symptom: Decryption failed or Invalid nonce errors.
    • Debug Steps:
      • Verify the encryption key in .env.
      • Check for corrupted BINARY data in the database (use HEX() in MySQL to inspect).
      • Ensure the nonce is not null when decrypting.
  2. Event Listener Conflicts

    • Symptom: Entities not encrypting/decrypting as expected.
    • Debug Steps:
      • Disable other Doctrine
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.
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc