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

Relationen in TYPO3 - Teil 1: 1:1 Relation

Bei der 1:1 Relation ist ein Objekt mit genau einem anderen Objekt verknüpft. Dieses Relation kommt relativ selten vor. Beispiel wäre eine Heirat: Ein Partner hat genau einen anderen Partner (zumindest in unserer Welt). Oder eine Person hat genau einen Personalausweis. Eine Person kann nicht mehrere Personalausweise haben und ein Personalausweis gehört genau einer Person.

1:1 Relation in TYPO3

In TYPO3 ist diese Relation nur als IRRE-Element sinnvoll einzusetzen. Würde man eine Selectbox machen, müsste man sich darum kümmern, dass bereits verwendete Objekte nicht noch einmal verknüpft werden können.

So soll es im Backend aussehen:

In unserem Beispiel brauchen wir zunächst zwei Tabellen für Personen und Personalausweise:

#
# Table structure for table tx_myextension_person
#
CREATE TABLE tx_myextension_person (
	uid int(11) NOT NULL auto_increment,
	pid int(11) DEFAULT '0' NOT NULL,
	name varchar(50) DEFAULT '' NOT NULL,
	idcard int(11) DEFAULT '0' NOT NULL,

	tstamp int(11) unsigned DEFAULT '0' NOT NULL,
	crdate int(11) unsigned DEFAULT '0' NOT NULL,
	cruser_id int(11) unsigned DEFAULT '0' NOT NULL,
	sorting int(11) unsigned DEFAULT '0' NOT NULL,
	deleted tinyint(4) unsigned DEFAULT '0' NOT NULL,
	hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
	starttime int(11) unsigned DEFAULT '0' NOT NULL,
	endtime int(11) unsigned DEFAULT '0' NOT NULL,

	PRIMARY KEY (uid),
	KEY parent (pid)
);

#
# Table structure for table tx_myextension_idcard
#
CREATE TABLE tx_myextension_idcard (
	uid int(11) NOT NULL auto_increment,
	pid int(11) DEFAULT '0' NOT NULL,
	idnumber varchar(50) DEFAULT '' NOT NULL,

	tstamp int(11) unsigned DEFAULT '0' NOT NULL,
	crdate int(11) unsigned DEFAULT '0' NOT NULL,
	cruser_id int(11) unsigned DEFAULT '0' NOT NULL,
	sorting int(11) unsigned DEFAULT '0' NOT NULL,
	deleted tinyint(4) unsigned DEFAULT '0' NOT NULL,
	hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
	starttime int(11) unsigned DEFAULT '0' NOT NULL,
	endtime int(11) unsigned DEFAULT '0' NOT NULL,

	PRIMARY KEY (uid),
	KEY parent (pid)
);

Das TCA des Feldes idcard sieht so aus:

'idcard' => Array (
    'exclude' => 1,
    'label' => 'Personalausweis',
    'config' => Array (
        'type' => 'inline',
        'foreign_table' => 'tx_myextension_idcard',
        'maxitems' => 1
    ),
),

Das Feld "idcard" in der Tabelle Person ist also für die Relation verantwortlich. In diesem Feld wird die UID der verknüpften Personalausweistabelle gespeichert.

Es ist außerdem sinnvoll, die Tabelle Personalausweis im Backend nicht editierbar zu machen, das macht man im TCA der Personalausweistabelle:

'ctrl' => array(
    [...]
    'hideTable' => '1'
),

Bidirektionale Relation

Es ist ja vielleicht gewünscht, dass man auch die Tabelle der Personalausweise editieren und mit einer Person verknüpfen kann. Das nennt man eine bidirektionale Relation, also von beiden Tabellen auf die jeweils andere. Das ist auch für die Frontendausgabe ggf. wichtig, denn momentan weiß ein Personalausweisdatensatz nicht, zu welcher Person er gehört. Dazu benötigt man ein weiteres Feld in der Personalausweistabelle:

ext_tables.sql:


CREATE TABLE tx_myextension_idcard (
[...]
	person int(11) DEFAULT '0' NOT NULL,
[...]
)

Das TCA für dieses Feld sieht so aus:

my_extension/Configuration/TCA/tx_myextension_idcard.php

'person' => Array (
    'exclude' => 1,
    'label' => 'Person',
    'config' => Array (
        'type' => 'inline',
        'foreign_table' => 'tx_myextension_person',
        'foreign_field' => 'idcard',
        'maxitems' => 1
    ),
),

Wichtig ist hier foreign_field. Das bedeutet, dass die UID des Personalausweisdatensatzes in diesem Feld gespeichert wird. Im Feld "person" des Personalausweisdatensatzes wird immer eine 1 gespeichert, heißt: Es existiert 1 Relation.

Damit funktioniert die Relation bereits. Allerdings gibt es die Unschönheit, dass beim Editieren des Persondatensatzes unter der Relation zum Personalausweis darin wiederum die Relation zur Person angezeigt wird. Das verhindern wir durch die Veränderung des Child-TCA:

'idcard' => Array (
    'exclude' => 1,
    'label' => 'Personalausweis',
    'config' => Array (
        'type' => 'inline',
        'foreign_table' => 'tx_myextension_idcard',
        'overrideChildTca' => [
            'types' => [
                '0' => [
                    'showitem' => 'idnumber',
                ],
            ],
        ],
        'maxitems' => 1
    ),
),

Jetzt haben wir im Backend das gewünschte Ergebnis. Die Tabellen Person und Personalausweis können editiert werden und darin können die Relationen angegeben werden.

Domain Model

Im Domain Model wird die Relation über den Object Storage festgelegt:

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

/**
 * idcard
 *
 * @var \Oliverweiss\MyExtension\Domain\Model\Idcard
 */
protected $idcard = null;

/**
 * Returns the idcard
 *
 * @return \Oliverweiss\MyExtension\Domain\Model\Idcard $idcard
 */
public function getIdcard()
{
    return $this->idcard;
}

/**
 * Sets the idcard
 *
 * @param \Oliverweiss\MyExtension\Domain\Model\Idcard $idcard
 * @return void
 */
public function setIdcard(\Oliverweiss\MyExtension\Domain\Model\Idcard $idcard)
{
    $this->idcard = $idcard;
}

Genau umgekehrt funktioniert es im Objekt des Personalausweises:

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

/**
 * person
 *
 * @var \Oliverweiss\MyExtension\Domain\Model\Person
 */
protected $person = null;

/**
 * Returns the person
 *
 * @return \Oliverweiss\MyExtension\Domain\Model\Person $person
 */
public function getPerson()
{
    return $this->person;
}

/**
 * Sets the person
 *
 * @param \Oliverweiss\MyExtension\Domain\Model\Person $person
 * @return void
 */
public function setPerson(\Oliverweiss\MyExtension\Domain\Model\Person $person)
{
    $this->person = $person;
}

Und so sehen die beiden Objekte im Frontend aus:

Man kann nun also bequem von beiden Richtungen auf jeweils das andere Objekt zugreifen.