Installation:
composer require doctrine/phpcr-bundle
Add the bundle to config/bundles.php:
return [
// ...
Doctrine\PHPCR\Bundle\DoctrinePHPCRBundle::class => ['all' => true],
];
Configuration:
Define your PHPCR backend in config/packages/doctrine_phpcr.yaml:
doctrine_phpcr:
document_managers:
default:
connection: default
backend:
type: "jackalope"
jackalope:
client:
type: "doctrine_dbal"
connection: "default"
connections:
default:
backend: "%env(DOCTRINE_PHPCR_BACKEND)%"
username: "%env(DOCTRINE_PHPCR_USERNAME)%"
password: "%env(DOCTRINE_PHPCR_PASSWORD)%"
uri: "%env(DOCTRINE_PHPCR_URI)%"
First Use Case:
Define a document entity (e.g., src/Document/Article.php):
namespace App\Document;
use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCR;
/** @PHPCR\Document */
class Article
{
/** @PHPCR\Id */
private $id;
/** @PHPCR\Field(type="string") */
private $title;
/** @PHPCR\Field(type="string") */
private $content;
// Getters/setters...
}
Use the DocumentManager in a controller:
use Doctrine\ODM\PHPCR\DocumentManager;
public function index(DocumentManager $dm)
{
$article = new Article();
$article->setTitle('Hello PHPCR');
$article->setContent('Content here...');
$dm->persist($article);
$dm->flush();
}
CRUD Operations:
// Create
$dm->persist($document);
$dm->flush();
// Read
$document = $dm->find(Article::class, '/path/to/node');
// Update
$document->setTitle('Updated');
$dm->flush();
// Delete
$dm->remove($document);
$dm->flush();
Hierarchical Data: Leverage PHPCR’s node hierarchy for nested structures:
$parent = $dm->find(Category::class, '/categories/tech');
$child = new Article();
$child->setParent($parent); // Sets parent reference
$dm->persist($child);
Querying: Use PHPCR’s query language (JCR-SQL2) or Doctrine’s query builder:
$query = $dm->createQueryBuilder(Article::class)
->where('title = :title')
->getQuery()
->execute(['title' => 'Hello PHPCR']);
Versioning:
Enable versioning in your doctrine_phpcr.yaml:
doctrine_phpcr:
document_managers:
default:
versioning: true
Access versions:
$versions = $dm->getRepository(Article::class)->findVersions($article);
Multilingual Content:
Use the PHPCR\Field(type="string", locale="en") annotation for localized fields.
Symfony Forms: Bind PHPCR documents to Symfony forms:
$form = $this->createForm(ArticleType::class, $article);
Define the form type with PHPCR-aware fields.
Event Listeners:
Use Doctrine events (e.g., prePersist, postLoad) for custom logic:
$dm->getEventManager()->addEventListener(
'prePersist',
function ($event) {
$document = $event->getDocument();
// Custom logic...
}
);
Custom Repositories:
Extend DocumentRepository for complex queries:
class ArticleRepository extends DocumentRepository
{
public function findByTag($tag)
{
return $this->createQueryBuilder('a')
->where('a.tags = :tag')
->getQuery()
->execute(['tag' => $tag]);
}
}
Migrations:
Use doctrine/phpcr-migrations-bundle for schema migrations:
composer require doctrine/phpcr-migrations-bundle
php bin/console doctrine:phpcr:migrations:diff
php bin/console doctrine:phpcr:migrations:migrate
Backend Configuration:
DOCTRINE_PHPCR_URI (e.g., http://localhost:8080/server for Jackalope).Node Paths:
/-separated paths (e.g., /content/articles). Avoid hardcoding paths; use relative paths or services.$dm->find(Article::class, '/content/articles/1') may break if the root node changes.Caching:
doctrine_phpcr.yaml:
doctrine_phpcr:
document_managers:
default:
cache:
type: "apcu"
namespace: "phpcr_cache"
Transactions:
Annotations vs. XML/YAML:
@PHPCR\Document) for simplicity, but XML/YAML mappings are useful for complex schemas.Case Sensitivity:
Enable PHPCR Logging:
Add to config/packages/monolog.yaml:
handlers:
doctrine_phpcr:
type: stream
path: "%kernel.logs_dir%/phpcr.log"
level: debug
channels: ["phpcr"]
Query Debugging:
Use getQuery()->getSQL() to inspect generated queries:
$query = $dm->createQueryBuilder(Article::class)->getQuery();
dump($query->getSQL());
Repository Debugging:
Override find() in custom repositories to log queries:
public function find($id)
{
dump("Finding document with ID: $id");
return parent::find($id);
}
Custom Backend:
Implement Doctrine\PHPCR\Backend\BackendInterface for non-Jackalope backends (e.g., custom Solr or CouchDB).
Event Subscribers:
Extend functionality with custom events (e.g., onFlush, postLoad):
$dm->getEventManager()->addEventSubscriber(new CustomEventSubscriber());
Custom Mappings:
Use PHPCR\Mapping\Driver\AnnotationDriver or PHPCR\Mapping\Driver\XmlDriver for non-standard mappings.
PHPCR Utilities:
Use Doctrine\PHPCR\Util\NodeHelper for node manipulation:
use Doctrine\PHPCR\Util\NodeHelper;
$node = NodeHelper::createNode($parent, 'new-node', 'nt:unstructured');
Symfony Console Commands: Create custom commands for PHPCR-specific tasks:
class ImportCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output)
{
$dm = $this->getContainer()->get('doctrine_phpcr.odm.default_document_manager');
// Import logic...
}
}
How can I help you explore Laravel packages today?