To integrate phpcr/phpcr-migrations into a Laravel project (assuming PHPCR integration via Symfony’s PHPCR Bundle or custom PHPCR setup):
Install the package:
composer require phpcr/phpcr-migrations
Set up PHPCR Session:
Ensure you have a PHPCR session available (e.g., via jackalope-doctrine-dbal or another PHPCR adapter). Example:
use PHPCR\SessionInterface;
use PHPCR\RepositoryInterface;
$repository = new YourPHPCRAdapter(); // Configure your PHPCR repository
$session = $repository->login();
Create a Migration Factory:
Define a factory class to encapsulate initialization logic (e.g., in app/Services/MigrationFactory.php):
namespace App\Services;
use PHPCR\Migrations\Migrator;
use PHPCR\Migrations\VersionStorage;
use PHPCR\Migrations\VersionFinder;
use PHPCR\SessionInterface;
class MigrationFactory
{
public function __construct(
private SessionInterface $session,
private string $migrationsPath = database_path('migrations/phpcr')
) {}
public function createMigrator(): Migrator
{
$storage = new VersionStorage($this->session);
$finder = new VersionFinder([$this->migrationsPath]);
$versions = $finder->getVersionCollection();
return new Migrator($this->session, $versions, $storage);
}
}
Register the Factory:
Bind the factory in AppServiceProvider:
public function register()
{
$this->app->singleton(MigrationFactory::class, function ($app) {
$session = $app['phpcr.session']; // Bind your PHPCR session
return new MigrationFactory($session);
});
}
Initialize Migrations:
Run initialization during deployment or a post-install script:
php artisan phpcr:migrate --initialize
Create an Artisan command for this (e.g., app/Console/Commands/PhpcrMigrate.php):
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\MigrationFactory;
class PhpcrMigrate extends Command
{
protected $signature = 'phpcr:migrate
{--initialize : Initialize migrations}
{--to= : Target version (e.g., 202401010000, up, down, top, bottom)}
{--output= : Output format (null, console)}';
public function handle(MigrationFactory $factory)
{
$migrator = $factory->createMigrator();
$output = $this->option('output') === 'console'
? $this->output
: new \Symfony\Component\Console\Output\NullOutput();
if ($this->option('initialize')) {
$migrator->initialize();
$this->info('Migrations initialized.');
return;
}
$target = $this->option('to') ?? 'top';
$migrator->migrate($target, $output);
$this->info("Migrated to version {$target}.");
}
}
Create Your First Migration:
Create a migration file at database/migrations/phpcr/Version202401010000.php:
namespace Database\Migrations\Phpcr;
use PHPCR\Migrations\VersionInterface;
use PHPCR\SessionInterface;
class Version202401010000 implements VersionInterface
{
public function up(SessionInterface $session)
{
// Create a node in PHPCR
$root = $session->getRootNode();
$node = $root->addNode('example_node');
$node->setProperty('description', 'Initial migration');
$session->save();
}
public function down(SessionInterface $session)
{
// Revert the changes
$root = $session->getRootNode();
if ($root->hasNode('example_node')) {
$root->removeItem('example_node');
$session->save();
}
}
}
Run Migrations:
php artisan phpcr:migrate --to=202401010000
Use migrations to initialize your PHPCR repository schema during deployment. For example:
php artisan phpcr:migrate --initialize --to=top
This ensures all migrations are registered in the repository’s version storage.
Create Migration Files:
database/migrations/phpcr/).VersionYYYYMMDDHHMM.php.database/
├── migrations/
│ ├── phpcr/
│ │ ├── Version202401010000.php
│ │ ├── Version202401020000.php
│ │ └── ...
Implement up and down Methods:
SessionInterface to interact with the repository.public function up(SessionInterface $session)
{
$root = $session->getRootNode();
$config = $root->addNode('config');
$users = $config->addNode('users');
$users->setProperty('max_depth', 5);
$session->save();
}
public function down(SessionInterface $session)
{
$root = $session->getRootNode();
if ($root->hasNode('config')) {
$root->removeItem('config');
$session->save();
}
}
Order Migrations Chronologically:
Version202401010000 before Version202401020000).Leverage Transactions:
public function up(SessionInterface $session)
{
$session->begin();
try {
// PHPCR operations
$session->save();
$session->commit();
} catch (\Exception $e) {
$session->rollback();
throw $e;
}
}
Custom Artisan Commands: Extend the basic command to support rollbacks, status checks, and dry runs:
protected $signature = 'phpcr:migrate
{action? : [top|bottom|up|down|to] Target action}
{version? : Target version (e.g., 202401010000)}
{--dry-run : Simulate migration without executing}
{--force : Force migration even if not needed}
{--step= : Number of steps to migrate (default: 1)}';
Status Command: Add a command to check migration status:
protected $signature = 'phpcr:migrate:status';
public function handle(MigrationFactory $factory)
{
$migrator = $factory->createMigrator();
$storage = $migrator->getVersionStorage();
$current = $storage->getCurrentVersion();
$versions = $migrator->getVersionCollection()->getAllVersions();
$this->table(['Version', 'Applied'], $versions->mapWithKeys(function ($applied, $version) {
return [$version => $applied ? '✓' : '✗'];
}));
$this->info("Current version: {$current}");
}
Event Listeners:
Trigger migrations during deployment or after specific events (e.g., deployed):
namespace App\Listeners;
use App\Services\MigrationFactory;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class RunPhpcrMigrations
{
public function __construct(private MigrationFactory $factory) {}
public function handle()
{
$migrator = $this->factory->createMigrator();
$migrator->migrate('top', new \Symfony\Component\Console\Output\ConsoleOutput());
}
}
SessionInterface to test migration logic:
use PHPCR\SessionInterface;
use PHPUnit\Framework\TestCase;
class MigrationTest extends TestCase
{
public function testUpMethod()
{
$session = $this->createMock(SessionInterface::class);
$rootNode = $this->createMock(\PHPCR\NodeInterface::class);
$session->expects($this->once())
How can I help you explore Laravel packages today?