Installation:
composer require doctrine/couchdb-odm-bundle
Add to config/bundles.php:
return [
// ...
Doctrine\Bundle\CouchDBBundle\DoctrineCouchDBBundle::class => ['all' => true],
];
Basic Configuration (config/packages/doctrine_couch_db.yaml):
doctrine_couch_db:
client:
dbname: 'your_db_name'
host: 'http://localhost:5984'
username: 'admin'
password: 'password'
odm:
auto_mapping: true
First Document:
// src/Entity/User.php
namespace App\Entity;
use Doctrine\ODM\CouchDB\Mapping\Annotations as CouchDB;
/**
* @CouchDB\Document(repositoryClass="App\Repository\UserRepository")
*/
class User
{
/**
* @CouchDB\Id
* @var string
*/
private $id;
/**
* @CouchDB\Field(type="string")
* @var string
*/
private $name;
// Getters/Setters...
}
First Query (in a controller/service):
use Doctrine\ODM\CouchDB\DocumentManager;
public function __construct(DocumentManager $dm) {
$this->dm = $dm;
}
public function index() {
$users = $this->dm->getRepository(User::class)->findAll();
// ...
}
// Create
$user = new User();
$user->setName('John Doe');
$this->dm->persist($user);
$this->dm->flush();
// Read
$user = $this->dm->find(User::class, $id);
// Update
$user->setName('Updated Name');
$this->dm->flush();
// Delete
$this->dm->remove($user);
$this->dm->flush();
use Doctrine\ODM\CouchDB\Query\Query;
$query = $this->dm->createQueryBuilder(User::class)
->field('name')->equals('John Doe')
->getQuery();
$users = $query->execute();
// src/Repository/UserRepository.php
namespace App\Repository;
use Doctrine\ODM\CouchDB\Repository\DocumentRepository;
class UserRepository extends DocumentRepository
{
public function findByName($name) {
return $this->createQueryBuilder()
->field('name')->equals($name)
->getQuery()
->execute();
}
}
// src/EventListener/UserListener.php
namespace App\EventListener;
use Doctrine\ODM\CouchDB\Event\LifecycleEventArgs;
class UserListener
{
public function prePersist(LifecycleEventArgs $args) {
$document = $args->getDocument();
if ($document instanceof User) {
$document->setCreatedAt(new \DateTime());
}
}
}
Register in services.yaml:
services:
App\EventListener\UserListener:
tags:
- { name: 'doctrine_couchdb.odm.event_listener', event: 'prePersist' }
$users = $this->dm->createQueryBuilder(User::class)->getQuery()->execute();
foreach ($users as $user) {
$user->setActive(false);
}
$this->dm->flush(); // Single flush for all updates
// src/Form/UserType.php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name')
->add('email');
}
}
Use with EntityType for document selection if needed.
// src/Entity/User.php
use Symfony\Component\Validator\Constraints as Assert;
class User
{
/**
* @Assert\NotBlank
* @CouchDB\Field(type="string")
*/
private $name;
}
If using API Platform, ensure your document is annotated for serialization:
use ApiPlatform\Core\Annotation\ApiResource;
/**
* @ApiResource()
* @CouchDB\Document
*/
class User { ... }
Use CouchDBTestClient for in-memory testing:
use Doctrine\ODM\CouchDB\Tests\Functional\CouchDBTestClient;
public function testUserCreation() {
$client = new CouchDBTestClient();
$dm = $this->getContainer()->get('doctrine_couchdb.odm.document_manager');
$dm->getConfiguration()->setClient($client);
// Test logic...
}
No Transactions: CouchDB is document-oriented and doesn’t support ACID transactions like SQL databases. Plan your operations accordingly (e.g., use external locks for critical sections).
Schema-less Nature:
Avoid assuming a fixed schema. Use @CouchDB\Field(type="...") explicitly to enforce types where needed.
Idempotency:
CouchDB’s _update handlers must be idempotent. Avoid relying on side effects in updates.
Performance with Large Datasets:
Avoid findAll() on large collections. Use pagination or indexed queries:
$query = $this->dm->createQueryBuilder(User::class)
->field('name')->equals('John')
->skip(0)
->limit(10)
->getQuery();
Case Sensitivity: Field names in queries are case-sensitive. Stick to consistent naming conventions.
Document Size Limits: CouchDB has a 20MB document size limit. For large data, consider attachments or external storage.
Enable SQL Logging:
Add to config/packages/dev/doctrine_couch_db.yaml:
doctrine_couch_db:
odm:
logging: true
Check logs for generated CouchDB queries.
Use explain():
$query = $this->dm->createQueryBuilder(User::class)->getQuery();
$explanation = $query->getQuery()->explain();
Check _changes Feed:
For debugging real-time updates, inspect the _changes feed directly:
curl -X GET http://localhost:5984/your_db/_changes?include_docs=true
Fauxton UI:
Use the built-in Fauxton UI (usually at http://localhost:5984/_utils) to inspect documents and indexes.
Auto-Mapping:
If auto_mapping: true, ensure your Entity classes are in a namespace scanned by the autoloader (e.g., src/Entity/).
Custom Connection Names: Configure multiple databases:
doctrine_couch_db:
connections:
default:
dbname: 'default_db'
analytics:
dbname: 'analytics_db'
host: 'analytics.couchdb.example.com'
odm:
default_connection: default
SSL Configuration:
doctrine_couch_db:
client:
host: 'https://your-couchdb.example.com'
ssl: true
verify_peer: false # Disable for self-signed certs (not recommended for production)
Custom Hydrators: Override hydration for specific fields:
use Doctrine\ODM\CouchDB\Hydrator\HydratorInterface;
class CustomHydrator implements HydratorInterface
{
public function hydrate($data, $document) { ... }
public function extract($document) { ... }
}
Register in services.yaml:
services:
App\Hydrator\CustomHydrator:
tags:
- { name: 'doctrine_couchdb.odm.hydrator', priority: 100 }
Custom Types: Extend Doctrine’s type system for custom field types:
How can I help you explore Laravel packages today?