-
Notifications
You must be signed in to change notification settings - Fork 325
Sulu PHPCR API
This document aims to describe a node API which will combine the currently separate PHPCR NodeInterface and Sulu Structure classes into one class. A side-effect of this goal will be the ability to map node types to classes - giving us a type of "object mapper".
This will make mapping PHPCR nodes to structures a non-issue and additionally
enable us to cast different classes depending on their role, so for example
content
nodes can be of a certain class, whilst route
nodes could be of
another.
One of the aims of this refactoring is to enable domain specific classes - i.e. we should cast a specific class for a spefici type of node.
PHPCR is an interface. The objects that we receieve from PHPCR are concrete instances of vendor-specific implementations of the PHCPR interface, therefore we cannot simply extend these classes - doing so would bind us completely to a specific PHPCR vendor.
We are therefore forced to wrap these vendor objects.
Structures are generated by Sulu from the templates (which are stored in XML
files). The generated structures are kept in the cache directory and extend the
Structure
base class
The ContentMapper
then maps values from the PHPCRNode to the Structure
class.
I propose that we make all node types in content
extend from a sulu:structure
node type.
This node type would be a simple definition:
[sulu:structure] > nt:unstructured
And as you can see from above it would exact exactly like the node type nt:unstructured
but
would allow us to do two things:
-
SELECT FROM [sulu:structure] WHERE ...
: Target SQL queries - Cast
sulu:structure
nodes to domain objects.
The second point is very beneficial, as it allows us to most (if not all) the logic in the content
mapper within a domain object (f.e. Sulu\Bundle\CoreBundle\Node\Structure
).
So we are effectively going to unify the PHPCR\NodeInterface
class and the Structure
class so that
the "mapping" operation done by ContentMapper
is not required.
Current persisted system properties:
Name | Type | Description | Notes |
---|---|---|---|
{locale}shadowBaseLanguage | String | Language to use when shadow is enabled | |
{locale}nodeType | integer | ? | |
{locale}creator | Integer | ID of user that created this node | |
{locale}changer | Integer | ID of user that last changed this node | |
{locale}created | DateTime | Date was created | |
{locale}state | Integer | ||
{locale}navContexts | Array | Navigation contexts | Should this be here? Could this be an extension or plugin? |
{locale}published | Date Time | Date node was published | |
{locale}enabledShadowLanguages | Array | List of languages that are "shadowed" | |
{locale}isShadow | Boolean | If the node in the prefixed language is a shadow |
(i.e. items which are inferred from the environment and the persisted properties):
Name | Description |
---|---|
{locale}webspaceKey | Determined from the environment |
{locale}languageCode | Inferred from env. and persisted properties |
{locale}globalState | Determined from state of this and ancestor nodes |
{locale}concreteLanguage | Determined by comparing all the existing languages against the "shadow" languages |
###Proposed API
$structure = $session->getNode('/sulu/sulu_io/contents/hello');
# Locale methods
(string) $structure->getLocale(); // COMPUTED: return the runtime locale of the node (aka LanguageCode)
(array) $structure->getLocales(); // get all mapped locales on this node (including shadows)
(array) $structure->getConcreteLocales(); // COMPUTED: return the concrete locales (i.e. locales which are not shadows)
(string) $structure->getShadowBaseLocale();
(array) $structure->getShadowLocales(); // return the locales for which shadowing is enabled
(boolean) $structure->isShadow(); // COMPUTED: If the current node is a shadow of a different concrete language
# System methods
(integer) $structure->getRole(); // CONTENT, INTERNAL_LINK, EXTERNAL_LINK (aka NodeType)
(integer) $structure->getCreator();
(integer) $structure->getChanger();
(integer) $structure->getState();
(integer) $structure->getComputedState(); // COMPUTED: aka. Global state, aka. Inherited State
(boolean) $structure->getPublished();
(string) $structure->getWebspaceKey(); // COMPUTED: do we really need this?
// Navigation
(array) $structure->getNavContexts();
Changes from the existing API
- Renamed
getNodeType
togetRole
: "NodeType" conflicts with the PHPCR concept. Role implies that this node "acts as" something i.e. acts as content, internal_link, etc. - Renamed
getGlobalState
togetComputedState
- Various changes for the locale methods.
Issues:
- The existing
Sulu\...\Property
class conflicts with the PHPCR concept of Property.- Renmae:
Property
=>Field
- Leave as
Property
and wrap the PHPCR property (so its still the property
- Renmae:
- The property is a mix of the attributes of the PHPCR property and the attributes of the "template" property.
- It has both get/set Value and metadata about the property in the same class
- ContentType refers to the type of the property. It should be renamed to
PropertyType
.
Solutions:
- Rename:
Property
=>StructureProperty
- Renmae:
Property
=>PropertyTemplate
- Remove
get|setValue
- Add
getPropertyTemplate
method to the PHPCR node.
- Remove
$node = $sesssion->getNode('/foo');
$maxOccurs = $node->getProperty('foobar')->getTemplate()->getMaxOccurs();
$tags = $node->getProperty('foobar')->getTemplate()->getTags();
Where getProperty
would return the PHPCR property (either the original or a wrapped one).
Wrapping the properties:
class SomeStructureCache extends Structure
{
public function getProperties()
{
foreach (parent::getProperties() as $phpcrProperty) {
$this->addChild(new StructureProperty(
$phpcrProperty,
}
}
}
Graphing the property attributes:
class SomeStructureCache extends Structure
{
public function init()
{
$this->getProperty('???')->setFoo();
$this->getProperty('???')->setFoo();
}
}
Doesn't work: We need an abstraction to access the translated properties.
We separate the access of the raw PHPCR property and the "sulu" property by adding the concept of the field:
class SomeStructureCache extends Structure
{
public function init()
{
$field1 = new Field('article');
$field1->setAttributes(array(
'title' => array(
'de' => 'Ort',
'en' => 'Place',
),
));
$this->addField($field1);
}
}
$someStructure = $session->get('/some/structure');
$someStructuture->setFieldLocale('de');
// access the field value through the field proxy
$article = $someStructure->getField('article')->getValue();
$article = $someStructure->getProperty('i18n:de-article')->getValue();