You are currently viewing Use content element relations in TYPO3 extensions

Use content element relations in TYPO3 extensions

Wouldn’t it be cool to enable editors to use standard or custom content elements in your extensions? The consequence is that you do not have to develop each and every functionality again. In this post I will show you, how you can use all available content elements of your installation.

A very prominent example is EXT:news. This is also the extension where I looked at, while developing a extension for a customer, as I needed a similar functionality. Thanks to Georg Ringer and open source, I am able to share some kind of howto about this topic. Here are two screenshots that show, how the content element relations will be shown on the backend:

typo3worx_ce-relations-example03
Multiple CE relations possible
typo3worx_ce-example-02
Inline editing of content elements

Prerequisites

1) A running TYPO3 instance

Hm, ok that sounds obvious, but for completeness I want to mention it here.

2) A minimal TYPO3 extension

In order to use this, you need at least a minimal TYPO3 extension, to which you can add this functionality. If you don’t have one and want to follow this howto codewise, please check the EXT:extension_builder and create an minimal one.

How to use content element relations in your TYPO3 extension

In the following sections I will show the various parts of you extension, which must be touched. If a mentioned file is not already there, you must create it.

Data structure: ext_tables.sql

First, we must tell TYPO3, where to store the content element relations. Therefore we need to extend the table ‚tt_content‘, add a column to the table of the extension containing the data and create an additional mm table. The necessary lines must be added in the file „ext_tables.sql“ of your extension.

#
# Table structure for table 'tt_content'
#
CREATE TABLE tt_content (
   tx_myextension_content_elements int(11) DEFAULT '0' NOT NULL,
);

#
# Table structure for table 'tx_myextension'
#
CREATE TABLE tx_myextension (
   content_elements TEXT
)

#
# Table structure for table 'tx_myextension_domain_model_mymodel_ttcontent_mm'
#
CREATE TABLE tx_myextension_domain_model_mymodel_ttcontent_mm (
   uid_local int(11) DEFAULT '0' NOT NULL,
   uid_foreign int(11) DEFAULT '0' NOT NULL,
   sorting int(11) DEFAULT '0' NOT NULL,
   KEY uid_local (uid_local),
   KEY uid_foreign (uid_foreign)
);

TCA für content element relations

Then the content elements must be added to the TCA. Therefore the parts „interface“, „types“ and „columns“ must be touched. As I did not want to add the complete TCA here, you must replace the “[…]” with the parts of your code.

'interface' => [
   'showRecordFieldList' => [...] . 'content_about, ' . [...]
],
'types' => [
   '1' => [
      'showitem' => [...] .'--div--; Content Elements, content_elements, ' . [...]
   ]
],
'columns' => [
   [...]
   'content_elements' => [
      'exclude' => 1,
      'l10n_mode' => 'mergeIfNotBlank',
      'label' => 'Content Elements',
      'config' => [
         'type' => 'inline',
         'allowed' => 'tt_content',
         'foreign_table' => 'tt_content',
         'foreign_sortby' => 'sorting',
         'foreign_field' => 'tx_myextension_content_elements',
         'minitems' => 0,
         'maxitems' => 99,
         'appearance' => [
            'collapseAll' => 1,
            'expandSingle' => 1,
            'levelLinksPosition' => 'bottom',
            'useSortable' => 1,
            'showPossibleLocalizationRecords' => 1,
            'showRemovedLocalizationRecords' => 1,
            'showAllLocalizationLink' => 1,
            'showSynchronizationLink' => 1,
            'enabledControls' => [
               'info' => false,
            ]
         ]
      ]
   ],
[...]
],

Domain – Model for tt_content

In order to use normal content elements, you must also provide a model in your extension for it. This happens in the file “EXT:myextension/Classes/Domain/Model/TtContent.php“. It provides all setter and getter methods for the table „tt_content“. As the complete source code would be too much for this post: there is a gist for it: https://gist.github.com/mschwemer/f0814450009204fc9a16d05957d77226

Repository for tt_content

Additional to the domain model the repository must be also provided. This is the purpose of the file: “EXT:myextension/Classes/Domain/Repository/TtContentRepository.php“. This is the code, you need:

<?php

namespace MyNameSpace\Myextension\Domain\Repository;

/**
* Repository for tt_content objects
*
*/
class TtContentRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
   protected $objectType = '\MyNameSpace\Myextension\Domain\Model\Ttcontent';
}

Main Model of your extension

In the next step the content elements must be made reachable by your main domain model. Basically it is the normal stuff like defining the property, adding the functions add*, remove* and the getter. But what you in addition is the function “getContentElementIdList“. This return all the IDs which are associated with your record. The output is needed in the next snippet.

/**
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\MyNameSpace\Myextension\Domain\Model\TtContent>
* @lazy
*/
protected $contentElements;

/**
* Initialize content element relation
*
* @return \MyNameSpace\Myextension\Domain\Model\Mymodel
*/
public function __construct()
{
   $this->contentElements = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
}

/**
* Adds a content element to the record
*
* @param \MyNameSpace\Myextension\Domain\Model\TtContent $contentElement
* @return void
*/
public function addContentElement(\MyNameSpace\MyExtension\Domain\Model\TtContent $contentElement)
{
   if ($this->getContentElements() === null) {
      $this->contentElements = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
   }
   $this->contentElements->attach($contentElement);
}

/**
* Get id list of content elements
*
* @return string
*/
public function getContentElementIdList()
{
   $idList = [];
   $contentElements = $this->getContentElements();
   if ($contentElements) {
      foreach ($this->getContentElements() as $contentElement) {
         $idList[] = $contentElement->getUid();
      }
   }
   return implode(',', $idList);
}

/**
* Get content elements
*
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage
*/
public function getContentElements()
{
   return $this->contentElements;
}

TypoScript

This is the TypoScript – Template, that defines the selection of the records, based on the table „tt_content“. It looks quite simple, but is an essential part reusing content elements. It is needed for the fluid template

# Rendering of content elements in detail view
lib.tx_myextension.contentElementRendering = RECORDS
lib.tx_myextension.contentElementRendering {
   tables = tt_content
   source.current = 1
   dontCheckPid = 1
}

Fluid – Template

The fluid template finally renders the content elements with in the plugin of your extension to the frontend.

<f:cObject typoscriptObjectPath="lib.tx_myextension.contentElementRendering">
   {myModel.contentElementIdList}
</f:cObject>

Conclusion

I think this is a nice way to use and add normal content elements in a custom extension. This way you don’t have to re-invent the wheel again and again and blow up your model. Currently all available content elements are allowed. I can imagine, that in most cases a restriction to certain content elements or plugins would be helpful. I did not dig deeper into it, but would be probably worth another post. What do you think?

I want to thank Georg Ringer using this code in his extension “news” and sharing it with us all. It always a good inspiration for me!

Credits
The blogpost image was originally published on pixabay by Snufkin using the CC0 Public Domain License. I modified using Pablo by Buffer.

Leave a Reply