jackalope/jackalope-jackrabbit
PHPCR storage backend for Jackalope using Apache Jackrabbit. Provides a Jackrabbit-specific transport/implementation so PHPCR applications can connect to and work with Jackrabbit repositories via Jackalope.
Install the Package
composer require jackalope/jackalope-jackrabbit
Ensure jackalope/jackalope is also installed (core PHPCR implementation).
Configure Jackrabbit Connection
Add a config entry in config/filesystems.php (or a custom config file):
'jackrabbit' => [
'driver' => 'jackalope',
'client' => [
'class' => \Jackalope\Transport\WebDAV\Client::class,
'uri' => env('JACKRABBIT_WEBDAV_URI', 'http://localhost:8080/server'),
'username' => env('JACKRABBIT_USERNAME', 'admin'),
'password' => env('JACKRABBIT_PASSWORD', 'admin'),
],
'repository' => [
'class' => \Jackalope\Repository\RepositoryFactory::class,
'transport' => 'client',
],
],
First Use Case: Fetching a Node
use Jackalope\Repository\RepositoryFactory;
use Jackalope\Transport\WebDAV\Client;
$client = new Client(env('JACKRABBIT_WEBDAV_URI'), env('JACKRABBIT_USERNAME'), env('JACKRABBIT_PASSWORD'));
$repository = RepositoryFactory::createRepository($client);
$session = $repository->login();
$node = $session->getNode('/'); // Root node
$children = $node->getNodes(); // List children
Create a Node
$parent = $session->getNode('/content');
$newNode = $parent->addNode('article', 'nt:unstructured');
$newNode->setProperty('title', 'My Article');
$session->save();
Querying with JCR-SQL2
$query = $session->createQuery(
"SELECT * FROM [nt:base] AS node WHERE ISDESCENDANTNODE(node, '/content')",
\PHPCR\Query\Query::JCR_SQL2
);
$result = $query->execute();
foreach ($result as $hit) {
echo $hit->getPath() . "\n";
}
Bulk Operations with Transactions
$session->beginTransaction();
try {
$node = $session->getNode('/content');
$node->setProperty('last_updated', new \DateTime());
$session->save();
$session->commit();
} catch (\Exception $e) {
$session->rollback();
throw $e;
}
Service Provider Binding
Bind the repository in AppServiceProvider:
public function register()
{
$this->app->singleton(\Jackalope\Repository\RepositoryInterface::class, function ($app) {
$client = new Client(config('jackrabbit.client.uri'), config('jackrabbit.client.username'), config('jackrabbit.client.password'));
return RepositoryFactory::createRepository($client);
});
}
Eloquent-like Repository Facade Create a facade for cleaner usage:
// app/Facades/Jackrabbit.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Jackrabbit extends Facade
{
protected static function getFacadeAccessor() { return \Jackalope\Repository\RepositoryInterface::class; }
}
Usage:
$node = Jackrabbit::getSession()->getNode('/content');
Leverage PHPCR events to trigger Laravel events:
$session->addListener(new class implements \PHPCR\Event\EventListener {
public function handleEvent(\PHPCR\Event\Event $event) {
if ($event->getType() === \PHPCR\Event\Event::NODE_ADDED) {
event(new \App\Events\NodeCreated($event->getNode()));
}
}
});
WebDAV vs. DAVex
WebDAV\Client) matches the server’s endpoint.curl -v http://your-jackrabbit/dav/ to verify the protocol.Session Management
$session->logout();
Property Types
Date, Binary). Cast values explicitly:
$node->setProperty('date', new \DateTime('now'), \PHPCR\PropertyType::DATE);
Performance with Large Repositories
$nodeIterator = $session->getNode('/')->getNodes();
foreach ($nodeIterator as $node) { /* ... */ }
Enable Jackalope Logging
Add to config/logging.php:
'channels' => [
'jackalope' => [
'driver' => 'single',
'path' => storage_path('logs/jackalope.log'),
'level' => 'debug',
],
],
Then configure the client:
$client = new Client(..., null, null, [
'logger' => \Monolog\Logger::getInstance('jackalope'),
]);
Common Errors
No such node: Verify paths are case-sensitive and absolute (e.g., /content, not content).Unauthorized: Check credentials and Jackrabbit’s repository.xml for access rules.Transport error: Ensure Jackrabbit’s WebDAV/DAVex port (default: 8080) is open and CORS is configured if accessing remotely.Custom Node Types
Extend Jackrabbit’s node types via nodeType.xml (server-side) or use PHPCR’s dynamic types:
$node->setPrimaryType('my:customType');
Caching Cache sessions or node queries (e.g., with Laravel’s cache):
$cacheKey = 'jackrabbit:node:/content';
$node = cache($cacheKey, function () use ($session) {
return $session->getNode('/content');
}, now()->addMinutes(5));
Async Operations Use Laravel Queues for long-running operations (e.g., bulk imports):
dispatch(new ImportNodesJob($session, $nodes));
Testing Spin up a local Jackrabbit instance for tests:
docker run -p 8080:8080 -e REPO_HOME=/tmp/repository jackrabbit
Use Laravel’s DatabaseMigrations pattern to reset the repo between tests.
How can I help you explore Laravel packages today?