TYPO3 8.7 randomly sorting content - typo3

We have an typo3 instance (8.7), and sometimes it happens, that over hundred of sites the content elements are resorted in an wrong way. It is definitely not done by an user of the system.
Our company has many typo3 projects and the big difference here, is our self developed extension, where you can create records and the record can contain tt_content elements.
TCA of the "field":
'content' => [
'exclude' => 0,
'label' => 'Content',
'config' => [
'type' => 'inline',
'foreign_table' => 'tt_content',
'foreign_sortby' => 'sorting',
'foreign_field' => 'event',
'foreign_record_defaults' => [
],
'maxitems' => 9999,
'appearance' => [
'collapse' => 1,
'levelLinksPosition' => 'top',
'showSynchronizationLink' => 1,
'showPossibleLocalizationRecords' => 1,
'showAllLocalizationLink' => 1,
'useSortable' => 1,
'enabledControls' => [
'dragdrop' => 1
],
],
],
],
The pid of the tt_content element is set with an hook.
The main problem is, I can´t reproduce this behaviour and it happens not very often.

How does TYPO3 organize the sorting of (content) records?
TYPO3 in general uses a field named sorting. the values are not consecutive, so not all records need an update for an insertion or remove of single records. Only on collisions the values are renumbered.
For tt_content you have one special handling: as the content is organized in columns (identified by field colPos) the sorting of each column is individual.
In this way you can have multiple chains of sorting values for each colPos.
In your definition you have no colPos value for the inserted tt_content records. Maybe that results in confusion when records are sorted and all sorting-values are rewritten without respecting the colPos field.
You can find similar chaos if you have records where the order of appearance is given by another field (e.g. title). if you define sortby = title the records are shown in this order - until TYPO3 thinks it must renumber the sorting field. this would result in Numbers overwritten titles. (**Don't use sortby =, but orderBy =)

Related

TCA type 'inline' handling in multi language environment

The following scenario: I have a page translated (connected mode, not copy/free mode!) with multiple elements that are translated from the default language.
In my elements without inline fields everything is fine!
In my elements with inline fields I am completely confused about the handling and/or the configuration!
Let’s say I have a content element which contains three inline elements (let’s call them “quotes”). If I translate these quotes 1:1 everything works as expected.
Well ... almost:
I can create new quotes in the translation, but they won’t be displayed.
I can change the sorting, which won’t be taken into account in frontend. The frontend uses the sorting of the default language.
If I create a new quote in the default language, I get the record displayed in the translation and can translate it. So this works as expected.
This leads me to my questions:
How do I make it the quotes/inline elements in the translation independent of the default language?
If this is not possible (which would be fine to me, as it contradicts the idea of the Translate/Connected-Mode somehow) how do I get rid of the buttons for Sort and Create new (of cause only in the translation, not the default language!)? Otherwise, of course, editors try this and wonder why it doesn’t work.
I hope I’ve simply forget an option, but I’ve been thinking about it and looking for a solution for hours now that I probably can’t see the forest for the trees.
This might help if it is a missing option:
TCA
'config' => [
'appearance' => [
'collapseAll' => '1',
'enabledControls' => [
'dragdrop' => '1',
],
'levelLinksPosition' => 'bottom',
'newRecordLinkTitle' => 'New quote',
'useSortable' => '1',
'showSynchronizationLink' => true,
'showAllLocalizationLink' => true,
'showPossibleLocalizationRecords' => true,
],
'foreign_field' => 'parent_id',
'foreign_sortby' => 'sorting',
'foreign_table' => 'my_quotes_table',
'foreign_table_field' => 'parent_table',
'minitems' => '1',
'type' => 'inline',
],
Typoscript
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
if.isTrue.field = my_quotes_field
table = my_quotes_table
pidInList.field = pid
where = parent_id=###uid### AND deleted=0 AND hidden=0
orderBy = sorting
markers.uid.field = uid
as = quotes
}
}
I am using TYPO3 version 11.5.17 with PHP 8.1 and MariaDB 10.5
1.How do I make it the quotes/inline elements in the translation independent of the default language?
You cannot (by any standard TYPO3 means) do this in connected mode. Your page needs to be in free-translation mode to do stuff like that.
If you want do hide the field for all the other languages you can add a "displayCond" property to your tca like this:
'displayCond' => [
'AND' => [
'FIELD:sys_language_uid:=:0'
]
],
This way the field at least stays hidden for the connected languages.

TCA field of type group doesn't show records if there are too many items

In my own extension I have extended fe_user table to add some relations for users to sysfolders (pages with doktype 254). I did it with common TCA override and adding the group type. In general that works for normal amount of records (like several tens or so) but in some cases some users can have chosen up to 3000+ relations.
Unfortunately, in such case TYPO3 BE doesn't render these relations in form properly (it's empty, although database contains a list of 3000 comma-separated UIDs and FE plugins displays these relations properly).
Of course in such case after saving the form in BE causes that the DB column is cleared.
As you can see, I tried to increase the maxitems without result. Is there some other config option that I missed, or for such huge amount I should use another type of form field? Any advice will be highly appreciated.
File typo3conf/ext/myext/Configuration/TCA/Overrides/fe_users.php
'tx_myext_points' => [
'exclude' => 0,
'label' => 'Sale points for user',
'config' => [
'type' => 'group',
'internal_type' => 'db',
'allowed' => 'pages',
'maxitems' => 999999,
'suggestOptions' => [
'default' => [
'searchWholePhrase' => 1
],
'pages' => [
'searchCondition' => 'doktype = 254'
]
]
]
],

TYPO3 - How can I sort a record by a field of a foreign_table and not by it's uid?

'passwordtype' => [
'exclude' => false,
'label' => '###label####',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'passwordtypes',
'foreign_sortby' => 'name',
],
],
That part of the tca works like a charm. The passwordtypes are getting sorted by name when I open the record of a password in the backend.
But I want the sorting of the passwordrecords also happening by the passwordtypes. So I tried 'sortby' => 'passwordtype' under 'ctrl' but that only sorts it by the uid, not by the name of the passwordtypes.
I also tried 'sortby' => 'passwordtype.name' but that creates an error. Is it possible to sort it by the name of passwordtypes instead of the uid? It clearly has the name of the passwordtypes already in the passwordlist because when I change the 'label' under 'ctrl' to 'passwordtype' than I can see the correct names and not uids.
I'm open for every idea even if I have to change my database-structure.
Let's dive into the core...
The record lists are generated via \TYPO3\CMS\Recordlist\Controller\RecordListController::main(). Fot getting the list itself, the method calls
\TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList::generateList().
generateList() does select data only from the actual table itself. No relations are resolved here. Then it becomes clearer why sorting by fields of a relation is not possible: they are not part of the selected data.
But... The labels of foreign records are shown.
Yes, that's done while rendering a concrete row of the list. At that
moment, no sorting can be applied anymore.
But... It works for sorting by UIDs.
Yes and no. You have a 1:n relation, so the UID of the foreign record
is saved as foreign_key in your (passwordrecords) table. The
'sortby' => 'passwordtype' is not applied to the table
passwordtype but to the column passwordrecords.passwordtype containing these UIDs.
Conclusion:
Out of the box, there seems not to be an option for sorting by a foreign table field.
Maybe, you can hook into the recordlist by \TYPO3\CMS\Backend\RecordList\RecordListGetTableHookInterface::getDBlistQuery() (called in \TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList::getTable) and modify the query parts to meet your needs.
Simply put the order in are foreign_table_where:
'passwordtype' => [
'exclude' => false,
'label' => '###label####',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'passwordtypes',
'foreign_table_where' => 'AND 1=1 ORDER BY name',
],
],

TYPO3 Extbase TCA MM-relation filter results by site-defined constant "newRecordStoragePid"

TLDR:
I've got two models with MM-Relations with different StoragePids defined via Constants in my template.
I don't know how to filter results while querying my data regarding my configured storagePids for my related Model.
Long version:
In my multisite-TYPO3 installation I've got two models "Person" and "PersonalInformation". These models have a MM-Relation defined via TCA.
"Person" contains all general data, stored in a global RecordStore. "PersonalInformation" contains editable Data i.e. images to be editable for each site separately. These data are stored in seperate RecordStores under each site.
That means within each site-template->Constants I've defined the extension-storagePid i.e.: $plugin.tx_myext.persistence.storagePid = 1
This config is on all sites the same, to be able to access the same RecordStore from each Site.
The RecordStore for "PersonalInformation" should be different for each site. So my setup.txt of my extension looks like:
persistence {
storagePid = {$plugin.tx_myext.persistence.storagePid},
{$plugin.tx_tx_myext.persistence.personalInformationStoragePid}
classes {
TYPO3\T3myext\Domain\Model\PersonalInformation {
newRecordStoragePid = {$plugin.tx_myext.persistence.personalInformationStoragePid}
}
}
}
And in my root-site-template under Constants I've defined plugin.tx_myext.persistence.personalInformationStoragePid for each site individually.
My TCA MM-Relation defined for PersonalInformation:
'person' => array(
'exclude' => 1,
'label' => 'LLL:EXT:myext/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_person',
'config' => array(
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'tx_myext_domain_model_person',
'foreign_table_where' => 'AND 1=1 ORDER BY last_name ASC',
'MM' => 'tx_myext_person_personalinformation_mm',
'size' => 10,
'autoSizeMax' => 30,
'maxitems' => 1,
'minitems' => 0,
'multiple' => 0,
),
),
My TCA MM-Relation defined for Person:
'personalinformation' => array(
'exclude' => 1,
'label' => 'LLL:EXT:myext/Resources/Private/Language/locallang_db.xlf:tx_myext_domain_model_person.personalinformation',
'config' => array(
'type' => 'none',
'readonly' => 1,
'foreign_table' => 'tx_myext_domain_model_personalinformation',
'MM_opposite_field' => 'personalinformation',
'MM' => 'tx_myext_person_personalinformation_mm',
'foreign_table_where' => 'AND tx_myext_domain_model_personalinformation.pid=###The-PID-defined-in-my-site-Const-for-personalInformationStoragePid###'
),
),
If I var_dump my Person in the Frontend all Person.PersonaInformation of all RecordStores are displayed. But I what to show only PersonalInformation Records of the current Site.
The field in the model will always give back all relations, independent of the storage pid. The foreign_table_where in TCA is only for the backend, so this will do nothing for the frontend.
If you want to only get relations from a certain pid, there are several solutions:
Filter it yourself, either in your template, model or controller. Just loop through the relations and check the pid. This option is easiest, but will be slow if you have a lot of relations.
Select the PersonalInformation records separately in your controller using a PersonalInformationRepository with a findByPerson function. This will respect the storagePid set in TypoScript. This will work fine if you only need the information for 1 person. If you need it for multiple persons on 1 page (in a list view for example) you can do this in a custom getPersonalInformation function in your Person model. If it's not cached it could also be slow for lists (depending on the amount of records).
Use a completely custom query using QueryBuilder (https://docs.typo3.org/typo3cms/CoreApiReference/latest/ApiOverview/Database/QueryBuilder/Index.html). This way you can do it in 1 query with joins.
What is the best solution depends on your exact situation and the number of records.

bi-directional relation loses sorting when child record is saved

In my own extension I have a bi-directional mm-relation between categories and items using an mm-table.
mm-table has 4 columns uid_local, uid_foreign, sorting, sorting_foreign.
TCA for both object tables is (only important parts)
categories table:
'items' => array(
'l10n_display' => 'defaultAsReadonly',
'l10n_mode' => 'exclude',
'config' => array(
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'tx_records_domain_model_item',
'foreign_table_where' => ' AND tx_records_domain_model_item.sys_language_uid = ###REC_FIELD_sys_language_uid###',
'MM' => 'tx_records_item_category_mm',
),
),
and items table
'categories' => array(
'exclude' => 0,
'l10n_display' => 'defaultAsReadonly',
'l10n_mode' => 'exclude',
'config' => array(
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'tx_records_domain_model_category',
'foreign_table_where' => ' AND tx_records_domain_model_category.sys_language_uid = ###REC_FIELD_sys_language_uid###',
'MM' => 'tx_records_item_category_mm',
'MM_opposite_field' => 'items',
),
),
I have 4 languages. Now for my problem.
I have a category with uid 7. The items are added and sorted in my select field. Output in front end is in the given order as set in the backend.
But now – when I update one of the items of the category that item will be placed before all other items in front end while the sorting in the back end is still as it was before the item's update. If i save the category afterwards, the front end will show the desired sorting again.
During my research I found that the mm table shows strange records. When the front end output is correct, I have one record per item in the category, and sorting is from 1 through 13.
After the item's update, I find three new records in the mm table. Three for three additional languages. These three records show my categories local_uid==7, foreign_uids are the uids of the localized item records, sorting_local is equal to 0 for all three records. I suggest this results in the wrong fornt end output although the primary language record still has the old sorting value. That's why it is shown correctly in the backend's category form.
Why do I find those 3 additional records after the item's update. The combination of the primary language's category uid with the localized uids of the items can not be correct.
Any suggestions?
I could make it work by setting l10n_mode => '' in the items table. To be honest, I do not exactly understand what happens there now, but it works.
Hint found on https://forge.typo3.org/issues/57272