Overview
In this guide, you can learn how to create a Symfony application that uses Doctrine MongoDB ODM to implement MongoDB's Queryable Encryption feature.
Queryable Encryption allows you to automatically encrypt and decrypt document fields. You can use Queryable Encryption to encrypt sensitive data in your application, store data fields as randomized encrypted data on the server, and query the encrypted fields. This tutorial shows you how to set up Queryable Encryption by using Doctrine MongoDB ODM, which provides an Object Document Mapper (ODM) for PHP applications.
Tip
To learn more about integrating Symfony and Doctrine with the MongoDB PHP Library, see the Symfony MongoDB Integration tutorial.
Prerequisites
Before you begin this tutorial, complete the following prerequisite tasks:
Create a MongoDB Atlas account and configure a cluster. Ensure that your cluster runs on MongoDB Server version 7.0 or later. To learn more, see the MongoDB Get Started guide.
Download the Automatic Encryption Shared Library. To view instructions, see the Install and Configure a Query Analysis Component guide.
Download the PHP extension. To learn more, see Installation in the extension documentation.
Install PHP 8.1 or later.
Install Composer 2.0 or later.
Install the Symfony CLI. To learn more, see the Symfony CLI documentation.
Tutorial
This tutorial shows how to create a Queryable Encryption application that uses Symfony and Doctrine MongoDB ODM. The application encrypts and decrypts patient medical records and queries encrypted medical data.
Set Up Your Project
Follow the steps in this section to install the project dependencies, configure your environment, and create the application structure.
Set up your environment.
Run the following commands in your terminal to create a new Symfony application and install the necessary dependencies:
symfony new qe-symfony-app cd qe-symfony-app composer require doctrine/mongodb-odm-bundle mongodb/mongodb
These commands create a qe-symfony-app project directory
and install the following dependencies:
Doctrine MongoDB ODM Bundle, the Symfony integration for Doctrine MongoDB ODM
MongoDB PHP Library, the official MongoDB driver for PHP
Specify your environment variables.
In your project directory, open the .env file and add or modify the
following variables:
MONGODB_URI=<connection URI> CRYPT_SHARED_LIB_PATH=<Automatic Encryption Shared Library path>
Replace the <connection URI> placeholder with the
connection URI that connects to your cluster, and replace
<Automatic Encryption Shared Library path> with the full
path to your Automatic Encryption Shared Library. Ensure the
path points to lib/mongo_crypt_v1.dylib inside your
downloaded package.
Create your application files.
In your project's src/ directory, create the following
subdirectories and files to define the document structure and
application logic:
src/Document/Patient.phpsrc/Document/PatientRecord.phpsrc/Document/Billing.phpsrc/Command/QueryableEncryptionCommand.php
Future steps in this tutorial instruct you to add code to each file.
Define your document classes.
This tutorial uses Patient, PatientRecord, and
Billing classes to represent patient medical records.
Paste the following code into the src/Document/Patient.php file:
namespace App\Document; use Doctrine\ODM\MongoDB\Mapping\Attribute as ODM; #[ODM\Document(collection: 'patients')] class Patient { #[ODM\Id] public string $id; #[ODM\Field] public string $patientName; #[ODM\Field] public int $patientId; #[ODM\EmbedOne(targetDocument: PatientRecord::class)] public PatientRecord $patientRecord; }
Then, paste the following code into the src/Document/PatientRecord.php file:
namespace App\Document; use Doctrine\ODM\MongoDB\Mapping\Attribute as ODM; use Doctrine\ODM\MongoDB\Mapping\EncryptQuery; #[ODM\EmbeddedDocument] class PatientRecord { #[ODM\Field] #[ODM\Encrypt(queryType: EncryptQuery::Equality)] public string $ssn; #[ODM\EmbedOne(targetDocument: Billing::class)] #[ODM\Encrypt] public Billing $billing; #[ODM\Field] public int $billAmount; }
Finally, paste the following code into the src/Document/Billing.php file:
namespace App\Document; use Doctrine\ODM\MongoDB\Mapping\Attribute as ODM; #[ODM\EmbeddedDocument] class Billing { #[ODM\Field] public string $type; #[ODM\Field] public string $number; }
The document classes define the structure for documents in
the patients collection. The PatientRecord class includes
the #[ODM\Encrypt] attribute to specify the following
encrypted fields:
patientRecord.ssn: Encrypted and configured for equality queriespatientRecord.billing: Encrypted, but not queryable
Create your console command.
Paste the following code into the
src/Command/QueryableEncryptionCommand.php file:
namespace App\Command; use App\Document\Billing; use App\Document\Patient; use App\Document\PatientRecord; use Doctrine\ODM\MongoDB\Configuration; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver; use MongoDB\BSON\Binary; use MongoDB\Client; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; ( name: 'app:queryable-encryption', description: 'Demonstrates Queryable Encryption with ' . 'Doctrine MongoDB ODM', ) class QueryableEncryptionCommand extends Command { protected function execute( InputInterface $input, OutputInterface $output, ): int { // Paste application variables below // Paste encryption credentials below // Paste connection and configuration below // Paste collection setup below // Paste insert operation below // Paste encrypted query below return Command::SUCCESS; } }
Future steps in this tutorial instruct you to add code under each corresponding comment.
Encrypt and Connect to MongoDB
After setting up your project files and dependencies, follow the steps in this section to configure your encryption credentials and connect to MongoDB.
Assign your application variables.
In your src/Command/QueryableEncryptionCommand.php file, add
the following code under the // Paste application variables below comment:
// Paste application variables below $keyVaultNamespace = 'encryption.__keyVault'; $encryptedDatabase = 'medicalRecords'; $encryptedCollection = 'patients';
The code sets the following variables:
keyVaultNamespace - The namespace in MongoDB that stores your Data Encryption Keys (DEKs). This tutorial uses the
__keyVaultcollection in theencryptiondatabase.encryptedDatabase - The database that stores your encrypted data. This tutorial uses the
medicalRecordsdatabase.encryptedCollection - The collection that stores your encrypted data. This tutorial uses the
patientscollection.
Configure your Customer Master Key.
Add the following code under the
// Paste encryption credentials below comment:
// Paste encryption credentials below $keyFile = __DIR__ . '/../../master-key.bin'; if (!file_exists($keyFile)) { file_put_contents($keyFile, random_bytes(96)); } $masterKeyBytes = file_get_contents($keyFile); $kmsProvider = [ 'type' => 'local', 'key' => new Binary( $masterKeyBytes, Binary::TYPE_GENERIC, ), ];
This code creates or loads a 96-byte local Customer Master
Key file. The $kmsProvider array configures the local
KMS provider used for encryption.
Warning
Local CMK Storage
This tutorial stores your Customer Master Key in a local file. Do not use this approach in production. Without a remote KMS, you risk unauthorized access to the encryption key or loss of the key needed to decrypt your data.
Configure your auto-encryption options.
Add the following code directly under the KMS provider configuration from the previous step:
$autoEncryptionOptions = [ 'keyVaultNamespace' => $keyVaultNamespace, ]; $cryptSharedLibPath = $_ENV['CRYPT_SHARED_LIB_PATH'] ?? ''; if ($cryptSharedLibPath) { $autoEncryptionOptions['extraOptions'] = [ 'cryptSharedLibPath' => $cryptSharedLibPath, ]; }
This code sets the key vault namespace and configures the path to the Automatic Encryption Shared Library.
Configure your DocumentManager.
Add the following code under the
// Paste connection and configuration below comment:
// Paste connection and configuration below $cacheDir = __DIR__ . '/../../var/cache/doctrine'; $config = new Configuration(); $config->setAutoEncryption($autoEncryptionOptions); $config->setKmsProvider($kmsProvider); $config->setProxyDir($cacheDir . '/Proxies'); $config->setProxyNamespace('Proxies'); $config->setHydratorDir($cacheDir . '/Hydrators'); $config->setHydratorNamespace('Hydrators'); $config->setDefaultDB($encryptedDatabase); $config->setMetadataDriverImpl( new AttributeDriver([__DIR__ . '/../Document']) ); $client = new Client( uri: $_ENV['MONGODB_URI'], uriOptions: [], driverOptions: $config->getDriverOptions(), ); $dm = DocumentManager::create($client, $config);
This code configures a Doctrine Configuration object
with your encryption settings, creates a MongoDB client
that passes encryption driver options, and initializes a
DocumentManager for encrypted operations.
Create your encrypted collection.
Add the following code under the
// Paste collection setup below comment:
// Paste collection setup below $schemaManager = $dm->getSchemaManager(); $schemaManager->dropDocumentCollection(Patient::class); $schemaManager->createDocumentCollection(Patient::class);
This code drops any existing collection and creates a new
patients collection with the encryption metadata
required for Queryable Encryption. Each call to
createDocumentCollection() generates new Data
Encryption Keys for the encrypted fields.
Perform Encrypted Operations
After configuring your application and database connection, follow the steps in this section to insert and query encrypted documents.
Insert encrypted data.
In your src/Command/QueryableEncryptionCommand.php file, add
the following code under the // Paste insert operation below comment:
// Paste insert operation below $billing = new Billing(); $billing->type = 'Visa'; $billing->number = '4111111111111111'; $record = new PatientRecord(); $record->ssn = '987-65-4320'; $record->billing = $billing; $record->billAmount = 1500; $patient = new Patient(); $patient->patientName = 'Jon Doe'; $patient->patientId = 12345678; $patient->patientRecord = $record; $dm->persist($patient); $dm->flush(); $dm->clear(); $output->writeln( 'Successfully inserted the patient document.' );
Queryable Encryption automatically encrypts the
patientRecord.ssn and patientRecord.billing fields
before storing the document in MongoDB.
Query encrypted data.
Add the following code under the
// Paste encrypted query below comment:
// Paste encrypted query below $found = $dm ->getRepository(Patient::class) ->findOneBy(['patientRecord.ssn' => '987-65-4320']); if ($found instanceof Patient) { $output->writeln('Found patient:'); $output->writeln( ' Name: ' . $found->patientName ); $output->writeln( ' SSN: ' . $found->patientRecord->ssn ); $output->writeln( ' Billing type: ' . $found->patientRecord->billing->type ); $output->writeln( ' Billing number: ' . $found->patientRecord->billing->number ); $output->writeln( ' Bill amount: ' . $found->patientRecord->billAmount ); } $output->writeln('Connection closed.');
This code performs an equality query on the encrypted
patientRecord.ssn field.
Run your application.
To start your application, run the following command from your
qe-symfony-app project directory:
php bin/console app:queryable-encryption
If successful, your command output resembles the following example:
Successfully inserted the patient document. Found patient: Name: Jon Doe SSN: 987-65-4320 Billing type: Visa Billing number: 4111111111111111 Bill amount: 1500 Connection closed.
In the database, the document is encrypted. MongoDB stores fields marked
with the #[ODM\Encrypt] attribute as BSON binary data and includes
a __safeContent__ field for metadata tags. The stored document resembles
the following:
{ "_id": { "$oid": "..." }, "patientName": "Jon Doe", "patientId": 12345678, "patientRecord": { "ssn": { "$binary": { ... } }, "billing": { "$binary": { ... } }, "billAmount": 1500 }, "__safeContent__": [ { "$binary": { ... } } ] }
Next Steps
Congratulations on completing the Doctrine ODM Queryable Encryption tutorial! You now have a sample Symfony application that uses Queryable Encryption to automatically encrypt and decrypt document fields. Your application encrypts sensitive data on the server side and queries the data on the client side.
To learn more about Queryable Encryption and Doctrine MongoDB ODM, visit the following resources:
View more driver Queryable Encryption tutorials in the MongoDB Server manual.
Learn more about Doctrine MongoDB ODM in the Doctrine MongoDB ODM documentation.
Learn more about integrating Doctrine MongoDB ODM with Symfony in the Symfony MongoDB Integration tutorial.