Skip to content

Commit 1a181be

Browse files
committed
Split tutorial into book
1 parent 70f1698 commit 1a181be

22 files changed

+844
-877
lines changed

book/conclusion.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Conclusion and further reading
2+
==============================
3+
4+
We hope this tutorial helps to get you started. If you miss anything, have suggestions or questions, please contact us on [email protected] or #jackalope on irc.freenode.net
5+
6+
Further reading
7+
---------------
8+
9+
Browse through the [API documentation](http://phpcr.github.com/doc/html/index.html) to see what each of the core elements mentioned in the introduction can do.
10+
11+
To fully understand the concepts behind the content repository API, we suggest reading [the Java content repository specification](http://www.day.com/specs/jcr/2.0/index.html) and
12+
then the [simplifications we did for PHP](https://github.com/phpcr/phpcr/blob/master/doc/JCR_TO_PHPCR.txt).

book/getting_started.rst

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
Introduction and Getting Stated
2+
===============================
3+
4+
This is an introduction into the PHP content repository. You will mostly see code examples. It should work with any PHPCR implementation. We propose using [Jackalope Jackrabbit](https://github.com/jackalope/jackalope-jackrabbit) to get started as it supports all features described here.
5+
6+
Installing Jackalope
7+
--------------------
8+
9+
Just follow the README of the [jackalope-jackrabbit](https://github.com/jackalope/jackalope-jackrabbit/blob/master/README.md) repository.
10+
11+
Browser to see what is in the repository
12+
----------------------------------------
13+
14+
There are currently two options for browsing and modifying the contents of the PHPCR repository.
15+
16+
- <a href="/documentation/phpcr-shell">PHPCR Shell</a>: Aims to provide a full command line shell interface to PHPCR content repositories. A pre-compiled PHAR archive is available on the github homepage.
17+
- <a href="https://github.com/marmelab/phpcr-browser">Marmelab PHPCR Browser</a>: A web based PHPCR browser.
18+
19+
In a nutshell
20+
-------------
21+
22+
The shortest self-contained example should output a line with 'value':
23+
24+
.. code-block:: php
25+
26+
<?php
27+
require("/path/to/jackalope-jackrabbit/vendor/autoload.php");
28+
29+
$factoryclass = '\Jackalope\RepositoryFactoryJackrabbit';
30+
$parameters = array('jackalope.jackrabbit_uri' => 'http://localhost:8080/server');
31+
// end of implementation specific configuration
32+
33+
$factory = new $factoryclass();
34+
$repository = $factory->getRepository($parameters);
35+
$credentials = new \PHPCR\SimpleCredentials('admin','admin');
36+
$session = $repository->login($credentials, 'default');
37+
$root = $session->getRootNode();
38+
$node = $root->addNode('test', 'nt:unstructured');
39+
$node->setProperty('prop', 'value');
40+
$session->save();
41+
42+
// data is stored now. in a follow-up request you can do
43+
$node = $session->getNode('/test');
44+
echo $node->getPropertyValue('prop'); // outputs "value"
45+
46+
Still with us? Good, lets get in a bit deeper...
47+
48+
Get some data into the repository
49+
---------------------------------
50+
51+
We will discuss the import feature in more detail later, but to have some data, we just import something here. Create an XML file test.xml like this:
52+
53+
.. code-block:: xml
54+
55+
<data xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
56+
<node title="Test" content="This is some test content" />
57+
<sibling title="Test" content="This is another test content">
58+
<child1 title="Child1 title" />
59+
<child2 title="Child2 title" />
60+
<otherchild title="Otherchild title"/>
61+
<yetanother title="Yetanother title">
62+
<child title="Child title" />
63+
</yetanother>
64+
</sibling>
65+
</data>
66+
67+
Now import this into the repository:
68+
69+
.. code-block:: php
70+
71+
<?php
72+
$session->importXML('/', 'test.xml', \PHPCR\ImportUUIDBehaviorInterface::IMPORT_UUID_CREATE_NEW);
73+
$session->save();
74+

book/import_export.rst

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
Import and export data
2+
======================
3+
4+
As promised, here are some more details on importing and exporting data. There
5+
are two formats:
6+
7+
* The *document view* translates the data into a XML document with node names
8+
as xml elements and properties as attributes and thus very readable. Type
9+
information is lost, and illegal XML characters are encoded.
10+
* The *system view* is a more strict XML document defining the exact structure
11+
of the repository with all type information. However, it is more verbose.
12+
13+
As an analogy, think about an SQL dump file with SQL statements and the dump of
14+
an SQL table into a csv file. You can restore the data from both, but the SQL
15+
dump knows every detail about your field types and so on while the CSV just
16+
knows the data.
17+
18+
When exporting, you tell explicitly to which format you want to export:
19+
20+
.. code-block:: php
21+
22+
<?php
23+
$file = fopen('/tmp/document.xml', 'w+');
24+
25+
// dump the tree at /foo/bar into a document view file
26+
$session->exportDocumentView(
27+
'/data/sibling',
28+
$file,
29+
true, // skip binary properties to not have large files in the dump
30+
false // recursivly output the child nodes as well
31+
);
32+
33+
fclose($file);
34+
35+
$file = fopen('/tmp/system.xml', 'w+');
36+
// export the tree at /foo/bar into a system view xml file
37+
$session->exportSystemView(
38+
'/data/sibling',
39+
$file,
40+
false, // do not skip binary properties
41+
false
42+
);
43+
44+
fclose($file);
45+
46+
Importing detects the format automatically. If the document is a valid JCR
47+
system view, it is interpreted according to that format, otherwise if it is a
48+
valid XML document it is imported as document:
49+
50+
.. code-block:: php
51+
52+
<?php
53+
$filename = 'dump.xml';
54+
$session->getRootNode()->addNode('imported_data', 'nt:unstructured');
55+
$session->importXML(
56+
'/imported_data', // attach the imported data at this node
57+
$filename,
58+
ImportUUIDBehaviorInterface::IMPORT_UUID_CREATE_NEW
59+
);
60+
61+
When importing nodes with a uuid, a couple of different behaviors can be used:
62+
63+
* `IMPORT_UUID_CREATE_NEW`: Create new UUIDs for nodes that are imported, so you never get collisions.
64+
* `IMPORT_UUID_COLLISION_THROW`: Throw an exception if a node with the same UUID already exists.
65+
* `IMPORT_UUID_COLLISION_REMOVE_EXISTING`: Remove an existing node if an imported node has the same UUID.
66+
* `IMPORT_UUID_COLLISION_REPLACE_EXISTING`: Replace existing node with the imported node. This can lead to the imported data being put in various places.

book/index.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
The Book
2+
========
3+
4+
.. toctree::
5+
6+
getting_started
7+
introduction
8+
reading_data_and_traversal
9+
references
10+
shareable_nodes
11+
same_name_siblings
12+
query
13+
writing
14+
orderable_child_nodes
15+
locking
16+
versioning
17+
transactions
18+
import_export
19+
observation
20+
node_types
21+
search
22+
performance
23+
conclusion

book/introduction.rst

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
Introduction
2+
============
3+
4+
In the following chapters, we will show how to use the API. But first, you
5+
need a very brief overview of the core elements of PHPCR. After reading this
6+
tutorial, you should browse through the API documentation to get an idea what
7+
operations you can do on each of those elements. See the conclusions for links
8+
if you want to have more background.
9+
10+
It is important to know about the following concepts:
11+
12+
* **Node, Property**: An object model for data structured similar to XML. A
13+
node is like the xml element, the property like the xml attribute.
14+
Properties are acquired from their nodes or directly from the session by
15+
their path. Nodes are acquired from parent nodes or from the session by
16+
their path. Both are Item, sharing the methods of that base interface. Names
17+
can be namespaced as in xml, and additionally may contain whitespaces or
18+
other not xml-legal characters.
19+
* **Session**: The authenticated connection to one workspace in the
20+
repository. Repository and workspace are immutable inside the Session. The
21+
session is the main interface to interact with the actual data. Sessions are
22+
acquired from a repository
23+
* **Repository**: Linking to one storage location with possibly many
24+
workspaces. Repositories are created with the help of the repository
25+
factory.
26+
* **RepositoryFactory**: Create repository instances for your implementation
27+
with implementation specific parameters.
28+
* **Workspace**: Provides general operations on the workspace of the Session it is acquired from.
29+
30+
Note on Implementation PHPCR Support
31+
------------------------------------
32+
33+
PHPCR is a modular standard and has a built-in way to discover the
34+
capabilities of your implementation. Therefore implementations do not
35+
have to support all sections of the specification.
36+
37+
TODO: Add a section about capability testing to show how to write portable code.

book/locking.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
Locking
2+
=======
3+
4+
In PHPCR, you can lock nodes to prevent concurrency issues. There is two basic types of locks:
5+
6+
* Session based locks are only kept until your session ends and released automatically on logout.
7+
* If a lock is not session based, it is identified by a lock token and stays in place until it times out
8+
9+
Note that jackalope currently only implements session based locks:
10+
11+
.. code-block:: php
12+
13+
<?php
14+
//get the node from the session
15+
$node = $session->getNode('/data/sibling');
16+
//the node has to be lockable
17+
$node->addMixin('mix:lockable');
18+
$session->save(); //node needs to be clean before locking
19+
20+
// get the lock manager
21+
$workspace = $session->getWorkspace();
22+
$lockManager = $workspace->getLockManager();
23+
var_dump($lockManager->isLocked('/data/sibling')); // should be false
24+
$lockManager->lock('/data/sibling', true, true); // lock child nodes as well, release when session closed
25+
// now only this session may change the node //sibling and its descendants
26+
var_dump($lockManager->isLocked('/data/sibling')); // should be true
27+
var_dump($lockManager->isLocked('/data/sibling/child1')); // should be true because we locked deep
28+
29+
// getting the lock from LockManager is not yet implemented with jackalope-jackrabbit
30+
$lock = $lockManager->getLock('/data/sibling');
31+
var_dump($lock->isLockOwningSession()); // true, this is our lock, not somebody else's
32+
var_dump($lock->getSecondsRemaining()); // PHP_INT_MAX because this lock has no timeout
33+
var_dump($lock->isLive()); // true
34+
35+
$node = $lock->getNode(); // this gets us the node for /sibling
36+
$node === $lockManager->getLock('/data/sibling')->getNode(); // getnode always returns the lock owning node
37+
38+
// now unlock the node again
39+
$lockManager->unlock('/data/sibling'); // we could also let $session->logout() unlock when using session based lock
40+
var_dump($lockManager->isLocked('/data/sibling')); // false
41+
var_dump($lock->isLive()); // false

book/node_types.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Node Types
2+
==========
3+
4+
PHPCR supports node types. Node types define what properties and children a node can or must have. The JCR specification explains exhaustivly what node types exist and what they are required to have or not: `JCR 2.0: 3.7.11 Standard Application Node Types <http://www.day.com/specs/jcr/2.0/3_Repository_Model.html#3.7.11%20Standard%20Application%20Node%20Types>`_
5+
6+
In a nutshell:
7+
8+
* `nt:unstructured` does not define any required properties but allows any property or child.
9+
* `nt:file` and `nt:folder` are built-in node types useful to map a file structure in the repository. (With jackalope-jackrabbit, files and folders are exposed over webdav)
10+
* If you **do not** want to enforce a schema on your node, use
11+
`nt:unstructured`.
12+
* If you need to store additional properties or children on existing node types like files, note that while a node can have only one primary type, every node can have any mixin types. Define a mixin type declaring your additional properties, register it with PHPCR and addMixin it to the nodes that need it.
13+
14+
You can define your own node types if you want the equivalent of a strictly defined database structure. See `JCR 2.0: 3.7 Node Types <http://www.day.com/specs/jcr/2.0/3_Repository_Model.html#3.7%20Node%20Types>`_ and `JCR 2.0: 19 Node Type Management <http://www.day.com/specs/jcr/2.0/19_Node_Type_Management.html>`_ / `PHPCR Node Type Namespace <http://phpcr.github.io/doc/html/index.html>`_.
15+

book/observation.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
Observation
2+
===========
3+
4+
Observation enables an application to receive notifications of persistent changes to a workspace.
5+
JCR defines a general event model and specific APIs for asynchronous and journaled observation.
6+
A repository may support asynchronous observation, journaled observation or both.
7+
8+
Note that Jackrabbit supports the full observation API but Jackalope currently only implements event journal reading.
9+
10+
Write operations in Jackalope will generate journal entries as expected.
11+
12+
.. code-block:: php
13+
14+
<?php
15+
use PHPCR\Observation\EventInterface; // Contains the constants for event types
16+
17+
// Get the observation manager
18+
$workspace = $session->getWorkspace();
19+
$observationManager = $workspace->getObservationManager();
20+
21+
// Get the unfiltered event journal and go through its content
22+
$journal = $observationManager->getEventJournal();
23+
$journal->skipTo(strtotime('-1 day')); // Skip all the events prior to yesterday
24+
foreach ($journal as $event) {
25+
// Do something with $event (it's a Jackalope\Observation\Event instance)
26+
echo $event->getType() . ' - ' . $event->getPath();
27+
}
28+
29+
// Filtering and using the journal as an iterator
30+
// You can filter the event journal on several criteria, here we keep events for node and properties added
31+
$journal = $observationManager->getEventJournal(EventInterface::NODE_ADDED | EventInterface::PROPERTY_ADDED);
32+
33+
while ($journal->valid()) {
34+
$event = $journal->current();
35+
// Do something with $event
36+
$journal->next();
37+
}
38+
39+

book/orderable_child_nodes.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Orderable child nodes
2+
=====================
3+
4+
While moving is about changing the parent of a node, ordering is used to set the
5+
position inside the child list. Preserving and altering order is an optional
6+
feature of PHPCR.
7+
8+
The only method needed is Node::orderBefore
9+
10+
.. code-block:: php
11+
12+
<?php
13+
//get the node from the session
14+
$node = $session->getNode('/data/node');
15+
16+
$node->addNode('first');
17+
$node->addNode('second'); // new nodes are added to the end of the list
18+
// order is: first, second
19+
20+
// ordering is done on the parent node. the first argument is the name of
21+
// the child node to be reordered, the second the name of the node to moved
22+
// node is placed before
23+
$node->orderBefore('second', 'first');
24+
// now the order is: second, first
25+

book/performance.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Performance considerations
2+
==========================
3+
4+
While PHPCR can perform reasonably well, you should be careful. You are working with an object model mapping interlinked data. Implementations are supposed to lazy load data only when necessary. But you should take care to only request what you actually need.
5+
6+
The implementations will also use some sort of storage backend (Jackrabbit, (no)SQL database, ...). There might be a huge performance impact in configuring that storage backend optimally. Look into your implementation documentation if there are recommendations how to optimize storage.
7+
8+
One thing *not* to worry about is requesting the same node with Session::getNode or Node::getNode/s several times. You always get the same object instance back without overhead.
9+
10+
11+
Only request what you need
12+
--------------------------
13+
14+
emember that you can filter nodes on Node::getNodes if you only need a list of specific nodes or all nodes in some namespace.
15+
16+
The values of binary properties can potentially have a huge size and should only loaded when really needed. If you just need the size, you can get the property instance and do a $property->getSize() instead of filesize($node->getPropertyValue). Any decent implementation will not preload the binary stream when you access the property object.
17+
18+
When getting the properties from a node, you can use Node::getPropertiesValues(filter, false). This allows the implementation to avoid instantiating Property objects for the property values (and saves you coding). The second boolean parameter tells wheter to dereference reference properties. If you do not need the referenced objects, pass false and you will get the UUID or path strings instead of node objects.(If you need one of them, you can still get it with Session::getNodeByIdentifier. But then the implementation will certainly not be able to optimize if you get several referenced nodes.)
19+
20+
21+
But request in one call as much as possible of what you need
22+
------------------------------------------------------------
23+
24+
If you need to get several nodes where you know the paths, use Session::getNodes with an array of those nodes to get all of them in one batch, saving round trip time to the storage backend.
25+
26+
lso use Node::getNodes with a list of nodes rather than repeatedly calling Node::getNode.
27+

0 commit comments

Comments
 (0)