TYPO3 extbase & IRRE: add existing entries with 'foreign_selector'
I "kickstarted" an extension with the extbase extender that contains some 1: 1 and 1: n ratios. It automatically sets the field types to "inline" and displays a nice IRRE interface to the backend.
But by default it is not possible to select an existing entry, just create new ones.
I have found various explanations on how to achieve this with "foreign_selector", but they are all very sketchy. The function itself should work, see https://forge.typo3.org/issues/43239
Can anyone walk me through this or point me to a working example in TER? I could create a walkthrough from the example once I get it working.
PS TCA field config created with extension_builder
:
'myfield' => array(
'exclude' => 1,
'label' => 'LLL:EXT:myextension/Resources/Private/Language/locallang_db.xlf:tx_myextension_domain_model_myitem.myfield',
'config' => array(
'type' => 'inline',
'foreign_table' => 'tx_myextension_domain_model_myfield',
'foreign_field' => 'myitem',
'maxitems' => 9999,
'appearance' => array(
'collapseAll' => 0,
'levelLinksPosition' => 'top',
'showSynchronizationLink' => 1,
'showPossibleLocalizationRecords' => 1,
'showAllLocalizationLink' => 1
),
),
),
source to share
The main problem is that 1: n type IRRE relationships work like this: a child record contains the uid of its parent. So your tx_myext_domain_model_city table contains the UID of your (imaginary) tx_myext_domain_model_address.
Therefore, with the default setting, you will not be able to select a city multiple times, since it can have exactly one parent.
Therefore, you will need to use a relationship table for this field. This table should contain a uid field for address (uid_address) and city (uid_city):
CREATE TABLE tx_irreforeignselectordemo_address_city_mm (
uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL,
uid_address int(11) unsigned DEFAULT '0' NOT NULL,
uid_city int(11) unsigned DEFAULT '0' NOT NULL,
sorting int(11) unsigned DEFAULT '0' NOT NULL,
PRIMARY KEY (uid),
KEY parent (pid)
);
And these fields need to have TCA config (while the table itself can be hidden):
return array(
'ctrl' => array(
'title' => 'Relation table',
'hideTable' => TRUE,
'sortby' => 'sorting',
),
'columns' => array(
'uid_address' => Array(
'label' => 'Address',
'config' => Array(
'type' => 'select',
'foreign_table' => 'tx_irreforeignselectordemo_domain_model_address',
'size' => 1,
'minitems' => 0,
'maxitems' => 1,
),
),
'uid_city' => Array(
'label' => 'City',
'config' => Array(
'type' => 'select',
'foreign_table' => 'tx_irreforeignselectordemo_domain_model_city',
'foreign_table_where' => ' AND sys_language_uid IN (0,-1)',
'size' => 1,
'minitems' => 0,
'maxitems' => 1,
),
),
),
'types' => array(
'0' => array('showitem' => 'uid_address,uid_city')
),
'palettes' => array()
);
Then you can configure the TCA of your address to make the IRRE field:
'type' => 'inline',
'foreign_table' => 'tx_yourext_address_city_mm',
'foreign_field' => 'uid_address',
'foreign_label' => 'uid_city',
'foreign_selector' => 'uid_city',
'foreign_unique' => 'uid_city',
'foreign_sortby' => 'sorting',
Note what foreign_unique
TYPO3 tells you that a city can only be selected once.
And you need to define the attitude from the other side (from your TCA city):
'addresses' => array(
'exclude' => 1,
'label' => 'Addresses',
'config' => array(
'type' => 'inline',
'foreign_table' => 'tx_irreforeignselectordemo_address_city_mm',
'foreign_field' => 'uid_city',
'foreign_label' => 'uid_address',
),
),
Once your configuration is complete, you can use it in the backend.
Since this is a non-standard MM relationship, Extbase won't be able to handle it by default. But we can compare this with the sys_file_reference table that was introduced in TYPO3 6. So we create an Extbase model for CityRelation with the properties "address" and "city" and map this model to our mm table:
config.tx_extbase.persistence.classes {
Visol\Irreforeignselectordemo\Domain\Model\CityRelation {
mapping {
tableName = tx_irreforeignselectordemo_address_city_mm
columns {
uid_address.mapOnProperty = address
uid_city.mapOnProperty = city
}
}
}
}
Now in our address model, we define the city (or cities - you allow more than one choice) as an ObjectStorage of type CityRelation:
/**
* Cities
*
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Visol\Irreforeignselectordemo\Domain\Model\CityRelation>
*/
protected $cities = NULL;
We now have a "cities" property that contains links to all the selected cities. You can iterate over them and use them:
<f:for each="{address.cities}" as="cityRelation">
<li>{cityRelation.city.name}</li>
</f:for>
Since I couldn't find an all-in-one demo for this and was interested in the topic, I created a demo extension that does what I just described - based on Core and two extensions that deal with the topic: https://github.com/lorenzulrich/irreforeignselectordemo
Anyway, the solution is m: n (because 1: n won't work for the reasons stated above), so I decided to use "cities" instead of "cities". While this may not make sense for a city selection (as your post suggests), it may make sense for other possibilities. Feel free to replace "cities" with "city" and set maxItems in the inline configuration to one - then you have a 1: n view.
source to share