Install Dependencies
composer require dantleech/glob-finder doctrine/phpcr doctrine/phpcr-odm
Ensure your Laravel app uses doctrine/phpcr-bundle (Symfony) or manually configures PHPCR-ODM.
First Use Case: Fetching Nodes
use Dantleech\GlobFinder\PhpcrOdmTraversalFinder;
// In a Laravel service or controller
$dm = app('doctrine_phpcr.odm.document_manager');
$finder = new PhpcrOdmTraversalFinder($dm);
// Find all articles under /cmf/articles/
$articles = $finder->find('/cmf/articles/*');
Where to Look First
PhpcrOdmTraversalFinder (handles glob-to-PHPCR conversion).tests/ directory for edge cases (e.g., recursive globs, special chars).Glob Syntax
/path/to/* (matches direct children)./path/**/* (matches all descendants; not natively supported—see Gotchas).*article* (matches "article", "blog_article", etc.).Laravel Integration
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->singleton(PhpcrOdmTraversalFinder::class, function ($app) {
return new PhpcrOdmTraversalFinder($app['doctrine_phpcr.odm.document_manager']);
});
}
// app/Facades/GlobFinder.php
public static function find($pattern) {
return app(PhpcrOdmTraversalFinder::class)->find($pattern);
}
Workflow: Content Migration
// Extract all legacy nodes for migration
$legacyNodes = app(PhpcrOdmTraversalFinder::class)->find('/legacy/*');
foreach ($legacyNodes as $node) {
$migrated = $this->migrateNode($node);
$this->archiveOriginal($node);
}
Combining with PHPCR-ODM Queries Use globs to narrow results before applying filters:
$candidateNodes = $finder->find('/products/*');
$publishedProducts = $dm->createQueryBuilder('Product')
->where('node IN (:nodes)')
->setParameter('nodes', $candidateNodes)
->getQuery()
->getResult();
/Content/ vs. /content/).$results = Cache::remember("glob_{$pattern}", now()->addHours(1), function () use ($finder, $pattern) {
return $finder->find($pattern);
});
try {
$nodes = $finder->find('/invalid/path/*');
} catch (\InvalidArgumentException $e) {
Log::error("Glob pattern failed: {$e->getMessage()}");
}
No Recursive Glob Support
** for recursive matching (e.g., /path/**/*).$session = $dm->getSession();
$root = $session->getNode('/path');
$nodes = $root->getNodes(); // Manual recursion needed
PHPCR-Specific Path Handling
/ vs. //).$session->getRootNode()->getPath() to verify paths.Performance on Large Hierarchies
/content/**/*).getNodes() with constraints.Case Sensitivity
strtolower($pattern)) if needed.Dependency Conflicts
/path/ vs. /path).?, [).$dm->getConfiguration()->setPHPCRConfiguration([
'logging' => true,
]);
$session = $dm->getSession();
if (!$session->nodeExists('/path')) {
throw new \RuntimeException("Path does not exist");
}
Custom Finder for Non-PHPCR
Extend PhpcrOdmTraversalFinder to support other backends (e.g., filesystem):
class FilesystemGlobFinder implements GlobFinderInterface {
public function find($pattern) {
return glob($pattern, GLOB_ONLYDIR | GLOB_BRACE);
}
}
Add Recursive Support
Override the finder to handle **:
public function find($pattern) {
if (strpos($pattern, '**') !== false) {
return $this->recursiveFind($pattern);
}
return parent::find($pattern);
}
Laravel Events Dispatch events for glob results:
event(new GlobFound($pattern, $results));
# config/packages/doctrine_phpcr.yaml
doctrine_phpcr:
session:
backend: doctrine_phpcr.session.pdo
connection: default
$articles = array_filter($nodes, fn($node) => $node->getType() === 'Article');
$candidateNodes = $finder->find('/articles/*');
$criteria = Criteria::create()
->where(Criteria::expr()->eq('published', true));
$published = $dm->findBy($candidateNodes, $criteria);
// app/Console/Commands/GlobSearch.php
public function handle() {
$pattern = $this->argument('pattern');
$results = app(PhpcrOdmTraversalFinder::class)->find($pattern);
$this->line("Found " . count($results) . " nodes:");
foreach ($results as $node) {
$this->line($node->getPath());
}
}
Usage:
php artisan glob:search "/articles/*"
How can I help you explore Laravel packages today?