Direkt zum Inhalt springen Direkt zur Navigation springen Direkt zum Footer springen

Relationen in TYPO3 - Teil 2: 1:n Relation

Ein Beispiel für eine 1:n Relation und die Relation von Mutter und Kindern: Eine Mutter kann mehrere Kinder haben, aber jedes Kind hat nur eine Mutter.

Es gibt zwei Möglichkeiten, eine 1:n Relation herzustellen: Selectbox und IRRE.

Der Datentyp "IRRE" ist speziell für 1:n Relationen geschaffen worden. Mehr zu diesem Datentyp gibt es in der TCA Referenz: docs.typo3.org/typo3cms/TCAReference/ColumnsConfig/Type/Inline.html

Selectbox

Will man eine 1:n Relation über eine einfache Selectbox lösen, muss die Relation vom Kindobjekt aus erfolgen. Damit handelt es sich genau genommen um eine n:1 Relation, aber das nur am Rande.

Das Datenbankfeld für die Relation im Kinddatensatz sieht so aus:

typo3conf/ext/my_extension/ext_tables.sql:

CREATE TABLE tx_myextension_domain_model_children (
[...]
 mother int(11) DEFAULT '0' NOT NULL
[...]
);

Das TCA sieht so aus:

typo3conf/ext/my_extension/Configuration/TCA/tx_myextension_domain_model_children.php:

'mother' => array(
  'config' => array(
    'type' => 'select',
    'foreign_table' => 'tx_myextension_domain_model_mother',
  ),
),

Die Relation erfolgt also vom Kindobjekt zum Mutterobjekt.

typo3conf/ext/my_extension/Classes/Domain/Model/Child.php

/**
 * mother
 *
 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Oliverweiss\MyExtension\Domain\Model\Mother>
 * @cascade remove
 */
protected $mother = null;

/**
 * Returns the mother
 *
 * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Oliverweiss\MyExtension\Domain\Model\Mother> $mother
 */
public function getMother()
{
    return $this->mother;
}

/**
 * Sets the mother
 *
 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Oliverweiss\MyExtension\Domain\Model\Mother> $mother
 * @return void
 */
public function setMother(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $mother)
{
    $this->mother = $mother;
}

Ausgabe im Frontend:

Diese Lösung ist relativ einfach, kann aber nur in eine Richtung, nämlich vom Kind zur Mutter gepflegt und auch nur so im Frontend ausgegeben werden (es sei denn man macht witzige Spielchen im Controller). Um auch die andere Richtung zu ermöglichen, brauchen wir eine bidirektionale Relation. Und die funktioniert auf der Mutterseite nur als IRRE Element.

IRRE Element

Das IRRE-Element für eine 1:n Relation wird in der Muttertabelle gepflegt:

Hierfür brauchen wir zunächst ein neues Feld in der Muttertabelle:

typo3conf/ext/my_extension/ext_tables.sql:

CREATE TABLE tx_myextension_domain_model_mother (
[...]
 children varchar(255) DEFAULT '' NOT NULL
[...]
);

typo3conf/ext/my_extension/Configuration/TCA/tx_myextension_domain_model_mother.php:

'children' => [
    'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_myextension_domain_model_mother.children',
    'config' => [
        'type' => 'inline',
        'foreign_table' => 'tx_myextension_domain_model_children',
    ],

],

In diesem Feld erfolgt die 1:n Relation also vom Mutterobjekt aus.

In der Datenbank wird die Relation im Feld children gespeichert. Allerdings geschieht dies als Kommaliste, da es ja mehr als ein Kindelemente geben kann. Das ist erstens aus technischer Sicht unschön. Vor allem aber ist es noch keine bidirektionale Relation, denn die Relation kann so nur vom Mutterelement gepflegt werden. Wir wollen es aber auch vom Kindelement pflegbar haben. Hierzu ändern wir zunächst den Feldtyp in der SQL zum Typ Integer:

typo3conf/ext/my_extension/ext_tables.sql:

CREATE TABLE tx_myextension_domain_model_mother (
[...]
 children int(11) DEFAULT '0' NOT NULL
[...]
);

Anschließend fügen wir dem TCA noch eine Zeile hinzu:

typo3conf/ext/my_extension/Configuration/TCA/tx_myextension_domain_model_mother.php:

'children' => [
    'exclude' => true,
    'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_myextension_domain_model_mother.children',
    'config' => [
        'type' => 'inline',
        'foreign_table' => 'tx_myextension_domain_model_children',
        'foreign_field' => 'mother'
    ],

],

Damit legen wir fest, dass die Relation in der Kindtabelle im Feld mother gespeichert wird. Das ist ja auch jenes Feld, das wir weiter oben als Selectbox angelegt haben. Wir haben im Backend nun also beides: Im Mutterelement ein IRRE-Element, in dem man die Kinder auswählen und anlegen kann und im Kindelement eine Selectbox, in dem man die Mutter auswählen kann.

Das Model für die Muttertabelle sieht so aus:

typo3conf/ext/my_extension/Classes/Domain/Model/Mother.php

/**
 * children 
 *
 * @var \Oliverweiss\myextension\Domain\Model\Children>
 */ 
protected $children; 

/*
 *
 * Returns the children
 * 
 * @return \Oliverweiss\myextension\Domain\Model\Children> $children 
 */ 
public function getChildren() {
    return $this->children;
}

/**
 * Sets the children
 *
 * @param \Oliverweiss\myextension\Domain\Model\Children> $children
 * @return void
 */
public function setCategory(\Oliverweiss\myextension\Domain\Model\Children> $children) {
    $this->children = $children;
}

Im Frontend können wir nun auch über das Mutterobjekt auf deren Kindobjekte zugreigen:

Bei einer bidirektionalen Relation verbinden wir also eine n:1 Relation (Kind -> Mutter) mit einer 1:n Relation (Mutter -> Kinder).