Latest posts
This post is the second in a serie about PHPCR with Jackalope. If you want to know more about PHPCR, I recommend watching
the slides about PHPCR.
In the previous post, I talked about setting up your repository. In this post we will start playing with some actual
content.
Insert data
To show the basic usage of PHPCR, we are going to load some data into our repository. For this I used Faker.
$ composer require fzaninotto/faker ~1.2
Because we are going to add more CLI scripts later on, I created a simple Symfony Console Application for this. I called
it console
at placed it in the root. You can find the console and the Command on Github.
With PHPCR, it is pretty easy to create new nodes. You simply call addNode
on it's parent node. So before we can
create new nodes, we need to rootNode. You can request the rootNode from the PHPCR session.
$session = $this->getHelper('phpcr')->getSession();
Here we use the helper that we defined in cli-config.php in my previous post to get the Session. Now we have the
session, we can simply retrieve the rootNode with $session->getRootNode();
With the rootNode, we can start creating
fake nodes.
$faker = \Faker\Factory::create();
while (count($rootNode->getNodes()) < 10) {
try {
$node = $rootNode->addNode($faker->word);
$node->setProperty('title', $faker->name);
$node->setProperty('body', $faker->text(1000));
} catch (\PHPCR\ItemExistsException $e) {
// Ignore, and try again
}
}
$session->save();
On line 5, you see the addNode
, the first argument you give it, is the nodeName. You can give a second argument, to
define the nodeType but in this case we will use the default value (which will be nt:unstructured).
On line 7 and 8, we set 2 properties with random values. The first argument is the name of the property, the second is
the value. You can also give a third parameter, to indicate the type, but this is not required as PHPCR will guess the
type based on given value.
Because nodeNames need to be unique, Jackalope will throw a ItemExists Exception when we try to create 2 nodes with the
same name. As that might happen with random date, we just catch the exception and try again.
Reading the data
Now we have some fake data, we can try to query it again. We start with simply iterating over all the childNodes.
Because every Node implements Traversable
, we can simply loop over a node to get its children.
foreach ($rootNode as $childNode) {
$name = $childNode->getName();
$title = $body = null;
if ($childNode->hasProperty('title')) {
$title = $childNode->getProperty('title')->getValue();
}
if ($childNode->hasProperty('body')) {
$body = $childNode->getProperty('body')->getValue();
}
// .. do something
}
All pretty straight forward. To retrieve propertyValues, there are a few alternatives. You can, for example, use some of
other property methods, to convert the property value to another type.
$dummyNode->setProperty('floatProperty', 3.1415);
$property = $dummyNode->getProperty('floatProperty');
var_dump($property->getString()); // string(6) "3.1415"
var_dump($property->getLong()); // int(3)
var_dump($property->getBoolean()); // bool(true)
var_dump($property->getBinary()); // resource(#) of type (stream)
Besides that, you can use $node->getPropertyValue('propertyName')
. By passing the second argument, you can convert the
value to the desired type.
$dummyNode->getPropertyValue('floatProperty', \PHPCR\PropertyType::STRING); // string(6) "3.1415"
Finding nodes
You can also retrieve a node by it's path or multiple nodes by an array with paths
$dummyNode = $session->getNode('/dummy');
$nodes = $session->getNodes(array('/', '/dummy'));
Or retrieve a childNode by it's name:
$dummyNode = $rootNode->getNode('dummy');
// Note that we call this on $rootNode, not on $session
You can even find nodes by searching with wildcards:
$rootNode->getNodes('c*');
There is a lot more to learn about nodes and properties. In the following chapters some of those features will be
explained. If you want to learn more about the possibilities, you might want to look at the PHPCR api tests.
In the next post, we will play with Queries and the QueryObjectModel.
This post is the first in a serie about PHPCR with Jackalope. If you want to know more about PHPCR, I recommend watching
the slides about PHPCR.
In this post I will walk through the first steps, setting up your Content Repository using Jackalope. In this case we
will use MySQL as storage, but in a future post, we will migrate our data to a Jackrabbit server.
Install Jackalope
First we will install Jackalope DoctrineDBAL. In this serie I will use composer, and in the examples I assume it's
installed system wide, so the composer
command is available.
$ mkdir phpcr-tutorial && cd phpcr-tutorial
$ composer require jackalope/jackalope-doctrine-dbal ~1.0
This will also install the base Jackalope package, the PHPCR and PHPCR-Utils packages and Doctrine DBAL (and their
dependencies).
To already be a bit prepared for switch storage backend later on, I setup a basic configuration that allows us to switch
easily later on. For this I installed symfony/yaml to parse the Yaml file.
$ composer require symfony/yaml ~2.3
The next step is to create your database and configure Jackalope with the correct transport layer.
Jackalope provides a file that gives us some powerfull CLI commands. For this to work, we need to tell Jackalope what
repository connection we are using. Jackalope requires a cli-config.php file for that in the root of your project.
In that file, we need to define the connection, but we can also add a HelperSet to the CLI. The HelperSet will contain
the PHPCR session, so Jacklope CLI has something to talk to.
Luckily Jackalope provides an example file, so we see how the connection is created. (For Jackrabbit, there is a
similar example file)
For this example, I created a bit of a revised version, to be prepared for the switch later. But you can choose to just
use the example provided by Jackalope Doctrine DBAL if you like. The full file I created can be found at github.
Below you will find the instantiation of the repository. Based on the config file, it's
decided which Jackalope Repository is created. In this part we only implemented the Doctrine DBAL version.
use \Doctrine\DBAL\DriverManager;
switch ($config['jackalope']['transport']) {
case 'jackalope-doctrine-dbal':
$dbConnection = DriverManager::getConnection(array(
/* database connection variables $config['jackalope']['dbal'] */
));
$factory = new \Jackalope\RepositoryFactoryDoctrineDBAL();
$repository = $factory->getRepository(array(
'jackalope.doctrine_dbal_connection' => $dbConnection
));
break;
case 'jackalope-jackrabbit':
throw new \Exception('Jackrabbit bootstrap has not yet been defined');
break;
default:
throw new \RuntimeException(sprintf('Invalid transport "%s" given', $config['jackalope']['transport']));
}
After that we need to actual login to the repository to be able to read and write data.
$credentials = new \PHPCR\SimpleCredentials($config['phpcr']['username'], $config['phpcr']['password']);
$session = $repository->login($credentials, $config['phpcr']['workspace']);
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'session' => new \PHPCR\Util\Console\Helper\PhpcrHelper($session)
));
Now we have the config file we can init the database.
$ ./vendor/bin/jackalope jackalope:init:dbal
Jackalope will tell you it has initialized the database tables.
Testing your repository
You should now have a working repository. We can test this by executing our first query, which should return the rootNode
$ ./vendor/bin/jackalope phpcr:workspace:query "SELECT * FROM [nt:unstructured]"
With this query, you request all notes with type nt:unstructed
, which in this case, is only the rootNode.
Now we're done with the first part. In the next part, we will start with reading and writing to the repository via
Jackalope.
Today I got into a discussion about Symfony CMF. The discussion started with the question, if Symfony CMF isn't trying
to create a solution for a problem that doesn't exists. That person thought CMF only solved a developers problem, but
couldn't solve the problems for front-end developers or editors (or even added more problems). It came down to it, that
he had seen the admin interfaces that come with Symfony CMF and saw that it was... less from perfect (to put it nice).
Goals of Symfony CMF
Symfony CMF aims to provide a toolbox to create your own custom CMS, not by reinventing the wheel, but by reusing
existing code and sharing the produced code (parts of it will be used in Drupal 8). It aims to make it easier for
developers to add CMS functionality to applications built on Symfony 2.
PHPCR
To do that, they looked at other solutions, solutions not limited to PHP, to structure content. This because a CMS has
mostly unstructured data, and forcing this into a RDBMS isn't always a good fit. Graph database are a better
fit. They found JCR, a Java specification that deals with content. They ported that to PHP and called it PHPCR;
PHP Content Repository. With PHPCR, there is an API to interface with your content repository.
What is doesn't solve
Althought Symfony CMF does provide some basic admin interfaces (with Sonata Admin), it isn't meant as an end product.
For most customers and editors, those screens just won't be enough. But there is nothing to stop you from developing
your own admin screens that does fit you customers needs.
Symfony CMF isn't a solution aimed at the end-user, that is still you job as a developer, to implement a solution that
helps your end-user and the editors. Symfony CMF just helps you to not get a panic attack when somebody tells you to add
content management to your Symfony2 application.
For more information about Symfony CMF, check the bigger picture slides
As I mentioned 2 months ago, I believe it's important to keep developing yourself. You can't just sit there and
repeat the same trick every time. I also told that I wanted to do that by trying to find a new job, and by attending
more conferences and user groups.
But that all isn't that easy, I couldn't attend my user group because of a agenda conflict (and missing the next one as well)
and conferences are just to expensive to pay for by myself.
The work part isn't much better as well. I shouldn't complain, I still have a job! But at that job it is hard to find a
challenge, and there is a lot of repetitive work involved. And because there is a lot of Drupal involved, OOP isn't part
of my daily routine anymore, and that sucks.
While searching for a new job, I have found a company were I really want to work. The first job interview went well and
they are open to further conversation, but the sad part is, that they don't have a spot at this moment and they don't
know on what term they will.
And that's a pretty difficult time for me, I really want that job, but I'm not sure if I should wait for it. At this point
I'm not even sure if I actually would get hired, so is it worth the risk?
And with all this, it's hard to keep myself busy on the level were I want to be (and grow from there), At work there isn't
much room for it, and at home I can't spend the time at it that I would like (my wife would like some attention now and then :-) ).
So I try to put some time in a JAVA project I started 6 months ago, at least I'm working on a OO project.
Any tips how to deal with such a time would be greatly appreciated! Sometimes I have the feeling I'm just not patient enough,
that I should just wait, and be grateful I have a job.
About 2 months ago, when 010php started, I started playing with the idea of giving talks myself. At first
I was pretty anxious to actually execute that plan. I talked with some people that I had the idea and they all encouraged
me to do it. So I started writing down some ideas I already had. But until last week, I didn't actually had a talk planned
anywhere.
But last Tuesday, Lineke asked me if I would give a uncon talk at PFcongres 2013,
I don't think she new I was already playing with the idea, but she was very convincing and I said I would do my best to prepare
something.
Because of limited time, I started to prepare the evening before the conference, at that point I hadn't even decided what
subject I would choose for the talk... I decided on PHPCR and luckily the preperation went fairly well and I even rehearsed
it once!
So yesterday the conference was there, and on my way in the train I even managed to talk myself into a calmer state. Don't
get me wrong, I was still nervous as hell... just a bit less then before. I spoke to Linke en we planned my talk at 12.10.
After that I just went to the talks, trying not to think about it to much anymore. At the talk just before my uncon slot,
I got a bit nervous when Joshua already mentioned he didn't now if he would be done in time. Should I leave the talk early
to be on time? Should I just leave with everybody else? Eventually I decided that I would leave 5 minutes before my talk
started. That gave me some time to visit a restroom (to much coffee...), and still be on time for my talk. This meant I
walked out while Joshua was still answering some questions (sorry for that), but it made me feel a bit better.
Giving the talk went pretty well, of course I forgot to bring a VGA connector, but I could use Lineke's (thanks!). I pretty
much gave my talk as intended (only forgot to tell 1 thing), and got some good questions, so apparently people weren't bored
to death :)
Just before the end of my talk I noticed myself saying Uhh between pretty much every sentence, so I learned something
while giving the talk!
After the talk Lineke already gave me some feedback (she noticed the Uhh's as well) and I spoke some people that had
seen it.
Another important thing I learned, is that a topic like PHPCR is very hard to explain, and nearly impossible to explain in
10-15 minutes, so next time I would probably want a 50min slot and I need to rethink how I can explain the subject to people
that have no clue about it. On a sidenote: you can still comment on my talk, so please leave feedback!
All with all was it a great experience and I'm certain I will do it again. Maybe I'll even submit a actual talk to a conference
next year.