Our last post, Symfony CMF vs WordPress, described several situations when the PHPCR-compliant content management framework was better at managing custom documents than the blogging platform.
In this post, we’ll dig a little deeper in how the PHP Content Repository works with its sidekick Jackalope. You’ll see that its applications extend beyond content management and that a design based on the PHPCR offers tremendous flexibility.
A Greater Need for Hierarchical and Flexible Data Structures
Apps running in the cloud or social media require an enormous amount of data, much of which is hierarchical and metadata-rich in nature, and can’t easily be broken down into a set of relational tables.
From a methodology perspective, lean software development compels developers to iterate quickly. As a result, they can’t afford to be bogged down by data structures that need constant change.
It is no surprise then that startups and big corporations alike are deploying applications using document-oriented databases, such as MongoDB.
However, an object-oriented database, no matter how great it is, remains a storage facility. It may offer only a subset of the features you need to easily manage, retrieve and version documents.
So, what if you could focus on what your application does with semi-structured or unstructured data instead of worrying about how to handle them?
This is where PHPCR and Jackalope come in.
Easily Manage Hierarchical and Unstructured Content with PHPCR
PHPCR is a port of the Java Content Repository (JCR) to PHP. Like its Java counterpart, PHPCR’s “raison d’etre” is to eliminate the complexity of dealing with hierarchical and unstructured content.
Think of a hierarchical data structure as something similar to the file system on your Mac or PC. It is made of trees (folders) containing nodes (files) instead of tables.
Very much like a file, a node is what holds your content using properties and values (text, number, binary data), as well as child nodes. Once you retrieve a node, you can navigate up or down the tree, as well as laterally using relationships.
The diagram below illustrates a tree structure containing blog posts as well as invoices.
The simplicity of the file system metaphor hides the complexity of what is required behind the scenes when it comes to navigating through unstructured data and performing full text searches and other queries.
To alleviate the pain that comes from handling hierarchical data, the PHPCR provides the following features:
- tree navigation using paths (e.g. /cms/blog/post-1 and relationships)
- full-text search and querying capabilities
- versioning and locking
- import and export of data using XML
- session handling and access management
- observation and handling of events happening to nodes
Use PHPCR to Stay Free From Inflexible Data Structures
The second objective of the repository is to make you focus on what you do with your content, and not how it is stored. As a result, the PHPCR is storage agnostic and enables very loose coupling between your application logic and its data structures.
The diagrams below highlight the differences between a typical MVC application using a framework, such as Symfony or Laravel, and one powered by the PHPCR.
In a MVC application using an Object-Relational Mapping library such as Doctrine, the PHP classes representing your data are more or less an abstraction of your tables. As a result, your application data layer is not necessarily tied to a DB vendor, but it is tightly coupled to your data structures.
On the other hand, an application built around PHPCR and hierarchical data is not only vendor-agnostic but it also frees you from being limited to data structures in persistence stores. The developer will spend more time on organizing documents and content in hierarchical trees and a lot less time dealing with the database.
The integration points between the PHPCR and the database are actually very loose because, first and foremost, it’s a standard API. By itself it does not retrieve or save your data.
In order to build applications around it, you must use a tool that implements the PHPCR API and interacts with persistence stores.
Call that, in technical jargon, the data transport layer. That layer is responsible to convert hierarchical structures into bits and pieces that may be stored in a variety of databases.
This is the role of Jackalope.
Jackalope, PHPCR’s Sidekick
Jackalope is the most comprehensive and versatile implementation of the PHPCR API.
Unlike a typical MVC application that interacts with a database directly through SQL or an Object-Relational Mapping tool (e.g. Doctrine), an application using the PHPCR connects to data stores using a Jackalope driver.
An application may actually use multiple drivers if you want to store your content in multiple repositories across your network as shown in the diagram below.
Since the PHPCR must work with a variety of databases, Jackalope comes in many flavors, the first two drivers being the most stable:
- Jackalope – DoctrineDBAL to save your data in a relational database supported by the Doctrine Database Abstraction Layer (used by Symfony)
- Jackalope – Jackrabbit to store your content using a proven Java-based JCR-compliant server (Jackrabbit)
- Jackalope – MongoDB to use the noSQL database
- Jackalope – Prismic to use the cloud-based storage service Prismic.io
- Jackalope – Midgard2 if you want to use a native PHP extension that can connect to multiple RDBMS.
The code samples below illustrate how you’d initialize the Jackrabbit and DoctrineBBAL drivers and create your first blog post with the PHPCR.
// Bootstrap Jackrabbit $jackrabbit_url = 'http://127.0.0.1:8080/server/'; $user = 'user'; $pass = 'pass'; $workspace = 'default'; // Create a session with the PHPCR $factory = new \Jackalope\RepositoryFactoryJackrabbit(); $repository = $factory->getRepository( array("jackalope.jackrabbit_uri" => $jackrabbit_url) ); $credentials = new \PHPCR\SimpleCredentials($user, $pass); $session = $repository->login($credentials, $workspace); // Create a first node for a blog post $rootNode = $session->getNode("/"); $post = $rootNode->addNode("my-first-post"); $post->setProperty("jcr:title", "Welcome to my Blog!"); $session->save();
// Bootstrap Doctrine DBAL $dbConn = \Doctrine\DBAL\DriverManager::getConnection(array( 'driver' => ‘pdo_mysql’, 'host' => ‘localhost’, 'user' => ‘user’, 'password' => ‘pass’, 'dbname' => ‘jackalope’, ‘workspace’ => ‘blog’ )); // Create a session with the PHPCR $factory = new \Jackalope\RepositoryFactoryDoctrineDBAL(); $repository = $factory->getRepository( array('jackalope.doctrine_dbal_connection' => $dbConn) ); $credentials = new \PHPCR\SimpleCredentials(null, null); $session = $repository->login($credentials, $workspace); // Create a first node for a blog post $rootNode = $session->getNode("/"); $post = $rootNode->addNode("my-first-post"); $post->setProperty("jcr:title", "Welcome to my Blog!"); $session->save();
As you can see, all you have to do is instantiate the proper driver and all subsequent calls remain identical. Should you decide to migrate from MySQL to MongoDB, just change the driver, sit back and relax—your application still works!
We won’t dig much further into code for now. (Sorry, geeks!) The intent was to highlight the power and flexibility of Jackalope, which deals with a lot of complexity to power the PHPCR.
All in all, the PHPCR and Jackalope are a solid starting foundation when it comes to writing solid applications that need to consume and distribute hierarchical data and complex documents.
Because they allow for interacting with complex data structures in a loosely coupled yet organized fashion, they make software development teams a lot more productive and able to respond to new business requirements more rapidly.
Hopefully by now you are as enthusiastic about those technologies and their benefits as we are.
In the case you’ve been disappointed that the PHPCR doesn’t implement ORM (Object-Relational Mapping) principles, and if your goal is to use it in the context of a MVC framework, you may like Doctrine’s PHPCR Object Document Mapper (ODM). It sits on top of Jackalope and will help you wrap your nodes and encapsulate additional business logic in custom classes. But that could be the topic of another post in and of itself!