Zwischen den beiden Models Seminar und Category soll eine m:m Relation erstellt werden. Die Relation soll im TYPO3 Backend sowie im Extbase-Kontext von beiden Seiten aus zugänglich sein (bidirektional).
Auf Datenbankebene ist in beiden Tabellen eine int-Spalte erforderlich, idealerweise benannt nach dem referenzierten Modell (also categories und seminars). Diese Spalten sind eigentlich nur für Extbase erforderlich, die eigentliche Relation wird in einer Relationstabelle tx_myext_seminar_category_mm gespeichert. Wichtig für eine Sortiermöglichkeit auch von der Gegenseite aus ist hierbei die Spalte sorting_foreign.
Im TCA ist für beide Tabellen zunächst eine gewöhnliche Konfiguration des Relationsfeldes (categories bzw. seminars) als m:m-Relation erforderlich (type=select, foreign_table, foreign_table_where und MM). Zusätzlich muss auf der Gegenseite (category-Tabelle) über MM_opposite_field die Spalte der referenzierten Tabelle definiert werden.
Im jeweiligen Extbase Model werden die Properties categories und seminars als Objectstorage vom Type des referenzierten Models definiert (abhängig vom Anwendungsfall als @lazy). Es fehlen dann nur noch die Getter und Setter (add, remove, get und set).
Datenbank
#
# Table structure for table 'tx_myext_domain_model_seminar'
#
CREATE TABLE tx_myext_domain_model_seminar (
uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
title varchar(255) DEFAULT '' NOT NULL,
categories int(11) unsigned DEFAULT '0' NOT NULL,
#...
PRIMARY KEY (uid),
KEY parent (pid),
KEY language (l10n_parent,sys_language_uid)
);
#
# Table structure for table 'tx_myext_domain_model_category'
#
CREATE TABLE tx_myext_domain_model_category (
uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
title varchar(255) DEFAULT '' NOT NULL,
seminars int(11) unsigned DEFAULT '0' NOT NULL,
#...
PRIMARY KEY (uid),
KEY parent (pid),
KEY language (l10n_parent,sys_language_uid)
);
#
# Table structure for table 'tx_myext_seminar_category_mm'
#
CREATE TABLE tx_myext_seminar_category_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)
);
TCA Konfiguration
$TCA['tx_myext_domain_model_seminar'] = array(
/* ... */
'columns' => array(
'categories' => array(
'exclude' => 1,
'label' => 'LLL:EXT:myext/Resources/Private/Language/locallang_db.xml:tx_myext_domain_model_seminar.categories',
'config' => array(
'type' => 'select',
'multiple' => 1,
'foreign_table' => 'tx_myext_domain_model_category',
'MM' => 'tx_myext_seminar_category_mm',
'foreign_table_where' => ' AND tx_myext_domain_model_category.pid=###CURRENT_PID### ORDER BY tx_myext_domain_model_category.title ',
'minitems' => 0,
'maxitems' => 99,
/* ... */
),
),
),
);
$TCA['tx_myext_domain_model_category'] = array(
/* ... */
'columns' => array(
'seminars' => array(
'exclude' => 1,
'label' => 'LLL:EXT:myext/Resources/Private/Language/locallang_db.xml:tx_myext_domain_model_category.seminars',
'config' => array(
'type' => 'select',
'multiple' => 1,
'foreign_table' => 'tx_myext_domain_model_seminar',
'MM' => 'tx_myext_seminar_category_mm',
'MM_opposite_field' => 'seminars',
'foreign_table_where' => ' AND tx_myext_domain_model_seminar.pid=###CURRENT_PID### ORDER BY tx_myext_domain_model_seminar.title ',
'minitems' => 0,
'maxitems' => 99,
/* ... */
),
),
),
);
Extbase Model
class Seminar extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
/**
* Categories
*
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Myext\Domain\Model\Category>
* @lazy
*/
protected $categories;
/**
* Adds a Category
*
* @param \Vendor\Myext\Domain\Model\Category $category
* @return void
*/
public function addCategory(\CVendor\Myext\Domain\Model\Category $category) {
$this->categories->attach($category);
}
/**
* Removes a Category
*
* @param \CVendor\Myext\Domain\Model\Category $categoryToRemove The Category to be removed
* @return void
*/
public function removeCategory(\Vendor\Myext\Domain\Model\Category $categoryToRemove) {
$this->categories->detach($categoryToRemove);
}
/**
* Returns the Categories
*
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Myext\Domain\Model\Category> $categories
*/
public function getCategories() {
return $this->categories;
}
/**
* Sets the Categories
*
* @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Myext\Domain\Model\Category> $categories
* @return void
*/
public function setCategories(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $categories) {
$this->categories = $categories;
}
}
class Category extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
/**
* Seminars
*
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Myext\Domain\Model\Seminar>
* @lazy
*/
protected $seminars;
/**
* Adds a Seminar
*
* @param \Vendor\Myext\Domain\Model\Seminar $seminar
* @return void
*/
public function addSeminar(\Vendor\Myext\Domain\Model\Seminar$seminar) {
$this->seminars->attach($seminar);
}
/**
* Removes a Seminar
*
* @param \Vendor\Myext\Domain\Model\Seminar $seminarToRemove The Seminar to be removed
* @return void
*/
public function removeSeminar(\Vendor\Myext\Domain\Model\Seminar $seminarToRemove) {
$this->seminars->detach($seminarToRemove);
}
/**
* Returns the Seminars
*
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Myext\Domain\Model\Seminar> $seminars
*/
public function getSeminars() {
return $this->seminars;
}
/**
* Sets the Seminars
*
* @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Myext\Domain\Model\Seminar> $seminars
* @return void
*/
public function setSeminars(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $seminars) {
$this->seminars = $seminars;
}
}
Kommentare
Neben dem DB-Feld "sorting_foreign" in der MM-Tabelle scheint ab Version 6.2 zwingend notwendig zu sein, in der TCA-Definition multiple=>1 zu setzen, sonst werden die Datensätze nicht gespeichert.
Das wird auch kurz in der Dokumentation erwähnt – leider sind die meisten Tutorials im Netz aber dazu veraltet und berücksichtigen diese Einstellung nicht:
https://docs.typo3.org/typo3cms/TCAReference/Reference/Columns/Select/Index.html#mm-opposite-field
Wir haben diese Erkenntnisse hier mal kurz zusammengefasst:
http://labor.99grad.de/?p=1036
https://docs.typo3.org/typo3cms/TCAReference/Reference/Columns/Select/Index.html#multiple