Developer
This chapter will explain different usecases for developer working with headless
extension.
New cObjects
EXT:headless comes with a bunch of new cObjects to be used via TypoScript:
- BOOL
- FLOAT
- INT
- JSON
- CONTENT_JSON
BOOL
, FLOAT
and INT
are basically like TEXT
(with value
and std
properties!) but make sure their result is being cast to bool, float or int.
JSON
To build and render a JSON object into your page output.
lib.meta = JSON
lib.meta {
if.isTrue = 1
fields {
title = TEXT
title {
field = seo_title
stdWrap.ifEmpty.cObject = TEXT
stdWrap.ifEmpty.cObject {
field = title
}
}
robots {
fields {
noIndex = BOOL
noIndex.field = no_index
}
}
ogImage = TEXT
ogImage {
dataProcessing {
10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
10 {
as = media
references.fieldName = og_image
processingConfiguration {
returnFlattenObject = 1
}
}
}
}
}
dataProcessing {
}
stdWrap {
}
}
The JSON cObjects comes with a bunch of properties: if
, fields
, data
and std
.
Property `if`
Can be used to decide whether or not to render the object.
Property `fields`
Array of cObjects. With special properties per item:
intval
/floatval
/boolval
to cast the result to int, float or bool.if
to return null in case it's emptyEmpty Return Null if
to remove the item in case it's emptyEmpty Unset Key data
to render data processors (have a look atProcessing lib.
for example)meta. og Image
Property `dataProcessing`
This property can be used to render data processors such as MenuProcessor.
Property `stdWrap`
This property can be used to run std
to the already rendered JSON output.
CONTENT_JSON
This cObject basically behaves like TYPO3's CONTENT
, the main difference is that content elements are grouped by col
& encoded into JSON by default.
CONTENT_
has the same options as CONTENT
but also offers two new options for edge cases in json context.
merge
This option allows to generate another CONTENT_
call in one definition & then merge both results into one dataset
(useful for handling slide feature of CONTENT cObject).
lib.content = CONTENT_JSON
lib.content {
table = tt_content
select {
orderBy = sorting
where = {#colPos} != 1
}
merge {
table = tt_content
select {
orderBy = sorting
where = {#colPos} = 1
}
slide = -1
}
}
doNotGroupByColPos = 0(default)|1
This option allows to return a flat array (without grouping by colPos) but still encoded into JSON.
lib.content = CONTENT_JSON
lib.content {
table = tt_content
select {
orderBy = sorting
where = {#colPos} != 1
}
doNotGroupByColPos = 1
}
Internal Extbase plugins
To integrate a custom frontend plugin which returns its data inside the JSON object, we have to do the following:
Follow the standard proceeding to register and configure extbase plugins:
Create the Demo
:
class DemoController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
public function indexAction() {
return json_encode([
'foo' => 'bar',
'settings' => $this->settings
]);
}
}
Use the plugin through TypoScript:
tt_content.list =< lib.contentElementWithHeader
tt_content.list {
fields {
content {
fields {
data = CASE
data {
key.field = list_type
demoplugin_type = USER
demoplugin_type {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
vendorName = Vendor
extensionName = ExtName
pluginName = DemoPlugin
controller = Demo
settings {
test = TEXT
test.value = The demo is working
}
}
}
}
}
}
}
Clear the cache and in the response we will see the following JSON output (shortened):
{
"content": {
"colPos0": [{
"type": "demoplugin_type",
"appearance": {...},
"content": {
"data": {
"foo": "bar",
"test": {
"value": "The demo is working",
"_typoScriptNodeValue": "TEXT"
}
}
}
}]
}
}
Integrating external plugins
The integration of other extension plugins is pretty simple. We're providing the headless_news extension as an example of how it works.
Main part is a user function definition to run a plugin from TypoScript:
tt_content.list =< lib.contentElementWithHeader
tt_content.list {
fields {
content {
fields {
data = CASE
data {
key.field = list_type
news_pi1 = USER
news_pi1 {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
vendorName = GeorgRinger
extensionName = News
pluginName = Pi1
controller = News
view < plugin.tx_news.view
persistence < plugin.tx_news.persistence
settings < plugin.tx_news.settings
}
}
}
}
}
}
For any other plugin, just change the vendor
, extension
, plugin
and controller
options,
and import needed constant and setup values (like for view, persistence and settings in this case).
Then use the constants of that extension to overwrite the paths to the fluid templates:
plugin.tx_news {
view {
templateRootPath = EXT:headless_news/Resources/Private/News/Templates/
partialRootPath = EXT:headless_news/Resources/Private/News/Partials/
layoutRootPath = EXT:headless_news/Resources/Private/News/Layouts/
}
}
As last step we need to re-implement the template logic to generate JSON instead of HTML structure. We do this by creating Fluid templates at the location specified in the previous step.
Because we don't enforce any standard for the JSON structure, we are pretty free here to adjust the structure to our needs (or to the requests of our frontend developer).
Here is the shortened List.
template which generates news items into a JSON array:
<f:spaceless>
{"list": [<f:for each="{news}" as="newsItem" iteration="newsIterator">
<f:if condition="{settings.excludeAlreadyDisplayedNews}">
<f:then>
<n:format.nothing>
<n:excludeDisplayedNews newsItem="{newsItem}"/>
</n:format.nothing>
</f:then>
</f:if>
<f:render section="NewsListView" arguments="{newsItem: newsItem,settings:settings,iterator:iterator}" />
{f:if(condition: newsIterator.isLast, else: ',')}
</f:for>],
"settings":
<f:format.raw>
<f:format.json value="{
orderBy: settings.orderBy,
orderDirection: settings.orderDirection,
templateLayout: settings.templateLayout,
action: 'list'
}"/>
</f:format.raw>
}
</f:spaceless>
Create custom content elements
To add custom content elements we can straight follow the native approach of TYPO3 and fluid_styled_content.
The only difference to make it work with headless
is the configuration of the frontend template in TypoScript.
There is an overwritten content object reference in lib.
which we can use, as well as an extended
object with a header definition lib.
:
tt_content.demo >
tt_content.demo =< lib.contentElementWithHeader
tt_content.demo {
fields {
content {
fields {
demoField = TEXT
demoField.value = This is a demo content-element
bodytext = TEXT
bodytext {
field = bodytext
parseFunc =< lib.parseFunc_RTE
}
demoSubfields {
fields {
demoSubfield = TEXT
demoSubfield.value = Nested field
}
}
}
}
}
}
The definition of fields
can be nested until various depth to reflect our desired JSON structure. Also the use of
dataProcessing
is possible the native way like in any other content elements (see content element definitions of this extension).
Create custom TypoScript
To add a default TypoScript object (such as CONTENT
) to the fields of your page object you need to make sure to render it a valid JSON.
Here's an example of how you can create a JSON array of multiple objects from a custom DB table:
lib.page {
fields {
related = CONTENT
related {
table = tx_myextension_domain_model_things
select {
pidInList = this
}
renderObj = JSON
renderObj {
fields {
title = TEXT
title.field = title
link = TEXT
link.typolink.parameter.field = uid
link.typolink.returnLast = url
}
# Add recognizable token at the end of this item
stdWrap.wrap = |###BREAK###
}
stdWrap {
# Wrap items into square brackets
innerWrap = [|]
# Replace 'inner tokens' by comma, remove others
split {
token = ###BREAK###
cObjNum = 1 |*|2|*| 3
1 {
current = 1
stdWrap.wrap = |
}
2 < .1
2.stdWrap.wrap = ,|
3 < .1
}
}
}
}
}
Meta data override
Here's an example of how to override the meta object by data from a DB record:
lib.meta.stdWrap.override.cObject = JSON
lib.meta.stdWrap.override.cObject {
if.isTrue.data = GP:tx_news_pi1|news
dataProcessing.10 = FriendsOfTYPO3\Headless\DataProcessing\DatabaseQueryProcessor
dataProcessing.10 {
table = tx_news_domain_model_news
uidInList.data = GP:tx_news_pi1|news
uidInList.intval = 1
pidInList = 0
max = 1
as = records
fields < lib.meta.fields
fields {
title = TEXT
title.field = title
subtitle = TEXT
subtitle.field = teaser
description = TEXT
description.field = bodytext
}
returnFlattenObject = 1
}
}
TypoScript DataProcessors
This extension provides a couple of handy DataProcessors.
- DatabaseQueryProcessor
- ExtractPropertyProcessor
- FilesProcessor
- FlexFormProcessor
- GalleryProcessor
- MenuProcessor
- RootSiteProcessor
For further information have a look into the default TypoScript to see them in action.
EXT:form & form output decorators
EXT:headless out of box provides for developers:
Friends
Of TYPO3\ Headless\ Form\ Decorator\ Form Definition Decorator Friends
Of TYPO3\ Headless\ Form\ Decorator\ Abstract Form Definition Decorator Friends
Of TYPO3\ Headless\ Form\ Decorator\ Definition Decorator Interface
Form
is default decorator and outputs
form: {
id: "ContactForm-1",
api: {
status: null,
errors: null,
actionAfterSuccess: null,
page: {
current: 0,
nextPage: null,
pages: 1
}
},
i18n: {},
elements: []
}
You can anytime extend & customize for your needs simply by creating a custom
decorator which implements Definition
or extend the provided
Abstract
which provides the ability to override the
definition of each element or the whole form definition.
After creating a custom decorator you can attach it to your form simply by setting
form
in the rendering options of the form, see more
Snippets
See issue #136