In Typo3 9.5 I have a table for products. The products can be of normal type or accessory. Both are linked bidirectionally which works fine in default language, but if I link products to the accessory the accessory is not shown on translated product pages.
If I use the other way - linking accessory to the product - it works fine.
I have debugged the problem and found out that the resulting lines in the mm table are different for both way (see below).
What I've also seen is that in \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbBackend::overlayLanguageAndWorkspace() at the beginning of the function there is only in default language a row with the accessory.
TCA excerpt tx_products_domain_model_product.php :
'products' => [
'exclude' => 1,
'label' => 'LLL:EXT:products/Resources/Private/Language/locallang_db.xlf:tx_products_domain_model_product.products',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'enableMultiSelectFilterTextfield' => true,
'foreign_table' => 'tx_products_domain_model_product',
'foreign_table_where' => ' AND tx_products_domain_model_product.isaccessory = 0 AND tx_products_domain_model_product.pid=###CURRENT_PID### AND tx_products_domain_model_product.sys_language_uid IN (-1,0)',
'MM' => 'tx_products_product_product_mm',
'MM_opposite_field' => 'accessory',
'size' => 10,
'autoSizeMax' => 30,
'maxitems' => 9999,
'multiple' => 0,
'appearance' => [
'collapseAll' => true,
'expandSingle' => true,
'levelLinksPosition' => 'bottom',
'useSortable' => true,
'showPossibleLocalizationRecords' => true,
'showRemovedLocalizationRecords' => true,
'showAllLocalizationLink' => true,
'showSynchronizationLink' => true,
'enabledControls' => [
'info' => false,
]
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
],
'displayCond' => 'FIELD:isaccessory:=:1',
],
'accessory' => [
'exclude' => 1,
'label' => 'LLL:EXT:products/Resources/Private/Language/locallang_db.xlf:tx_products_domain_model_product.accessory',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'enableMultiSelectFilterTextfield' => true,
'foreign_table' => 'tx_products_domain_model_product',
'foreign_table_where' => ' AND tx_products_domain_model_product.isaccessory = 1 AND tx_products_domain_model_product.pid=###CURRENT_PID### AND tx_products_domain_model_product.sys_language_uid IN (-1,0)',
'MM' => 'tx_products_product_product_mm',
// 'MM_opposite_field' => 'products',
'size' => 10,
'autoSizeMax' => 30,
'maxitems' => 9999,
'multiple' => 0,
'appearance' => [
'collapseAll' => true,
'expandSingle' => true,
'levelLinksPosition' => 'bottom',
'useSortable' => true,
'showPossibleLocalizationRecords' => true,
'showRemovedLocalizationRecords' => true,
'showAllLocalizationLink' => true,
'showSynchronizationLink' => true,
'enabledControls' => [
'info' => false,
]
],
'behaviour' => [
'allowLanguageSynchronization' => true,
],
],
'displayCond' => 'FIELD:isaccessory:=:0',
],
636 = product DE
643 = product EN
644 = product FR
687 = accessory DE
688 = accessory EN
689 = accessory FR
The non-default language relation fields have the option 'Value of default language' set.
Assigning the product 636 to accessory 687 :
┌────────────┬───────────────┬───────────┬───────────────────┐
│ uid_local │ uid_foreign │ sorting │ sorting_foreign │
├────────────┼───────────────┼───────────┼───────────────────┤
│ 687 │ 636 │ 1 │ 0 │
│ 688 │ 636 │ 1 │ 0 │
│ 689 │ 636 │ 1 │ 0 │
└────────────┴───────────────┴───────────┴───────────────────┘
In this case the accessory is not shown on translated product pages.
Assigning the accessory 687 to the product 636:
┌───────────┬─────────────┬─────────┬─────────────────┐
│ uid_local │ uid_foreign │ sorting │ sorting_foreign │
├───────────┼─────────────┼─────────┼─────────────────┤
│ 687 │ 636 │ 1 │ 1 │
│ 687 │ 643 │ 0 │ 3 │
│ 687 │ 647 │ 0 │ 3 │
└───────────┴─────────────┴─────────┴─────────────────┘
In this case everything works as expected.
I think the problem of the first variant is the language overlay. For FR 636 should get overlayed with 644, but this does not happen and the product get's excluded.
But the accessory is not even present in the language overlay function.
Related
I've an issue wit TYPO3 selectField, selectMultipleSideBySide.
I've configure the field like this:
$tmpColumns = [
'tx_tcademo_list' => [
'label' => 'Items',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'tx_tcademo_domain_model_item',
'MM' => 'tx_tcademo_record_item_mm',
'MM_hasUidField' => true,
'MM_match_fields' => [
'tablenames' => 'fe_users',
'fieldnames' => 'tx_tcademo_items',
],
'minitems' => 0,
'maxitems' => 99999,
],
],
];
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('fe_users', $tmpColumns);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes('fe_users', 'tx_tcademo_list');
Issue is:
The field is added to fe_users correctly, and items can be selected as expected, and they're saved to db MM relation, but when form is redrawn, the selected items are not marked as selected, and will be overwritten with next save.
Link to extension which demonstrates the issue:
TCA Demo extension
I want to make a custom content element, just with TYPO3 v9.5 core, that has custom content elements of type inline (children elements). Each of them has the fields header and image from tt_content.
So what I got already is this:
custom content element with inline child
Expanding the child element by clicking on it, I get something like this:
expanded child element with image and header field
So far, so good, but why are my childrens not in the parent element when I am looking at my page content:
I want them childrens to be in the parent container, but they are next to it.
And my other question would be, how can I get the parent element with all its children into fluid, so that I can loop over the childrens and for example output them as list items. All children have the UID of the parent.
And pls dont tell me to use extensions... I wanna learn TYPO3 and build it with TYPO3 core :) for the sake of practicing.
This is how my tt_content in TCA/overrides looks like:
Sorry for the variable names.... but Iam struggling hard. It might be a bit hard to understand:
'tt_content',
'CType',
['custom element', 'portfolio_list', 'content-text'],
'textmedia',
'after'
);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
['custom element', 'portfolio_list2', 'content-text'],
'textmedia',
'after'
);
// $GLOBALS['TCA']['tt_content']['types']['portfolio_list'] = [
// 'showitem' => '
// --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
// --palette--;;general,
// image; Image,
// header; Title,
// bodytext; Schreiben Sie einen Text,
// --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
// --palette--;;hidden,
// --palette--;;access,
// ',
// 'columnsOverrides' => [
// 'bodytext' => [
// 'config' => [
// 'enableRichtext' => true,
// 'richtextConfiguration' => 'default',
// ],
// ],
// ],
// ];
$GLOBALS['TCA']['tt_content']['types']['portfolio_list2'] = [
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
--palette--;;general,
image; Image,
header; Title,
portfolio_list2;Elemente,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
--palette--;;hidden,
--palette--;;access,
',
'columnsOverrides' => [
'bodytext' => [
'config' => [
'enableRichtext' => true,
'richtextConfiguration' => 'default',
],
],
'portfolio_list2' => [
'exclude' => true,
'label' => 'Einzelnes-Portfolio',
'config' => [
'type' => 'inline',
'allowed' => 'tt_content',
'foreign_table' => 'tt_content',
'foreign_sortby' => 'sorting',
'foreign_field' => 'portfolio_list',
'minitems' => 0,
'maxitems' => 99,
'appearance' => [
'collapseAll' => true,
'expandSingle' => true,
'levelLinksPosition' => 'bottom',
'useSortable' => true,
'showPossibleLocalizationRecords' => true,
'showRemovedLocalizationRecords' => true,
'showAllLocalizationLink' => true,
'showSynchronizationLink' => true,
'enabledControls' => [
'info' => false,
],
],
],
],
],
];
// $GLOBALS['TCA']['tt_content']['columns']['header']['config']=[
// 'type' => 'inline',
// 'foreign_table' => 'tt_content',
// 'foreign_field' => 'header',
// 'foreign_sortby' => 'sorting',
// 'maxitems' => 9999,
// 'appearance' => [
// 'collapseAll' => 0,
// 'levelLinksPosition' => 'top',
// 'showSynchronizationLink' => 1,
// 'showPossibleLocalizationRecords' => 1,
// 'useSortable' => 1,
// 'showAllLocalizationLink' => 1
// ]
// ];
// $GLOBALS['TCA']['tt_content']['columns']['bodytext']['config']=[
// 'type' => 'inline',
// 'foreign_table' => 'tt_content',
// 'foreign_field' => 'header',
// 'foreign_sortby' => 'sorting',
// 'maxitems' => 9999,
// 'appearance' => [
// 'collapseAll' => 0,
// 'levelLinksPosition' => 'top',
// 'showSynchronizationLink' => 1,
// 'showPossibleLocalizationRecords' => 1,
// 'useSortable' => 1,
// 'showAllLocalizationLink' => 1
// ]
// ];
// $GLOBALS['TCA']['tt_content']['columns']['image']['config']=[
// 'type' => 'inline',
// 'foreign_table' => 'tt_content',
// 'foreign_field' => 'image',
// 'foreign_sortby' => 'sorting',
// 'maxitems' => 9999,
// 'appearance' => [
// 'collapseAll' => 0,
// 'levelLinksPosition' => 'top',
// 'showSynchronizationLink' => 1,
// 'showPossibleLocalizationRecords' => 1,
// 'useSortable' => 1,
// 'showAllLocalizationLink' => 1
// ]
// ];
$temporaryColumn = [
'portfolio_list2' => [
'exclude' => true,
'label' => 'Einzelnes-Portfolio',
'config' => [
'type' => 'inline',
'allowed' => 'tt_content',
'foreign_table' => 'tt_content',
'foreign_sortby' => 'sorting',
'foreign_field' => 'portfolio_list',
'minitems' => 0,
'maxitems' => 99,
'appearance' => [
'collapseAll' => true,
'expandSingle' => true,
'levelLinksPosition' => 'bottom',
'useSortable' => true,
'showPossibleLocalizationRecords' => true,
'showRemovedLocalizationRecords' => true,
'showAllLocalizationLink' => true,
'showSynchronizationLink' => true,
'enabledControls' => [
'info' => false,
],
],
],
],
];
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns(
'tt_content',
$temporaryColumn
);
// $customImage = [
// 'portfolio_list' => [
// 'exclude' => true,
// 'label' => 'Einzelnes-Portfolio',
// 'config' => [
// 'type' => 'inline',
// 'foreign_table' => 'tt_content',
// 'foreign_field' => 'teaser_field',
// 'foreign_sortby' => 'sorting',
// 'maxitems' => 9999,
// 'appearance' => [
// 'collapseAll' => 0,
// 'levelLinksPosition' => 'top',
// 'showSynchronizationLink' => 1,
// 'showPossibleLocalizationRecords' => 1,
// 'useSortable' => 1,
// 'showAllLocalizationLink' => 1
// ],
// ],
// ],
// ];
// \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('tt_content', $customImage);
Do I need Domain Models and make a 1:n relation or something like that? Sorry, for stupid questions. I am 2.5 months into TYPO3 and I just start my career in coding :)
I would be so thankful for any kind of help :) THX a lot!
What you need to get the content element values in the FrontEnd, is DataProcessing. DataProcessing is basically an SQL query done by TypoScript. Here you can find a detailed Tutorial on how to build custom elements. There is a section which explains the DataProcessing with an example.
How to create custom content elements on TYPO3
Here is the official documentation
DataProcessing
As for the children that are being shown in the backend, you can hide them by creating a Hook for the Plugin preview. In the article i send you, there is a detailed explanation on how you can create the hook. it is called "The PageLayoutView Hook" Here is the official documentation
Configure Custom Backend Preview for Content Element
Once you created the class, in order to hide the children, you will have to add this:
New hook to determine if content record is used/unused
What you need to do, is either leave the children on colPos=0 and edit the function to return $record['colPos'] === 0 or you can create a new Backend Field on your layout, give it the 999 colPos number (or any number you want, it doesnt really matter) and leave the function as is. But you have to remember, when a new child is created, you have to give it automatically the colPos defined on your function, otherwise the editor will have to manually select the colPos 999 every single time. You can achive that by editing this to your needs:
$GLOBALS['TCA']['tx_news_domain_model_news']['columns']['content_elements']['config']['overrideChildTca']['columns']['colPos']['config']['default'] = '1';
Hope it helped. Any other questions, feel free to ask
Best regards
I’d like to create a relation between pages and other tables (for example news and fe_users).
For that, I create 3 fields in the tables concerned (news or fe_users).
Each field is a MM selection for pages that are attached to a given category using this config
'policies' => [
'displayCond' => 'FIELD:sys_language_uid:=:0',
'exclude' => true,
'label' => $ll . 'tx_relations.policies',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'enableMultiSelectFilterTextfield' => true,
'foreign_table' => 'pages',
'foreign_table_where' => 'AND pages.uid IN (SELECT uid_foreign FROM sys_category_record_mm WHERE sys_category_record_mm.uid_local = 1 AND sys_category_record_mm.tablenames = "pages") ORDER BY pages.title ASC, pages.uid ASC',
'MM' => 'tx_pages_references_mm',
'MM_match_fields' => [
'tablenames' => $tablename,
'fieldname' => 'policies',
],
'minitems' => 0,
'maxitems' => 9999
]
],
'working_groups' => [
'displayCond' => 'FIELD:sys_language_uid:=:0',
'exclude' => true,
'label' => $ll . 'tx_relations.working_groups',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'enableMultiSelectFilterTextfield' => true,
'foreign_table' => 'pages',
'foreign_table_where' => 'AND pages.uid IN (SELECT uid_foreign FROM sys_category_record_mm WHERE sys_category_record_mm.uid_local = 2 AND sys_category_record_mm.tablenames = "pages") ORDER BY pages.title ASC, pages.uid ASC',
'MM' => 'tx_pages_references_mm',
'MM_match_fields' => [
'tablenames' => $tablename,
'fieldname' => 'working_groups',
],
'minitems' => 0,
'maxitems' => 9999
]
],
'networks' => [
'displayCond' => 'FIELD:sys_language_uid:=:0',
'exclude' => true,
'label' => $ll . 'tx_relations.networks',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'enableMultiSelectFilterTextfield' => true,
'foreign_table' => 'pages',
'foreign_table_where' => 'AND pages.uid IN (SELECT uid_foreign FROM sys_category_record_mm WHERE sys_category_record_mm.uid_local = 3 AND sys_category_record_mm.tablenames = "pages") ORDER BY pages.title ASC, pages.uid ASC',
'MM' => 'tx_pages_references_mm',
'MM_match_fields' => [
'tablenames' => $tablename,
'fieldname' => 'networks',
],
'minitems' => 0,
'maxitems' => 9999
]
],
When saving the record,
I have the following records in the DB
uid_local uid_foreign tablenames fieldname sorting
1 129 tx_news_domain_model_news policies 1
1 130 tx_news_domain_model_news working_groups 1
1 131 tx_news_domain_model_news networks 1
Which seems correct to me...
But when I edit the record in the BE, every page is unselected...
So, INSERT seems ok, but SELECT seems down... Can you tell me what am I doing wrong ?
In a custom built TYPO3 extension, records are only available in language -1 in the backend. That was specified as such, but should now be changed so records can be translated.
Something is missing. I thought this would be in TCA, but the definitions look quite normal:
'columns' => [
'sys_language_uid' => [
'exclude' => true,
'label' => 'LLL:EXT:lang/locallang_general.xlf:LGL.language',
'config' => [
'type' => 'select',
'renderType' => 'selectSingle',
'special' => 'languages',
'items' => [
[
'LLL:EXT:lang/locallang_general.xlf:LGL.allLanguages',
-1,
'flags-multiple'
]
],
'default' => 0,
],
],
'l10n_parent' => [
'displayCond' => 'FIELD:sys_language_uid:>:0',
'exclude' => true,
'label' => 'LLL:EXT:lang/locallang_general.xlf:LGL.l18n_parent',
'config' => [
'type' => 'select',
'renderType' => 'selectSingle',
'items' => [
['', 0],
],
'foreign_table' => 'tx_myext_domain_model_item',
'foreign_table_where' => 'AND tx_myext_domain_model_item.pid=###CURRENT_PID### AND tx_myext_domain_model_item.sys_language_uid IN (-1,0)',
],
],
Also, when I change TCA, for example change config as such:
'config' => array(
'type' => 'select',
'renderType' => 'selectSingle',
'foreign_table' => 'sys_language',
'foreign_table_where' => 'ORDER BY sys_language.title',
'items' => array(
array('LLL:EXT:lang/locallang_general.xlf:LGL.allLanguages', -1),
array('LLL:EXT:lang/locallang_general.xlf:LGL.default_value', 0)
),
),
It's still the same: only language -1 (ALL) is selectable.
Of course, the sysfolder those records are on is translated in multiple languages. Also, the columns for sys_language_uid, l10n_parent and l10n_diffsource are present in the database.
Are the other languages maybe disabled somewhere? Where else could I look?
Or which element could be missing else to make records translateable?
Thanks for any hint.
I am new to ApiGility and am attempting to update my shopping cart via an API call as apposed to a route. I am using Zend Framework 2 with a code connected api.
The problem I am currently facing is that no matter what I try, I am unable to put the information to the api without validation errors.
My module.config:
Updatecart settings:
'Api\\V1\\Rest\\Updatecart\\Controller' => array(
'listener' => 'Api\\V1\\Rest\\Updatecart\\UpdatecartResource',
'route_name' => 'api.rest.updatecart',
'route_identifier_name' => 'updatecart_id',
'collection_name' => 'updatecart',
'entity_http_methods' => array(
0 => 'GET',
1 => 'PATCH',
2 => 'PUT',
3 => 'DELETE',
),
'collection_http_methods' => array(
0 => 'GET',
1 => 'POST',
2 => 'PUT',
3 => 'PATCH',
4 => 'DELETE',
),
'collection_query_whitelist' => array(
0 => 'prod_id',
1 => 'quantity',
2 => 'quantity_accumulation',
3 => 'tax',
),
'page_size' => 25,
'page_size_param' => null,
'entity_class' => 'Api\\V1\\Rest\\Updatecart\\UpdatecartEntity',
'collection_class' => 'Api\\V1\\Rest\\Updatecart\\UpdatecartCollection',
'service_name' => 'updatecart',
),
Relevant filter settings:
'Api\\V1\\Rest\\Updatecart\\Validator' => array(
0 => array(
'name' => 'prod_id',
'required' => true,
'filters' => array(
0 => array(
'name' => 'Zend\\Filter\\Int',
'options' => array(),
),
),
'validators' => array(),
'description' => 'The id of the product in the cart to be updated',
'error_message' => 'Missing product id',
'allow_empty' => false,
'continue_if_empty' => false,
),
1 => array(
'name' => 'quantity',
'required' => true,
'filters' => array(),
'validators' => array(),
'description' => 'quantity of product',
'error_message' => 'You must include a quantity',
'allow_empty' => false,
'continue_if_empty' => false,
),
2 => array(
'name' => 'tax',
'required' => true,
'filters' => array(),
'validators' => array(),
'description' => 'Add the VAT, GST, Sales Tax that will be applicable to this item. Use 0.00 for no value.',
'error_message' => 'Please add a tax value, 0.00 for no value.',
'allow_empty' => false,
'continue_if_empty' => false,
),
3 => array(
'name' => 'quantity_accumulation',
'required' => true,
'filters' => array(
0 => array(
'name' => 'Zend\\Filter\\Boolean',
'options' => array(),
),
),
'validators' => array(),
'description' => 'Either accumulate the entered quantity to the current basket quantity or set as the entered quantity.',
'allow_empty' => false,
'continue_if_empty' => false,
'error_message' => 'Quantity accumulation field error.',
),
),
When calling the PUT method:
https://cloud.mysite.dev:8890/api/updatecart/1?prod_id=1&quantity=1&update_type=1&tax=0.00
I keep getting Failed Validation errors:
{
"validation_messages": {
"prod_id": [
"Missing product id"
],
"quantity": [
"You must include a quantity"
],
"tax": [
"Please add a tax value, 0.00 for no value."
],
"quantity_accumulation": [
"Quantity accumulation field error."
]
},
"type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Failed Validation"
}
You need to supply your data as http body (payload) in json format.
Make the call to the URI without the query params.
https://cloud.mysite.dev:8890/api/updatecart/1
{
"prod_id": 1,
"quantity": 1,
"update_type": 1,
"tax": "0.00"
}
Also make sure you supply the right request headers:
Accept: application/json
Content-Type: application/json