Typisches Beispiel für eine m:n Relation sind Kategorien, die man Artikeln geben kann. Ein Artikel kann mehrere Kategorien haben und eine Kategorie kann zu mehreren Artikeln gehören. Auch hier gibt es die Möglichkeit, entweder Selectboxen zu verwenden oder IRRE-Elemente.
m:n Relation mit Selectbox
Im TYPO3 Backend ist das eine Selectbox mit mehreren Auswahlmöglichkeiten. Es gibt verschiedene Darstellungsmöglichkeiten, auch Checkboxen sind möglich. Mehr hierzu in der Dokumentation für den TCA-Type "select". Der relevante Parameter hierfür ist "renderType", den wir nicht setzen, somit ist er "selectMultipleSideBySide". Funktional ergibt sich kein Unterschied.
Die Speicherung erfolgt in der Datenbank entweder als Kommaliste der IDs oder über eine mm-Tabelle.
m:n Relation mit Kommaliste
Die Speicherung als Kommaliste ist wirklich nicht unbedingt zu empfehlen, weil es sich dabei um keine saubere Datenhaltung handelt (TYPO3 speichert Frontend- und Backendbenutzergruppen immer noch so, aber egal...). Die Kommaliste muss in ein Feld vom Typ varchar gespeichert werden.
my_extension/ext_tables.sql:
CREATE TABLE tx_myextension_domain_model_article (
categories VARCHAR(255) DEFAULT "" NOT NULL
);
my_extension/Configuration/TCA/tx_myextension_domain_model_categories.php:
'categories' => array(
'config' => array(
'type' => 'select',
'foreign_table' => 'tx_myextension_domain_model_categories',
'maxitems' => 50,
),
),
Durch den Wert "maxitems" wird aus einer single Selectbox eine multiple und damit haben wir eine m:n Relation.
my_extension/Classes/Domain/Model/Article.php
/**
* categories
*
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Oliverweiss\MyExtension\Domain\Model\Categories>
*/
protected $categories = null;
/**
* Returns the categories
*
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Oliverweiss\MyExtension\Domain\Model\Categories> $categories
*/
public function getCategories()
{
return $this->categories;
}
/**
* Sets the categories
*
* @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Oliverweiss\MyExtension\Domain\Model\Categories> $categories
* @return void
*/
public function setCategories(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $categories)
{
$this->categories = $categories;
}
Im Frontend wird von Extbase auch die Kommaliste korrekt in Objektrelationen aufgelöst:
Die Variante mit einer Kommaliste funktioniert also prinzipiell und ist einfacher, als die Variante über eine mm-Relation. Sie bietet aber z. B. keine Möglichkeit, bidirektionale Relationen zu haben. Will man also im Frontend alle Artikel ausgeben, die mit einer bestimmten Kategorie versehen wurden, klappt das nicht. Dafür braucht man eine mm-Tabelle.
m:n Relation mit mm-Tabelle
Hier ist der Aufbau komplizierter, aber die Datenhaltung einfach sauberer. Die Relationen werden in einer mm-Tabelle gespeichert.
Das Feld categories in der Artikeltabelle muss (sollte) wieder integer sein und wir legen eine Intermediate Tabelle an.
my_extension/ext_tables.sql:
CREATE TABLE tx_myextension_domain_model_articles (
categories int(11) DEFAULT '0' NOT NULL
);
CREATE TABLE tx_myextension_articles_categories_mm (
uid_local int(11) unsigned DEFAULT '0' NOT NULL,
uid_foreign int(11) unsigned DEFAULT '0' NOT NULL,
sorting int(11) unsigned DEFAULT '0' NOT NULL,
sorting_foreign int(11) unsigned DEFAULT '0' NOT NULL,
KEY uid_local (uid_local),
KEY uid_foreign (uid_foreign)
);
my_extension/Configuration/TCA/tx_myextension_domain_model_categories.php:
'categories' => array(
'config' => array(
'type' => 'select',
'foreign_table' => 'tx_yourplugin_domain_model_categories',
'MM' => 'tx_yourplugin_article_categories_mm',
'size' => 1,
'maxitems' => 50,
),
),
Die Relationen werden jetzt in der Tabelle gespeichert, die unter "MM" angegeben ist. Für jede Relation gibt es einen Datensatz in der mm-Tabelle. Das Feld "uid_local" ist die UID der Artikels, "uid_foreign" ist die UID der Kategorie. Wichtig ist hier zu wissen, dass im Feld categories der Artikel Tabelle nicht mehr die UID der Kategorie gespeichert wird, sondern die Anzahl der verknüpften Kategorien.
Das TCA der Tabelle Artikel und die Ausgabe im Frontend sind identisch wie oben beschrieben, deshalb sparen wir uns das hier.
bidirektionale Relation
Die Konfiguration einer mm-Tabelle ermöglicht uns auch, eine bidirektionale Relation zu erstellen. Im Backend wollen wir auch bei den Kategorien die entsprechenden Artikel zuweisen können:
Hierzu brauchen wir zunächst ein neues Feld in der Kategorien Tabelle:
my_extension/ext_tables.php
CREATE TABLE tx_myextension_domain_model_categories (
[...]
articles int(11) unsigned DEFAULT '0',
[...]
)
my_extension/Configuration/TCA/tx_myextension_domain_model_categories.php:
'articles' => [
'exclude' => true,
'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_myextension_domain_model_categories.articles',
'config' => [
'type' => 'select',
'maxitems' => 50,
'foreign_table' => 'tx_myextension_domain_model_article',
'MM' => 'tx_myextension_article_categories_mm',
'MM_opposite_field' => 'categories'
],
],
Auch hier wird also die gleiche mm-Tabelle angegeben. Außerdem muss Typo3 aber das Feld kennen, das auf der Gegenseite für die Relation verantwortlich ist. Nur so kann die bidirektionale Relation richtig aufgelöst werden.
my_extension/Classes/Domain/Model/Categories.php
/**
* articles
*
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Oliverweiss\MyExtension\Domain\Model\Article>
*/
protected $articles = null;
/**
* Returns the articles
*
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Oliverweiss\MyExtension\Domain\Model\Article> $articles
*/
public function getArticles()
{
return $this->articles;
}
/**
* Sets the articles
*
* @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Oliverweiss\MyExtension\Domain\Model\Article> $articles
* @return void
*/
public function setArticles(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $articles)
{
$this->articles = $articles;
}
In der Ausgabe sieht man die große Stärke einer mm-Relation: