Message-ID: <2027898623.3754.1485855170121.JavaMail.confluence@ip-10-127-227-164> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_3753_6057081.1485855170121" ------=_Part_3753_6057081.1485855170121 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
eZ Platform can support arbitrary data to be stored in the fields of a c= ontent item. In order to support custom data, besides the standard data typ= es, a developer needs to create a custom Field Type.
The implementation of a custom Field Type is done based on the Field Typ=
e SPI and its interfaces. These can be found under eZ\Publish\SPI\Fie=
ldType
.
In order to provide custom functionality for a Field Type, the SPI inter= acts with multiple layers of the eZ Platform architecture, as shown in the = following diagram:
On the top layer, the Field Type needs to provide conversion from and to= a simple PHP hash value to support the REST API. The generated hash value = may only consist of scalar values and hashes. It must not contain objects o= r arrays with numerical indexes that aren't sequential and/or don't start w= ith zero.
Below that, the Field Type must support the Public API implementation (a= ka Business Layer), regarding:
On the bottom level, a Field Type can additionally hook into the Persist=
ence SPI, in order to store data from a FieldValue in an external service. =
Note that all non-standard eZ Publish database tables (e.g. ezur=
l
) are also considered "external storage" from now on.
The following sequence diagram visualizes the process of creating a new =
Content
across all layers, especially focused on the interacti=
on with a FieldType
.
In the next lines/pages, this document explains how to implement a custo= m Field Type based on the SPI and what is expected from it. As a code examp= le, please refer to the Url FieldType, which has been implemented as a refe= rence.
The interaction with the Public API is done through the interface =
eZ\Publish\SPI\FieldType\FieldType
. A custom Field Type must provide=
an implementation of this interface. In addition, it is considered best pr=
actice to provide a value object class for storing the custom field value p=
rovided by the Field Type.
In order to make use of a custom Field Type, the user must apply it in a=
eZ\Publish\API\Repository\Values\ContentType\FieldDefinition
=
of a custom Content Type. The user may in addition provide settings for the=
Field Type and a validator configuration. Since the Public API canno=
t know anything about these, their handling is delegated to the Field Type =
itself through the following methods:
getFieldTypeIdentifier()
Returns a unique identifier for the custo=
m Field Type, which is used to assign the type to a Field Definition. By co=
nvention for the returned type identifier string should be prefixed by a un=
ique shortcut for the vendor (e.g. ez
for eZ Systems).
getSettingsSchema()
Using this method, the Public API retriev=
es a schema for the field type settings. A typical setting would e.g. be a =
default value. The settings structure defined by this schema is stored in t=
he FieldDefinition
. Since it is not possible to define a gener=
ic format for such a schema, the Field Type is free to return any serializa=
ble data structure from this method.
getValidatorConfigurationSchema()
In addition to normal settings, the Field=
Type should provide a schema settings for it's validation process. Th=
e schema describes, what kind of validation can be performed by the Field T=
ype and which settings the user can specify to these validation methods. Fo=
r example, the ezstring
type can validate minimum and maximum =
length of the string. It therefore provides a schema to indicate to the use=
r that he might specify the corresponding restrictions, when creating a
validateFieldSettings()
Before the Public API stores settings for=
the FieldType
in a FieldDefinition
, the type is =
asked to validate the settings (which were provided by the user). As a resu=
lt, the FieldType
must return if the given settings comply to =
the schema defined by getSettingsSchema()
. validateValid=
atorConfiguration()
Analog to validateFieldSettings()
, =
this method verifies that the given validator configuration complies to the=
schema provided by getValidatorConfigurationSchema()
.
It is important to note that while the schema definitions of the F=
ieldType
maybe both be of arbitrary, serializable format, it is high=
ly recommended to use a simple hash structure. It is highly recommended to =
follow the Best pra=
ctices in order to create future proof schemas.
Note: Since it is not possible to enforce a schema form=
at, the code using a specific FieldType
must basically know al=
l FieldType
s it deals with.
This will also apply to all user interfaces and the REST API, which ther=
efore must provide extension points to register handling code for custom
If you implement Nameable as an extra service, and register this Service=
using the tag nameable
, the method {FieldType}Nameable-=
>getFieldName()
will be used to retrieve the name.
Otherwise the regular {FieldType}->getName()
method is u=
sed.
# Nameable services (for fieldtypes that need advance name h= andling) ezpublish.fieldType.ezobjectrelation.nameable_field: class: %ezpublish.fieldType.ezobjectrelation.nameable_field.class% arguments: - @ezpublish.spi.persistence.cache.contentHandler tags: - {name: ezpublish.fieldType.nameable, alias: ezobjectrelation}==20
A field type needs to deal with the custom value format provided by it. =
In order for the public API to work properly, it delegates working with suc=
h custom field values to the corresponding Field Type. The SPI\FieldT=
ype\FieldType
interface therefore provides the following methods:
acceptValue()
This method is responsible for accepting =
and converting user input for the field. It checks the input structure it a=
ccepts and might build and return a different structure holding the data. A=
n example would be, that the user just provides an HTTP link as a string, w=
hich is converted to the value object of the Url FieldType. Unlike the
Note: The method must assert structural c= onsistency of the value, but must not validate plausibility of the value.= p>
getEmptyValue()
Through settings, the FieldType can specify, that the user may define a default value for the
itself is asked for an "empty value" as the final fallba=
ck. The value chain for a specific field is therefore like this, when a Fie=
ld
of the type. If no such default is provided by the user, the FieldType
is filled out:
FieldDefinition
?FieldType
validate()
In contrast to acceptValue()
=
this method validates the plausibility of the given value, based on the FieldDefinition
.
As said above, the value format of a FieldType
is free form=
. However, in order to make eZ Publish store the value in it's database, it=
must comply to certain rules at storage time. To not restrict the value it=
self, a FieldValue
must be converted to the storage specific f=
ormat used by the Persistence SPI: eZ\Publish\SPI\Persistence\Content=
\FieldValue
. After restoring a Field of FieldType
, the =
conversion must be undone. The following methods of the FieldType are responsible for that:
toPersistenceValue()
This method receives the value of a Field
of FieldType
and must return an SPI FieldV=
alue
, which can be stored.
fromPersistenceValue()
As the counterpart, this method receives =
an SPI FieldValue
and must reconstruct the original value of t=
he Field
from it.
The SPI FieldValue struct has several properties, which might be used by= the FieldType as follows:
$data
The data to be stored in the eZ Publish d= atabase. This may either be a scalar value, a hash map or a simple, seriali= zable object.
$externalData
The arbitrary data stored in this field w= ill not be touched by any of the eZ Publish components directly, but will b= e hold available for Storing external data.
$sortKey
An value which can be used to sort =
Content
by the field.
Note: TBD: Where will = you register the Indexable implementations?
Fields, or a custom field type, might contain or maintain data which is = relevant for user searches. To make the search engine aware of the data in = your field type you need to implement an additional interface and register = the implementation.
If your field type does not maintain any data, which should be available= to search engines, feel free to just ignore this section.
The eZ\Publish\SPI\FieldType\Indexable
defines two methods,=
which are required to be implemented, if the field type provides data rele=
vant to search engines. The interface defines two methods for this:
getIndexData( Field $field )
This method is supposed to return the act=
ual index data for the provided eZ\Publish\SPI\Persistence\Content\Fi=
eld
. The index data consists of an array of eZ\Publish\SPI\Per=
sistence\Content\Search\Field
instances. They are described below in=
further detail.
getIndexDefinition()
To be able to query data properly an inde=
xable field type also is required to return search specification. You must =
return a hash map of eZ\Publish\SPI\Persistence\Content\Search\FieldT=
ype
instances from this method, which could look like:
array( 'url' =3D> new Search\FieldType\StringField(), 'text' =3D> new Search\FieldType\StringField(), )=20
This example from the Ur=
l
field type shows that the field type will always return two indexa=
ble values, both strings. They have the names url
and te=
xt
respectively.
The search field values, returned by the getIndexData
metho=
d are simple value objects consisting of the following properties:
$name
The name of the field
$value
The value of the field
$type
An eZ\Publish\SPI\Persistence\=
Content\Search\FieldType
instance, describing the type information o=
f the field.
There are bunch of available search field types, which are automagically= handled by our Search backend configuration. When using those there is no = requirement to adapt , for example, the Solr configuration in any way. You = can always use custom field types, though, but these might require re-confi= guration of the search backend. For Solr this would mean adapting the schem= a.xml.
The default available search field types are:
StringField.php
Standard string values. Will also be quer= ies by full text searches.
TextField.php
Standard text values. Will be queried by = full text searches. Configured text normalizations in the search backend ap= ply.
BooleanField.php
Boolean values.
DateField.php
Date field. Can be used for date range qu= eries.
FloatField.php
Field for floating point numbers.
IntegerField.php
Field for integer numbers.
PriceField.php
Field for price values. Currency conversi= on might be applied by the search backends. Might require careful configura= tion.
IdentifierField.php
Field used for IDs. Basically acts like t= he string field, but will not be queried by fulltext searches
CustomField.php
Custom field, for custom search data type= s. Will probably require additional configuration in the search backend.
As mentioned before, if you are using the standard type definitions dyn=
amicField
definitions in Solr, for example.
If you want to configure the handling of your field, you can always add =
a special field definition the Solr schema.xml
. The field type=
names, which are used by the Solr search backend look like this for fields=
: <content_type_identifier>/<field_ident=
ifier>/<search_field_name>_<type>
. You can, of=
course define custom dynamicField
definitions to match, for e=
xample, on your custom _<type>
definition.
You could also define a custom field definition dedicatedly for certain = fields, like for the name field in an article:
<field name=3D"article/name/value_s" type=3D"string" indexe= d=3D"true" stored=3D"true" required=3D"false"/>=20
If you want to learn more about the Solr implementation and detailed inf= ormation about configuring it, check out the Solr Search Service Implementation Notes.
A FieldType
may store arbitrary data in external data sourc=
es and is in fact encouraged to do so. External storages can be e.g. a web =
service, a file in the file system, another database or even the eZ Publish=
database itself (in form of a non-standard table). In order to perform thi=
s task, the FieldType
will interact with the Persistence SPI, =
which can be found in eZ\Publish\SPI\Persistence
, through the =
eZ\Publish\SPI\FieldType\FieldStorage
interface.
Whenever the internal storage of a Content item that includes a Field of=
the FieldType
is accessed, one of the following methods is ca=
lled to also access the external data:
hasFieldData()
Returns if the FieldType
sto=
res extrnal data at all.
storeFieldData()
Called right before a Field
=
of FieldType
is stored. The method should perform the storing =
of $externalData
. The method must return true
, if=
the call manipulated internal data of the given Fie=
ld
, so that it is updated in the internal database.
getFieldData()
Is called after a Field
has =
been restored from the database in order to restore $externalData.
deleteFieldData()
Must delete external data for the given <= code>Field, if exists.
getIndexData()
See search service
Each of the above methods receive a $context array, which contains infor= mation on the underlying storage and the environment. This context can be u= sed to store data in the eZ Publish data storage, but outside of the normal= structures (e.g. a custom table in an SQL database). Note that the Field T= ype must take care on it's own for being compliant to different data source= s and that 3rd parties can extend the data source support easily. For more = information about this, take a look at the Best practices section.
The FieldType
system is designed for future storage back en=
ds of eZ Publish. However, the old database schema (Legacy Storage=
) must still be supported. Since this database cannot store arbitrary value=
information as provided by a FieldType
, another conversion st=
ep must take place if the Legacy Storage is used.
The conversion takes place through the interface eZ\Publish\Core\P=
ersistence\Legacy\Content\FieldValue\Converter
, which you must provi=
de an implementation of with your FieldType
. The following met=
hods are contained in the interface:
toStorageValue()
Converts a Persistence Value
=
into a legacy storage specific value.
fromStorageValue()
Converts the other way around.
toStorageFieldDefinition()
Converts a Persistence FieldDefinit=
ion
to a storage specific one.
fromStorageFieldDefinition
Converts the other way around.
getIndexColumn()
Returns the storage column which is used = for indexing.
The registration of a Converter
currently works through the=
$config
parameter of eZ\Publish\Core\Persistence\Legacy=
\Handler
. See the class documentation for further details.
When REST API is used, conversion needs to be done for FieldType=
code> values, settings and validator configurations. These are converted to=
and from a simple hash format that can be encoded in REST payload (typical=
ly XML or JSON). As conversion needs to be done both when transmitting and =
receiving data through REST,
FieldType
implements following pa=
irs of methods:
toHash()
Converts FieldType Value into a plain has= h format.
fromHash()
Converts the other way around.
fieldSettingsToHash()
Converts FieldType settings to a simple h= ash format.
fieldSettingsFromHash()
Converts the other way around.
validatorConfigurationToHash()
Converts FieldType validator configuratio= n to a simple hash format.
validatorConfigurationFromHash()
Converts the other way around.
Some FieldTypes
will require additional processing, for exa=
mple a FieldType
storing a binary file, or one having more com=
plex settings or validator configuration. For this purpose specific impleme=
ntations of an abstract class eZ\Publish\Core\REST\Common\FieldTypePr=
ocessor
are used. This class provides following methods:
preProcessValueHash()
Performs manipulations on a received valu=
e hash, so that it conforms to the format expected by the fromHash()<=
/code> method described above.
postProcessValueHash()
Performs manipulations on a outgoing valu=
e hash, previously generated by the toHash()
method described =
above.
preProcessFieldSettingsHash()
Performs manipulations on a received sett=
ings hash, so that it conforms to the format expected by the fieldSet=
tingsFromHash()
method described above.
postProcessFieldSettingsHash()
Performs manipulations on a outgoing sett=
ings hash, previously generated by the fieldSettingsToHash()
m=
ethod described above.
preProcessValidatorConfigurationHash()
Performs manipulations on a received vali=
dator configuration hash, so that it conforms to the format expected by the=
validatorConfigurationFromHash()
method described above.
postProcessValidatorConfigurationHash()
Performs manipulations on a outgoing vali=
dator configuration hash, previously generated by the validatorConfig=
urationToHash()
method described above.
Base implementations of these methods simply return the given hash, so y=
ou can implement only the methods your FieldType
requires. Som=
e FieldTypes
coming with the eZ Publish installation already i=
mplement processors and you are encouraged to take a look at them.
For details on registering a FieldType
processor, see =
Register Field Type.=
In this chapter, best practices for implementing a custom FieldTyp= e are collected. We highly encourage following these practices to be= future proof.
In order to allow the usage of a FieldType
that uses extern=
al data with different data storages, it is recommended to implement a gate=
way infrastructure and a registry for the gateways. In order to ease this a=
ction, the Core implementation of FieldType
s provides corresp=
onding interfaces and base classes. These can also be used for custom field=
types.
The interface eZ\Publish\Core\FieldType\StorageGateway
is i=
mplemented by gateways, in order to be handled correctly by the registry. I=
t has only a single method:
setConnection()
The registry mechanism uses this method t=
o set the SPI storage connection (e.g. the database connection to the Legac=
y Storage database) into the gateway, which might be used to store external=
data. The connection is retrieved from the $context
array&nbs=
p; automatically by the registry.
Note that the Gateway implementation itself must take care about validat=
ing that it received a usable connection. If it did not, it should throw a =
RuntimeException
.
The registry mechanism is realized as a base class for FieldStorag=
e
implementations: eZ\Publish\Core\FieldType\GatewayBasedStora=
ge
. For managing StorageGateway
s, the following method=
s are already implemented in the base class:
addGateway()
Allows the registration of additional
getGateway()
This protected method is used by the impl=
ementation to retrieve the correct StorageGateway
for the curr=
ent context.
As a reference for the usage of these infrastructure, the Keyword, Url a= nd User types can be examined.
It is recommended to use a simple hash map format for the settings schem= a retured by eZ\Publish\SPI\FieldType\FieldType::getSet= tingsSchema(), which follows these rules:
default
=
)type
to identify the setting type (e.g. int
o=
r string
)default
containing the default setting valueAn example schema could look like this:
array( 'backupData' =3D> array( 'type' =3D> 'bool', 'default' =3D> false ), 'defaultValue' =3D> array( 'type' =3D> 'string', 'default' =3D> 'Sindelfingen' ) );=20
The schema for validator configuration should have a similar format than= the settings schema has, except it has an additional level, to group setti= ngs for a certain validation mechanism:
For example, for the ezstring
type, the validator schema co=
uld be:
array( 'stringLength' =3D> array( 'minStringLength' =3D> array( 'type' =3D> 'int', 'default' =3D> 0, ), 'maxStringLength' =3D> array( 'type' =3D> 'int' 'default' =3D> null, ) ), );=20
To register a Field Type, see Register Field Type.
To be integrated in unit and integration tests, Field Types need to be r=
egistered through the service.ini
in eZ/P=
ublish/Core/settings
.
A FieldType always need a piece of template to be correctly displayed. S= ee Field Type templ= ate.
FieldType
s should be integration tested on 1 different lev=
els:
For both test environments, infrastructure is already in place, so that =
you can easily implement the required tests for your custom FieldType=
This type of integration test ensures, that a Field Type stores its data= properly on basis of different Persistence SPI implementations.
The integration tests with the Persistence SPI can be found in eZ\=
Publish\SPI\Tests\FieldType
. In order to implement a test for your c=
ustom FieldType
, you need to extend the common base class KeywordIntegrationTest
, UrlIntegrationTest
and UserIntegrationTest
can=
deal.
Running the test is fairly simple: Just specify the global phpunit=
.xml
for PHPUnit configuration and make it execute a single test or =
a directory of tests, for example:
$ phpunit -c phpunit.xml eZ/Publish/SPI/Tests/FieldType=20
in order to run all FieldType
tests.
On a second level, the interaction between an implementation of the Publ=
ic API (aka the Business Layer) and the Field Type is tested. Again, there =
is a common base class as the infrastructural basis for such tests, which r=
esides in eZ\Publish\API\Repository\Tests\FieldType\BaseIntegrationTe=
st
.
Note that the In-Memory stubs for the Public API integration test suite,= do not perform actual Field Type calls, but mainly emulate the behavior of= a Field Type for simplicity reasons.
If your Field Type needs to convert data between storeFieldData()<=
/code> and
getFieldData()
, you need to implement a eZ\Pu=
blish\API\Repository\Tests\Stubs\PseudoExternalStorage
in addition, =
which performs this task. Running the tests against the Business Layer impl=
ementation of the Public API is not affected by this.