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

Phpcr Odm Laravel Package

doctrine/phpcr-odm

Doctrine PHPCR-ODM brings Doctrine-style object document mapping to PHP Content Repository (PHPCR) implementations. Map PHP objects to nodes and query content repositories via familiar Doctrine APIs. Supports Jackrabbit and Doctrine DBAL setups, with tests and docs available.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup for Laravel Integration

  1. Install Dependencies

    composer require doctrine/phpcr-odm jackalope/jackalope-doctrine-dbal doctrine/dbal
    

    (Use jackalope/jackalope-jackrabbit for Java-based PHPCR if needed.)

  2. Configure PHPCR in Laravel Create a service provider (e.g., PHPCRServiceProvider) to bootstrap the ODM:

    // app/Providers/PHPCRServiceProvider.php
    namespace App\Providers;
    
    use Doctrine\ODM\PHPCR\Configuration;
    use Doctrine\ODM\PHPCR\DocumentManager;
    use Doctrine\ODM\PHPCR\DocumentManagerFactory;
    use Doctrine\ODM\PHPCR\Mapping\Driver\AttributeDriver;
    use Illuminate\Support\ServiceProvider;
    
    class PHPCRServiceProvider extends ServiceProvider
    {
        public function register()
        {
            $config = new Configuration();
            $config->setMetadataDriverImpl(new AttributeDriver());
            $config->setProxyDir(__DIR__.'/../../storage/framework/proxies');
            $config->setProxyNamespace('App\\Proxies');
    
            // Configure PHPCR backend (Doctrine DBAL example)
            $config->setRepositoryImplementation('jackalope/jackalope-doctrine-dbal');
            $config->setConnectionParams([
                'driver' => 'pdo_mysql',
                'host' => env('DB_HOST'),
                'dbname' => env('DB_DATABASE'),
                'user' => env('DB_USERNAME'),
                'password' => env('DB_PASSWORD'),
            ]);
    
            $dm = DocumentManagerFactory::create($config);
            $this->app->singleton('phpcr.odm', fn() => $dm);
        }
    }
    

    Register the provider in config/app.php.

  3. Define a PHPCR Document

    // app/Models/Page.php
    use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCR;
    
    #[PHPCR\Document]
    class Page
    {
        #[PHPCR\Id(strategy: 'uuid')]
        private string $id;
    
        #[PHPCR\Field(type: 'string')]
        private string $title;
    
        #[PHPCR\Field(type: 'string')]
        private string $content;
    
        #[PHPCR\ParentDocument]
        private ?Page $parent;
    
        #[PHPCR\Children]
        private array $children = [];
    
        // Getters/setters...
    }
    
  4. First Usage in a Controller

    use Doctrine\ODM\PHPCR\DocumentManager;
    
    class PageController extends Controller
    {
        public function __construct(private DocumentManager $dm) {}
    
        public function create()
        {
            $homePage = new Page();
            $homePage->setTitle('Home');
            $homePage->setContent('Welcome!');
    
            $this->dm->persist($homePage);
            $this->dm->flush();
    
            return redirect()->route('home');
        }
    }
    

Key First Use Cases

  • Hierarchical Content: Create nested pages (e.g., /home/about/team).
  • Versioning: Leverage PHPCR’s native versioning for content drafts.
  • Querying: Fetch all children of a node:
    $children = $this->dm->createQueryBuilder(Page::class)
        ->where('parent = :parent')
        ->setParameter('parent', $parentPage)
        ->getQuery()
        ->getResult();
    

Implementation Patterns

1. Document Lifecycle Management

Persistence Workflow

// Create and persist a document
$document = new Page();
$this->dm->persist($document);
$this->dm->flush(); // Saves to PHPCR

// Update and flush
$document->setTitle('Updated Title');
$this->dm->flush();

// Detach (remove from DocumentManager)
$this->dm->detach($document);

Bulk Operations

// Bulk insert (avoid flush per document)
$documents = [];
for ($i = 0; $i < 100; $i++) {
    $doc = new Page();
    $doc->setTitle("Page $i");
    $documents[] = $doc;
}
$this->dm->persist($documents);
$this->dm->flush(); // Single flush for all

2. Hierarchical Relationships

Parent-Child Binding

// Set parent-child relationship
$parent = $this->dm->find(Page::class, '/home');
$child = new Page();
$child->setParent($parent);
$this->dm->persist($child);
$this->dm->flush();

// Query children
$children = $parent->getChildren();

Path-Based Queries

// Find nodes under a specific path
$query = $this->dm->createQueryBuilder(Page::class)
    ->where('path LIKE :path')
    ->setParameter('path', '/home/%')
    ->getQuery();
$results = $query->getResult();

3. Querying with Doctrine QueryBuilder

Basic Queries

// Find by field
$pages = $this->dm->createQueryBuilder(Page::class)
    ->where('title = :title')
    ->setParameter('title', 'About Us')
    ->getQuery()
    ->getSingleResult();

Joins and Aggregations

// Count children recursively
$query = $this->dm->createQueryBuilder(Page::class)
    ->select('COUNT(c)')
    ->from(Page::class, 'p')
    ->leftJoin('p.children', 'c')
    ->getQuery();
$count = $query->getSingleScalarResult();

4. Event Listeners (Lifecycle Hooks)

// Register a pre-persist listener
$this->dm->getEventManager()->addEventListener(
    'prePersist',
    fn($event) => {
        $document = $event->getDocument();
        if ($document instanceof Page) {
            $document->setCreatedAt(now());
        }
    }
);

5. Integration with Laravel Services

Caching Strategies

// Cache query results (PSR-6 compatible)
use Doctrine\Common\Cache\Psr6\Psr6Cache;

$cache = new Psr6Cache($this->app->make(\Psr\Cache\CacheItemPoolInterface::class));
$this->dm->getConfiguration()->setQueryCacheImpl($cache);

Eloquent Coexistence

// Hybrid repository example
class HybridRepository
{
    public function __construct(
        private DocumentManager $phpcrDm,
        private \Illuminate\Database\Eloquent\Model $eloquentModel
    ) {}

    public function syncData()
    {
        $phpcrData = $this->phpcrDm->find(Page::class, '/home');
        $this->eloquentModel->updateFromPhpcr($phpcrData);
    }
}

6. Custom ID Generators

// Use UUID for IDs
#[PHPCR\Id(strategy: 'uuid')]
private string $id;

// Or custom generator
#[PHPCR\Id(strategy: 'custom')]
#[PHPCR\CustomIdGenerator(class: 'App\Services\CustomIdGenerator')]
private string $id;

Gotchas and Tips

Pitfalls

  1. Path vs. ID Confusion

    • PHPCR uses paths (e.g., /site/pages/home) for hierarchical queries, not just IDs.
    • Fix: Always use path in queries for hierarchical data:
      // Wrong: Assumes ID is path
      $this->dm->find(Page::class, '/home'); // Fails if ID is UUID
      
      // Correct: Use path explicitly
      $this->dm->createQueryBuilder(Page::class)
          ->where('path = :path')
          ->setParameter('path', '/home')
          ->getQuery();
      
  2. Flush Behavior Changes in 2.0+

    • DocumentManager::flush() no longer saves the PHPCR session if there are no ODM changes.
    • Fix: Explicitly call $session->save() if you need to force a session save:
      $this->dm->flush(); // ODM changes only
      $this->dm->getSession()->save(); // Force session save
      
  3. ChildrenCollection Quirks

    • ChildrenCollection::slice() no longer accepts node names as offsets (use sliceByChildName).
    • Fix: Update old code:
      // Old (deprecated)
      $slice = $children->slice('about');
      
      // New
      $slice = $children->sliceByChildName('about');
      
  4. PSR-6 Cache Requirement

    • Configuration now requires PSR-6 Cache (not Doctrine Cache).
    • Fix: Bind a PSR-6 cache to Laravel’s container:
      $this->app->bind(\Psr\Cache\CacheItemPoolInterface::class, fn() => new \Symfony\Cache\Adapter\FilesystemAdapter());
      
  5. Attribute Mapping Only

    • Annotations are deprecated; use
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle