diff --git a/.github/phpunit.functional.xml b/.github/phpunit.functional.xml
new file mode 100644
index 0000000..d1fa385
--- /dev/null
+++ b/.github/phpunit.functional.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ ../Classes/
+
+
+
+
+ ../Tests/Functional/
+ ../Tests/Functional/Fixtures/
+
+
+
+
+
+
+
+
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index db07d6e..55d845f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,6 +17,31 @@ jobs:
bootstrap: vendor/autoload.php
configuration: .github/phpunit.xml
+ functional-tests:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.4'
+ extensions: mbstring, intl, pdo_sqlite
+ tools: composer:v2
+
+ - name: Composer install
+ run: composer install --ignore-platform-reqs --no-interaction
+
+ - name: Prepare TYPO3 extension path
+ run: |
+ mkdir -p public/typo3conf/ext/
+ if [ ! -L public/typo3conf/ext/webcomponents ]; then ln -snvf ../../../. public/typo3conf/ext/webcomponents; fi
+
+ - name: Functional Tests
+ env:
+ typo3DatabaseDriver: pdo_sqlite
+ typo3DatabaseName: typo3
+ run: vendor/bin/phpunit -c .github/phpunit.functional.xml
+
phpstan:
runs-on: ubuntu-latest
steps:
diff --git a/Classes/DataProviding/Helpers/InlineItemsHelper.php b/Classes/DataProviding/Helpers/InlineItemsHelper.php
index c5b522f..6814534 100644
--- a/Classes/DataProviding/Helpers/InlineItemsHelper.php
+++ b/Classes/DataProviding/Helpers/InlineItemsHelper.php
@@ -8,7 +8,6 @@
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
-use TYPO3\CMS\Core\Database\Query\QueryHelper;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -25,49 +24,55 @@ public function __construct(
) {}
/**
- * @param array $parentRecord
- * @return list>
+ * @param array $parentRecord
+ * @return list>
*/
public function loadInlineItems(array $parentRecord, string $inlineFieldName, string $parentTable = 'tt_content'): array
{
// if the field is empty, and we're not in a workspace context, then there are no inline items
- /** @var int|string $versioningWorkspaceId */
- $versioningWorkspaceId = $this->context->getPropertyFromAspect('workspace', 'id');
- $versioningWorkspaceId = (int)$versioningWorkspaceId;
+ $workspaceId = $this->context->getPropertyFromAspect('workspace', 'id');
+ $versioningWorkspaceId = is_int($workspaceId) || is_string($workspaceId) ? (int)$workspaceId : 0;
if (empty($parentRecord[$inlineFieldName]) && $versioningWorkspaceId === 0) {
return [];
}
$inlineFieldTca = $this->getInlineFieldTca($inlineFieldName, $parentTable);
- $foreignTable = $inlineFieldTca['config']['foreign_table'] ?? null;
- if ($foreignTable === null) {
+ $inlineFieldConfig = $inlineFieldTca['config'] ?? [];
+ $foreignTable = $inlineFieldConfig['foreign_table'] ?? null;
+ if (!is_string($foreignTable) || $foreignTable === '') {
throw new \Exception(
sprintf('Could not load inline items for field "%s". Missing \'foreign_table\' in its TCA configuration', $inlineFieldName),
1686299043
);
}
- $foreignField = $inlineFieldTca['config']['foreign_field'] ?? null;
- $foreignTableTca = $GLOBALS['TCA'][$foreignTable];
- $foreignSortby = $inlineFieldTca['config']['foreign_sortby'] ?? $foreignTableTca['ctrl']['sortby'] ?? null;
+ $foreignField = $inlineFieldConfig['foreign_field'] ?? null;
+ if (!is_string($foreignField) || $foreignField === '') {
+ $foreignField = null;
+ }
+ $foreignTableTca = $GLOBALS['TCA'][$foreignTable] ?? [];
+ $foreignSortby = $inlineFieldConfig['foreign_sortby'] ?? $foreignTableTca['ctrl']['sortby'] ?? null;
+ if (!is_string($foreignSortby) || $foreignSortby === '') {
+ $foreignSortby = null;
+ }
$queryBuilder = $this->connectionPool->getQueryBuilderForTable($foreignTable);
$queryBuilder->getRestrictions()->removeAll();
$parentRecordId = $parentRecord['_LOCALIZED_UID'] ?? $parentRecord['uid'] ?? 0;
- $constraints = [];
+ $constraints = $this->pageRepository->getDefaultConstraints($foreignTable);
if (!empty($foreignField)) {
- $constraints[] = $queryBuilder->expr()->eq($foreignField, $queryBuilder->createNamedParameter($parentRecordId, Connection::PARAM_INT));
+ $constraints['foreign_field'] = $queryBuilder->expr()->eq($foreignField, $queryBuilder->createNamedParameter($parentRecordId, Connection::PARAM_INT));
} else {
$itemsUidList = GeneralUtility::intExplode(',', (string)($parentRecord[$inlineFieldName] ?? ''), true);
if (empty($itemsUidList)) {
return [];
}
- $constraints[] = $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($itemsUidList, ArrayParameterType::INTEGER));
+ $constraints['uidList'] = $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($itemsUidList, ArrayParameterType::INTEGER));
}
- if (isset($foreignTableTca['ctrl']['languageField'])) {
- $constraints[] = $queryBuilder->expr()->in($foreignTableTca['ctrl']['languageField'], $queryBuilder->createNamedParameter([-1, $parentRecord['sys_language_uid'] ?? 0], ArrayParameterType::INTEGER));
+ $languageField = $foreignTableTca['ctrl']['languageField'] ?? null;
+ if (is_string($languageField) && $languageField !== '') {
+ $constraints['language'] = $queryBuilder->expr()->in($languageField, $queryBuilder->createNamedParameter([-1, $parentRecord['sys_language_uid'] ?? 0], ArrayParameterType::INTEGER));
}
- $constraints[] = QueryHelper::stripLogicalOperatorPrefix($this->pageRepository->enableFields($foreignTable));
- $queryBuilder->select('*')->from($foreignTable)->where(...$constraints);
+ $queryBuilder->select('*')->from($foreignTable)->where(...array_values($constraints));
if ($foreignSortby) {
$queryBuilder->orderBy($foreignSortby);
}
@@ -96,7 +101,7 @@ public function loadInlineItems(array $parentRecord, string $inlineFieldName, st
}
/**
- * @return array{config: array{foreign_table?: string, foreign_field?: string, foreign_sortby?: string}, label: string, type: string}
+ * @return array{config?: array}
*/
protected function getInlineFieldTca(string $inlineFieldName, string $localTableName = 'tt_content'): array
{
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItems.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItems.csv
new file mode 100644
index 0000000..e86d597
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItems.csv
@@ -0,0 +1,8 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","sys_language_uid","input_1","sorting"
+,"1","1","1","tx_inlineitemshelperfixture_parent","-1","BJB","2"
+,"2","1","1","tx_inlineitemshelperfixture_parent","-1","BJB","1"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspace.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspace.csv
new file mode 100644
index 0000000..919e747
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspace.csv
@@ -0,0 +1,8 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","sys_language_uid","input_1","t3ver_wsid","t3ver_state","sorting"
+,"1","1","1","tx_inlineitemshelperfixture_parent","-1","BJB","1","1","2"
+,"2","1","1","tx_inlineitemshelperfixture_parent","-1","BJB","1","1","1"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemAtTheBeginning.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemAtTheBeginning.csv
new file mode 100644
index 0000000..bc47cd6
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemAtTheBeginning.csv
@@ -0,0 +1,9 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","input_1","t3ver_wsid","t3ver_state","sorting"
+,"1","1","1","tx_inlineitemshelperfixture_parent","BJB","0","0","2"
+,"2","1","1","tx_inlineitemshelperfixture_parent","BJB","0","0","3"
+,"3","1","1","tx_inlineitemshelperfixture_parent","BJB","1","1","1"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemAtTheEnd.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemAtTheEnd.csv
new file mode 100644
index 0000000..f6affcb
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemAtTheEnd.csv
@@ -0,0 +1,9 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","input_1","t3ver_wsid","t3ver_state","sorting"
+,"1","1","1","1","-1","BJB","0","0","2"
+,"2","1","1","1","-1","BJB","0","0","3"
+,"3","1","1","1","-1","BJB","1","1","4"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemInTheMiddle.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemInTheMiddle.csv
new file mode 100644
index 0000000..dda6ee5
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemInTheMiddle.csv
@@ -0,0 +1,9 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","input_1","t3ver_wsid","t3ver_state","sorting"
+,"1","1","1","tx_inlineitemshelperfixture_parent","BJB","0","0","2"
+,"2","1","1","tx_inlineitemshelperfixture_parent","BJB","0","0","5"
+,"3","1","1","tx_inlineitemshelperfixture_parent","BJB","1","1","4"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithRemovedItem.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithRemovedItem.csv
new file mode 100644
index 0000000..64c6aa0
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsInWorkspaceWithRemovedItem.csv
@@ -0,0 +1,9 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","input_1","t3ver_wsid","t3ver_state","t3ver_oid","sorting"
+,"1","1","1","tx_inlineitemshelperfixture_parent","BJB","0","0","0","3"
+,"2","1","1","tx_inlineitemshelperfixture_parent","BJB","0","0","0","2"
+,"3","1","1","tx_inlineitemshelperfixture_parent","BJB","1","2","1","3"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsRearrangedInWorkspace.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsRearrangedInWorkspace.csv
new file mode 100644
index 0000000..26479bc
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/MultipleInlineItemsRearrangedInWorkspace.csv
@@ -0,0 +1,10 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","sys_language_uid","input_1","sorting","t3ver_oid","t3ver_wsid"
+,"1","1","1","1","-1","BJB/first","1","0","0"
+,"2","1","1","1","-1","BJB/second","2","0","0"
+,"3","1","1","1","-1","BJB/first","2","1","1"
+,"4","1","1","1","-1","BJB/second","1","2","1"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItem.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItem.csv
new file mode 100644
index 0000000..46db3b3
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItem.csv
@@ -0,0 +1,7 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","sys_language_uid","input_1"
+,"1","1","1","tx_inlineitemshelperfixture_parent","-1","BJB"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemChangedInWorkspace.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemChangedInWorkspace.csv
new file mode 100644
index 0000000..f21a2ed
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemChangedInWorkspace.csv
@@ -0,0 +1,8 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","sys_language_uid","input_1","t3ver_wsid","t3ver_state","t3ver_oid"
+,"1","1","1","tx_inlineitemshelperfixture_parent","-1","BJB","0","0","0"
+,"2","1","1","tx_inlineitemshelperfixture_parent","-1","BJB/changed","1","0","1"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemEnabledInWorkspace.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemEnabledInWorkspace.csv
new file mode 100644
index 0000000..b0fc83e
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemEnabledInWorkspace.csv
@@ -0,0 +1,8 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","sys_language_uid","hidden","t3ver_wsid","t3ver_state","t3ver_oid"
+,"1","1","1","1","-1","1","0","0","0"
+,"2","1","1","1","-1","0","1","0","1"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemInWorkspace.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemInWorkspace.csv
new file mode 100644
index 0000000..97c3457
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemInWorkspace.csv
@@ -0,0 +1,7 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","sys_language_uid","input_1","t3ver_wsid","t3ver_state"
+,"1","1","1","1","-1","BJB","1","1"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemWithoutDefaultTranslation.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemWithoutDefaultTranslation.csv
new file mode 100644
index 0000000..9680443
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/OneInlineItemWithoutDefaultTranslation.csv
@@ -0,0 +1,7 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","sys_language_uid","input_1"
+,"1","1","2","tx_inlineitemshelperfixture_parent","1","BJB"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/Fixtures/TranslatedInlineItem.csv b/Tests/Functional/DataProviding/Helpers/Fixtures/TranslatedInlineItem.csv
new file mode 100644
index 0000000..394a7da
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/Fixtures/TranslatedInlineItem.csv
@@ -0,0 +1,8 @@
+"tx_inlineitemshelperfixture_child"
+,"uid","pid","parentid","parenttable","sys_language_uid","input_1"
+,"1","1","1","tx_inlineitemshelperfixture_parent","0","BJB"
+,"2","1","2","tx_inlineitemshelperfixture_parent","1","BJB/overlaid"
+
+"tx_inlineitemshelperfixture_parent"
+,"uid"
+,"1"
diff --git a/Tests/Functional/DataProviding/Helpers/InlineItemsHelperTest.php b/Tests/Functional/DataProviding/Helpers/InlineItemsHelperTest.php
new file mode 100644
index 0000000..68e0e7f
--- /dev/null
+++ b/Tests/Functional/DataProviding/Helpers/InlineItemsHelperTest.php
@@ -0,0 +1,276 @@
+setWorkspace(0);
+ $this->subject = new InlineItemsHelper(
+ $this->get(ConnectionPool::class),
+ $this->get(Context::class),
+ $this->get(PageRepository::class),
+ );
+ }
+
+ #[Test]
+ public function returnsEmptyArrayForNoData(): void
+ {
+ $items = $this->subject->loadInlineItems(['pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertSame([], $items);
+ }
+
+ #[Test]
+ public function returnsEmptyArrayForNoDataInWorkspace(): void
+ {
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertSame([], $items);
+ }
+
+ #[Test]
+ public function returnsOneItemForOneRecordInDatabase(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/OneInlineItem.csv');
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1, self::INLINE_FIELD => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(1, $items);
+ self::assertIsArray($items[0]);
+ }
+
+ #[Test]
+ public function returnsRecordFromDatabase(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/OneInlineItem.csv');
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1, self::INLINE_FIELD => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ $connection = $this->getConnectionPool()->getConnectionForTable(self::CHILD_TABLE);
+ $rows = $connection->executeQuery(
+ 'SELECT * FROM ' . self::CHILD_TABLE . ' WHERE parentid = :parentid',
+ ['parentid' => 1]
+ )->fetchAllAssociative();
+
+ self::assertIsArray($items);
+ self::assertCount(1, $items);
+ self::assertSame($rows, $items);
+ }
+
+ #[Test]
+ public function returnsSortedItems(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/MultipleInlineItems.csv');
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1, self::INLINE_FIELD => 2], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(2, $items);
+ self::assertSame(2, $items[0]['uid']);
+ self::assertSame(1, $items[1]['uid']);
+ }
+
+ #[Test]
+ public function returnsOneItemWithOneRecordCreatedInWorkspace(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/OneInlineItemInWorkspace.csv');
+
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(1, $items);
+ self::assertIsArray($items[0]);
+ }
+
+ #[Test]
+ public function returnSortedItemsInWorkspace(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/MultipleInlineItemsInWorkspace.csv');
+
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(2, $items);
+ self::assertSame(2, $items[0]['uid']);
+ self::assertSame(1, $items[1]['uid']);
+ }
+
+ #[Test]
+ public function returnSortedItemsWithCreatedOneAtTheBeginningInWorkspace(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemAtTheBeginning.csv');
+
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(3, $items);
+ self::assertSame(3, $items[0]['uid']);
+ self::assertSame(1, $items[1]['uid']);
+ self::assertSame(2, $items[2]['uid']);
+ }
+
+ #[Test]
+ public function returnSortedItemsWithCreatedOneInTheMiddleInWorkspace(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemInTheMiddle.csv');
+
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(3, $items);
+ self::assertSame(1, $items[0]['uid']);
+ self::assertSame(3, $items[1]['uid']);
+ self::assertSame(2, $items[2]['uid']);
+ }
+
+ #[Test]
+ public function returnSortedItemsWithCreatedOneAtTheEndInWorkspace(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/MultipleInlineItemsInWorkspaceWithNewItemAtTheEnd.csv');
+
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(3, $items);
+ self::assertSame(1, $items[0]['uid']);
+ self::assertSame(2, $items[1]['uid']);
+ self::assertSame(3, $items[2]['uid']);
+ }
+
+ #[Test]
+ public function returnSortedItemsWithRemovedItemInWorkspace(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/MultipleInlineItemsInWorkspaceWithRemovedItem.csv');
+
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(1, $items);
+ self::assertSame(2, $items[0]['uid']);
+ }
+
+ #[Test]
+ public function returnsItemChangedInWorkspace(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/OneInlineItemChangedInWorkspace.csv');
+
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(1, $items);
+ self::assertSame('BJB/changed', $items[0]['input_1']);
+ }
+
+ #[Test]
+ public function returnsSortedItemsWithSortingChangedInWorkspace(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/MultipleInlineItemsRearrangedInWorkspace.csv');
+
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertIsArray($items);
+ self::assertCount(2, $items);
+ self::assertSame('BJB/second', $items[0]['input_1']);
+ self::assertSame('BJB/first', $items[1]['input_1']);
+ }
+
+ #[Test]
+ public function returnsItemEnabledInWorkspace(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/OneInlineItemEnabledInWorkspace.csv');
+
+ $this->setWorkspace(1);
+
+ $items = $this->subject->loadInlineItems(['uid' => 1, 'pid' => 1], self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertCount(1, $items);
+ }
+
+ #[Test]
+ public function returnsOverlaidItemWhenParentRecordIsTranslated(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/TranslatedInlineItem.csv');
+ $record = [
+ 'uid' => 1,
+ 'pid' => 1,
+ 'sys_language_uid' => 1,
+ self::INLINE_FIELD => 1,
+ '_LOCALIZED_UID' => 2,
+ ];
+
+ $items = $this->subject->loadInlineItems($record, self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertCount(1, $items);
+ self::assertSame('BJB/overlaid', $items[0]['input_1']);
+ }
+
+ #[Test]
+ public function returnsItemWithoutDefaultTranslationWhenContentElementIsTranslated(): void
+ {
+ $this->importCSVDataSet(__DIR__ . '/Fixtures/OneInlineItemWithoutDefaultTranslation.csv');
+ $record = [
+ 'uid' => 1,
+ 'pid' => 1,
+ 'sys_language_uid' => 1,
+ self::INLINE_FIELD => 1,
+ '_LOCALIZED_UID' => 2,
+ ];
+
+ $items = $this->subject->loadInlineItems($record, self::INLINE_FIELD, self::PARENT_TABLE);
+
+ self::assertCount(1, $items);
+ self::assertSame('BJB', $items[0]['input_1']);
+ }
+
+ private function setWorkspace(int $workspaceId): void
+ {
+ $context = GeneralUtility::makeInstance(Context::class);
+ $context->setAspect('workspace', new WorkspaceAspect($workspaceId));
+ }
+}
diff --git a/Tests/Functional/Fixtures/Extensions/inline_items_helper_fixture/Configuration/TCA/tx_inlineitemshelperfixture_child.php b/Tests/Functional/Fixtures/Extensions/inline_items_helper_fixture/Configuration/TCA/tx_inlineitemshelperfixture_child.php
new file mode 100644
index 0000000..0d0b9b1
--- /dev/null
+++ b/Tests/Functional/Fixtures/Extensions/inline_items_helper_fixture/Configuration/TCA/tx_inlineitemshelperfixture_child.php
@@ -0,0 +1,46 @@
+ [
+ 'title' => 'InlineItemsHelper fixture child',
+ 'label' => 'input_1',
+ 'tstamp' => 'tstamp',
+ 'crdate' => 'crdate',
+ 'delete' => 'deleted',
+ 'sortby' => 'sorting',
+ 'versioningWS' => true,
+ 'languageField' => 'sys_language_uid',
+ 'transOrigPointerField' => 'l10n_parent',
+ 'transOrigDiffSourceField' => 'l10n_diffsource',
+ 'translationSource' => 'l10n_source',
+ 'enablecolumns' => [
+ 'disabled' => 'hidden',
+ ],
+ 'security' => [
+ 'ignorePageTypeRestriction' => true,
+ ],
+ ],
+ 'columns' => [
+ 'parentid' => [
+ 'config' => [
+ 'type' => 'passthrough',
+ ],
+ ],
+ 'parenttable' => [
+ 'config' => [
+ 'type' => 'passthrough',
+ ],
+ ],
+ 'input_1' => [
+ 'label' => 'Input',
+ 'config' => [
+ 'type' => 'input',
+ ],
+ ],
+ ],
+ 'types' => [
+ '0' => [
+ 'showitem' => 'input_1',
+ ],
+ ],
+];
diff --git a/Tests/Functional/Fixtures/Extensions/inline_items_helper_fixture/Configuration/TCA/tx_inlineitemshelperfixture_parent.php b/Tests/Functional/Fixtures/Extensions/inline_items_helper_fixture/Configuration/TCA/tx_inlineitemshelperfixture_parent.php
new file mode 100644
index 0000000..7f4da08
--- /dev/null
+++ b/Tests/Functional/Fixtures/Extensions/inline_items_helper_fixture/Configuration/TCA/tx_inlineitemshelperfixture_parent.php
@@ -0,0 +1,39 @@
+ [
+ 'title' => 'InlineItemsHelper fixture parent',
+ 'label' => 'uid',
+ 'tstamp' => 'tstamp',
+ 'crdate' => 'crdate',
+ 'delete' => 'deleted',
+ 'sortby' => 'sorting',
+ 'versioningWS' => true,
+ 'languageField' => 'sys_language_uid',
+ 'transOrigPointerField' => 'l10n_parent',
+ 'transOrigDiffSourceField' => 'l10n_diffsource',
+ 'translationSource' => 'l10n_source',
+ 'enablecolumns' => [
+ 'disabled' => 'hidden',
+ ],
+ 'security' => [
+ 'ignorePageTypeRestriction' => true,
+ ],
+ ],
+ 'columns' => [
+ 'inline_2' => [
+ 'label' => 'Inline items',
+ 'config' => [
+ 'type' => 'inline',
+ 'foreign_table' => 'tx_inlineitemshelperfixture_child',
+ 'foreign_field' => 'parentid',
+ 'foreign_table_field' => 'parenttable',
+ ],
+ ],
+ ],
+ 'types' => [
+ '0' => [
+ 'showitem' => 'inline_2',
+ ],
+ ],
+];
diff --git a/Tests/Functional/Fixtures/Extensions/inline_items_helper_fixture/composer.json b/Tests/Functional/Fixtures/Extensions/inline_items_helper_fixture/composer.json
new file mode 100644
index 0000000..98e1271
--- /dev/null
+++ b/Tests/Functional/Fixtures/Extensions/inline_items_helper_fixture/composer.json
@@ -0,0 +1,11 @@
+{
+ "name": "julius-baer/inline-items-helper-fixture",
+ "type": "typo3-cms-extension",
+ "description": "Functional test fixture extension for InlineItemsHelper",
+ "version": "1.0.0",
+ "extra": {
+ "typo3/cms": {
+ "extension-key": "inline_items_helper_fixture"
+ }
+ }
+}
diff --git a/Tests/Unit/DataProviding/Helpers/InlineItemsHelperTest.php b/Tests/Unit/DataProviding/Helpers/InlineItemsHelperTest.php
new file mode 100644
index 0000000..31c4d5e
--- /dev/null
+++ b/Tests/Unit/DataProviding/Helpers/InlineItemsHelperTest.php
@@ -0,0 +1,52 @@
+subject = new InlineItemsHelper(
+ self::createStub(ConnectionPool::class),
+ self::createStub(Context::class),
+ self::createStub(PageRepository::class),
+ );
+ }
+
+ #[Test]
+ public function throwsExceptionIfTcaIsNotLoadable(): void
+ {
+ self::expectExceptionCode(1587038305);
+
+ $this->subject->loadInlineItems(['my_field' => 1], 'my_field', 'my_table');
+ }
+
+ #[Test]
+ public function throwsExceptionIfFieldIsNotInline(): void
+ {
+ $GLOBALS['TCA'] = ArrayUtility::setValueByPath($GLOBALS['TCA'] ?? [], 'my_table/columns/my_field/config/type', 'text');
+
+ self::expectExceptionCode(1686299043);
+
+ $this->subject->loadInlineItems(['my_field' => 1], 'my_field', 'my_table');
+
+ $GLOBALS['TCA'] = ArrayUtility::removeByPath($GLOBALS['TCA'], 'my_table/columns/my_field/config/type');
+ }
+}