Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save JulianXIMA/da7235a8c86bb247aa1248f4a268b2a8 to your computer and use it in GitHub Desktop.
Save JulianXIMA/da7235a8c86bb247aa1248f4a268b2a8 to your computer and use it in GitHub Desktop.
TYPO3 Gridelements to Container UpgradeWizard
<?php
namespace Xima\XmTemplatesNnfdav\Updates;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\Exception;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\RepeatableInterface;
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
class GridelementsMigrationUpdate implements UpgradeWizardInterface, RepeatableInterface
{
/**
* the individual mappings must be sorted like the values in the tx_gridelements_backend_layout field
* 'tx_gridelements_backend_layout' => [
* 'container_identifier' => 'Enter CType for container-Ext here, e.g. container-2cols',
* 'children_map' => [
* int value of tx_gridelements_columns => here the new colPos for container-Ext,
* ],
* 'flexform_tca_map' => [
* 'projektteaser' => 'tx_myext_projectteaser',
* 'jobteaser' => 'tx_myext_jobteaser',
* 'breite' => 'tx_myext_blockwidth',
* ],
* ],
*/
protected const MAPPING = [
'1' => [
'container_identifier' => 'container-2cols',
'container_view_column' => 0,
'children_map' => [
10 => 200,
20 => 201,
],
],
'2' => [
'container_identifier' => 'container-3cols',
'container_view_column' => 'null',
'children_map' => [
10 => 202,
20 => 203,
30 => 204,
],
],
'3' => [
'container_identifier' => 'container-2cols',
'container_view_column' => 1,
'children_map' => [
10 => 200,
20 => 201,
],
],
];
public function getIdentifier(): string
{
return 'gridelements_migration';
}
public function getTitle(): string
{
return 'Gridelements to Container Migration';
}
public function getDescription(): string
{
return 'Migrate gridelements items to container';
}
public function getPrerequisites(): array
{
return [
DatabaseUpdatedPrerequisite::class
];
}
protected static function getSetStatementsFlexformMapping($flexformMapping)
{
}
public function executeUpdate(): bool
{
$sqls = [];
foreach (self::MAPPING as $layout => $mapping) {
// update flexform
$flexSql = "select uid, pi_flexform from tt_content where pi_flexform is not null and CType='gridelements_pi1' and tx_gridelements_backend_layout='" . $layout . "' and deleted=0;";
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tt_content');
$flexformToMigrate = $connection->executeQuery($flexSql)->fetchAll();
foreach ($flexformToMigrate as $row) {
$sqlSetStatements = [];
$flexData = \TYPO3\CMS\Core\Utility\GeneralUtility::xml2array($row['pi_flexform']);
foreach ($mapping['flexform_tca_map'] ?? [] as $flexName => $tcaName) {
$flexformValue = $flexData['data']['sDEF']['lDEF'][$flexName]['vDEF'] ?? '';
if (!$flexformValue) {
continue;
}
$stringEscape = MathUtility::canBeInterpretedAsInteger($flexformValue) ? '' : '"';
$sqlSetStatements[] = $tcaName . '=' . $stringEscape . $flexformValue . $stringEscape;
}
if (count($sqlSetStatements)) {
$sqls[] = 'update tt_content set ' . implode(', ', $sqlSetStatements) . ' where uid=' . $row['uid'];
}
}
// updates for CType and colPos
$sqls[] = "update tt_content set CType='" . $mapping['container_identifier'] . "', pi_flexform=null, view_column=" . $mapping['container_view_column'] . " where CType='gridelements_pi1' and tx_gridelements_backend_layout='" . $layout . "' and deleted=0;";
foreach ($mapping['children_map'] as $oldId => $newId) {
$sqls[] = "update tt_content t1 inner join tt_content t2 on t1.tx_gridelements_container=t2.uid set t1.colPos=" . $newId . ", t1.tx_container_parent=t2.uid, t1.tx_gridelements_container=0, t1.tx_gridelements_columns=0 where t1.deleted=0 and t2.tx_gridelements_backend_layout='" . $layout . "' and t1.tx_gridelements_columns=" . $oldId . ";";
}
// remove deleted gridelements
$sqls[] = 'delete from tt_content where CType="gridelements_pi1" and tx_gridelements_backend_layout="' . $layout . '" and deleted=1;';
}
// remove all gridelements children with colPos = -1 which don't belong to any gridelements-container
$sqls[] = 'delete from tt_content where colPos = -1 and uid not in (select t1.uid from tt_content t1 inner join tt_content t2 on t1.tx_gridelements_container = t2.uid)';
/**
* Fixes negative colPos to make db compare possible. There are some gridelements container which have "hidden" child-elements.
* They were created in a 3 column container, which later on was changed to a 2 column container. Means 1 element remains but is "hidden".
* These elements remain as "unused elements" after the migration from gridelements to container on the affected pages in the TYPO3 backend.
*/
$sqls[] = 'update tt_content set colPos = 123 where colPos = -1;';
foreach ($sqls as $sql) {
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tt_content');
$connection->executeQuery($sql);
}
return true;
}
/**
* Looks for tt_content elements that have a layout that should be migrated or with colPos = -1
* @throws Exception
* @throws DBALException
*/
public function updateNecessary(): bool
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
$queryBuilder->resetRestrictions();
$conditions = [];
foreach (self::MAPPING as $layoutName => $mapping) {
$conditions[] = $queryBuilder->expr()->eq('tx_gridelements_backend_layout',
$queryBuilder->createNamedParameter($layoutName, \PDO::PARAM_STR));
}
$elements = $queryBuilder->count('uid')
->from('tt_content')
->where(
$queryBuilder->expr()->or(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('gridelements_pi1',
\PDO::PARAM_STR)),
$queryBuilder->expr()->orX(...$conditions)
),
$queryBuilder->expr()->eq('colPos', -1)
),
)
->execute()
->fetchOne();
return (bool)$elements;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment