Die Extension migration ist ein kleines Schweizer Taschenmesser im TYPO3-Bereich. Neben der Möglichkeit ganze Seitenbäume auf der CLI zu ex- und wieder importieren, bietet die Extension auch die Möglichkeit, Migrationen innerhalb der bestehenden Datenbank durchzuführen. Die Dokumentation auf GitHub bietet hier ein paar hilfreiche Tipps.
In diesem Blogbeitrag geht es jedoch nur um die News-Migration. Wir haben in der Vergangenheit bereits eine Hand voll älterer Projekte von tt_news auf tx_news umgestellt und ich habe euch die benutzten Klassen einmal hier bereitgestellt. Falls ihr diese benötigt, könnt ihr euch direkt bedienen.
Hinweis: Über die Zeit hinweg gab es verschiedene Varianten von tt_news und natürlich auch von tx_news. Eventuell müsst ihr also an der einen oder anderen Stelle doch noch Hand anlegen. Wir hoffen jedoch, dass euch der Code dennoch weiter hilft - seht es vielleicht als eine Art Kickstarter.
1. Die Hauptkonfiguration
Datei EXT:migration_extend/Configuration/Migration.php:
<?php
return [
// Default values if not given from CLI
'configuration' => [
'key' => '',
'dryrun' => true,
'limitToRecord' => null,
'limitToPage' => 583,
'recursive' => true
],
// Define your migrations
'migrations' => [
[
'className' => \In2code\MigrationExtend\Migration\Importer\NewsCategoriesImporter::class,
'keys' => [
'news',
'categories',
'categoriesimport'
]
],
[
'className' => \In2code\MigrationExtend\Migration\Migrator\CategoriesMigrator::class,
'keys' => [
'news',
'categories',
'categoriesmigration'
]
],
[
'className' => \In2code\MigrationExtend\Migration\Importer\NewsImporter::class,
'keys' => [
'news'
]
],
[
'className' => \In2code\MigrationExtend\Migration\Migrator\NewsMigrator::class,
'keys' => [
'news'
]
],
[
'className' => \In2code\MigrationExtend\Migration\Migrator\ContentMigrator::class,
'keys' => [
'content'
]
]
]
];
Datei EXT:migration_extend/ext_tables.sql
CREATE TABLE tx_news_domain_model_news (
_migrated tinyint(4) unsigned DEFAULT '0' NOT NULL,
_migrated_uid int(11) unsigned DEFAULT '0' NOT NULL,
_migrated_table varchar(255) DEFAULT '' NOT NULL,
_migrated_twice tinyint(4) unsigned DEFAULT '0' NOT NULL
);
CREATE TABLE sys_category (
_migrated tinyint(4) unsigned DEFAULT '0' NOT NULL,
_migrated_uid int(11) DEFAULT '0' NOT NULL,
_migrated_table varchar(255) DEFAULT '' NOT NULL,
_migrated_twice tinyint(4) unsigned DEFAULT '0' NOT NULL
);
Datei EXT:migration_extend/ext_localconf.php:
<?php
if (!defined('TYPO3_MODE')) {
die('Access denied.');
}
call_user_func(
function () {
/**
* Fluid Namespace
*/
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['migrationextend'][]
= 'In2code\MigrationExtend\ViewHelpers';
}
);
2. Die Importer
Datei EXT:migration_extend/Classes/Migration/Importer/NewsImporter.php (Basis Import der Newsdatensätze):
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\Importer;
use In2code\Migration\Migration\Importer\AbstractImporter;
use In2code\Migration\Migration\Importer\ImporterInterface;
/**
* Class NewsImporter
*/
class NewsImporter extends AbstractImporter implements ImporterInterface
{
/**
* @var string
*/
protected $tableName = 'tx_news_domain_model_news';
/**
* @var string
*/
protected $tableNameOld = 'tt_news';
/**
* @var bool
*/
protected $truncate = false;
/**
* @var bool
*/
protected $keepIdentifiers = false;
/**
* @var array
*/
protected $mapping = [
'type' => 'type',
'title' => 'title',
'short' => 'teaser',
'bodytext' => 'bodytext',
'datetime' => 'datetime',
'author' => 'author',
'author_email' => 'author_email',
'keyword' => 'keywords',
'archivedate' => 'archive',
'editlock' => 'editlock',
'keywords' => 'keywords',
'page' => 'internalurl',
'ext_url' => 'externalurl',
'hidden' => 'hidden',
'fe_group' => 'fe_group',
'sys_language_uid' => 'sys_language_uid',
'l18n_parent' => 'l10n_parent',
'l18n_diffsource' => 'l10n_diffsource',
'category' => 'categories',
];
}
Datei EXT:migration_extend/Classes/Migration/Importer/NewsCategoriesImporter.php (Basis Import der News Kategorien):
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\Importer;
use In2code\Migration\Migration\Importer\AbstractImporter;
use In2code\Migration\Migration\Importer\ImporterInterface;
use In2code\MigrationExtend\Migration\PropertyHelpers\CreateSortingNumberFromPropertyPropertyHelper;
/**
* Class NewsCategoriesImporter
*/
class NewsCategoriesImporter extends AbstractImporter implements ImporterInterface
{
/**
* Table name where to migrate to
*
* @var string
*/
protected $tableName = 'sys_category';
/**
* Table name from migrate to
*
* @var string
*/
protected $tableNameOld = 'tt_news_cat';
/**
* @var bool
*/
protected $truncate = false;
/**
* @var bool
*/
protected $keepIdentifiers = false;
/**
* @var array
*/
protected $mapping = [
'pid' => 'pid',
'title' => 'title',
'parent_category' => 'parent',
'fe_group' => 'fe_group',
'sorting' => 'sorting'
];
/**
* PropertyHelpers are called after initial build via mapping
*
* "newProperty" => [
* [
* "className" => class1::class,
* "configuration => ["red"]
* ],
* [
* "className" => class2::class
* ]
* ]
*
* @var array
*/
protected $propertyHelpers = [
'sorting' => [
[
'className' => CreateSortingNumberFromPropertyPropertyHelper::class,
'configuration' => [
'property' => 'title'
]
]
]
];
}
3. Die Migratoren
Datei EXT:migration_extend/Classes/Migration/Migrator/ContentMigrator.php (migriert News Plugins):
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\Migrator;
use In2code\Migration\Migration\Migrator\AbstractMigrator;
use In2code\Migration\Migration\Migrator\MigratorInterface;
use In2code\Migration\Migration\PropertyHelpers\FlexFormGeneratorPropertyHelper;
/**
* Class ContentMigrator
*/
class ContentMigrator extends AbstractMigrator implements MigratorInterface
{
/**
* @var string
*/
protected $tableName = 'tt_content';
/**
* @var array
*/
protected $propertyHelpers = [
'pi_flexform' => [
[
// Build FlexForm for News plugin
'className' => FlexFormGeneratorPropertyHelper::class,
'configuration' => [
'condition' => [
'CType' => 'list',
'list_type' => '9' // Tt_news plugin
],
'flexFormTemplate' => 'EXT:migration_extend/Resources/Private/FlexForms/News.xml',
'flexFormField' => 'pi_flexform',
'overwriteValues' => [
'list_type' => 'news_pi1'
],
'additionalMapping' => [
[
// create new variable {additionalMapping.switchableControllerActions}
'variableName' => 'switchableControllerActions',
'keyField' => 'flexForm:what_to_display', // "flexForm:path/path" or: "row:uid"
'mapping' => [
'LIST' => 'News->list;News->detail',
'LIST2' => 'News->list;News->detail',
'LIST3' => 'News->list;News->detail',
'HEADER_LIST' => 'News->list;News->detail',
'LATEST' => 'News->list;News->detail',
'SINGLE' => 'News->detail',
'SINGLE2' => 'News->detail',
'AMENU' => 'News->list;News->detail',
'SEARCH' => 'News->list;News->detail',
'CATMENU' => 'News->list;News->detail',
'VERSION_PREVIEW' => 'News->list;News->detail',
'EVENT_FUTURE' => 'News->list;News->detail',
'EVENT_PAST' => 'News->list;News->detail',
'LATEST_EVENT_PAST' => 'News->list;News->detail',
'LATEST_EVENT_FUTURE' => 'News->list;News->detail',
'EVENT_CURRENT' => 'News->list;News->detail',
'LATEST_EVENT_CURRENT' => 'News->list;News->detail',
'EVENT_REGISTERABLE' => 'News->list;News->detail',
'LATEST_EVENT_REGISTERABLE' => 'News->list;News->detail'
]
],
[
// create new variable {additionalMapping.categorySetting}
'variableName' => 'categorySetting',
'keyField' => 'flexForm:categoryMode', // "flexForm:path/path" or: "row:uid"
'mapping' => [
'0' => '', // show all
'1' => 'or', // show from categories (OR)
'2' => 'and', // show from categories (AND)
'-1' => 'notand', // don't show from categories (AND)
'-2' => 'notor', // don't show from categories (OR)
]
],
[
// create new variable {additionalMapping.archiveSetting}
'variableName' => 'archiveSetting',
'keyField' => 'flexForm:archive', // "flexForm:path/path" or: "row:uid"
'mapping' => [
'0' => '', // don't care
'1' => 'archived', // archived only
'-1' => 'active', // not archived only
]
]
]
]
]
],
];
}
Datei EXT:migration_extend/Classes/Migration/Migrator/NewsMigrator.php (Erweiterte Migration der News Datensätze mit Relationen):
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\Migrator;
use In2code\Migration\Migration\Migrator\AbstractMigrator;
use In2code\Migration\Migration\Migrator\MigratorInterface;
use In2code\Migration\Migration\PropertyHelpers\SlugPropertyHelper;
use In2code\MigrationExtend\Migration\PropertyHelpers\CreateNewsCategoryRelationPropertyHelper;
use In2code\MigrationExtend\Migration\PropertyHelpers\CreateNewsFileRelationsPropertyHelper;
use In2code\MigrationExtend\Migration\PropertyHelpers\CreateNewsImageRelationAndMoveImagePropertyHelper;
use In2code\MigrationExtend\Migration\PropertyHelpers\CreateNewsRelatedRelationsPropertyHelper;
/**
* Class NewsMigrator
* To update previous imported news records with relations
*/
class NewsMigrator extends AbstractMigrator implements MigratorInterface
{
/**
* @var string
*/
protected $tableName = 'tx_news_domain_model_news';
/**
* @var bool
*/
protected $enforce = true;
/**
* Filter selection of old records like "and pid > 0" (to prevent elements in a workflow e.g.)
*
* @var string
*/
protected $additionalWhere = 'and _migrated=1 and _migrated_table="tt_news" and _migrated_twice=0';
/**
* @var array
*/
protected $values = [
'_migrated_twice' => 1 // Don't migrate a second time (for other branches that should also be migrated)
];
/**
* @var array
*/
protected $sql = [
'end' => [
'update sys_file_reference r left join tx_news_domain_model_news n on r.uid_foreign = n.uid
set r.pid = n.pid where r.tablenames LIKE "tx_news_domain_model_news" and n.deleted=0'
]
];
/**
* @var array
*/
protected $propertyHelpers = [
'categories' => [
[
'className' => CreateNewsCategoryRelationPropertyHelper::class
]
],
'fal_media' => [
[
'className' => CreateNewsImageRelationAndMoveImagePropertyHelper::class
]
],
'fal_related_files' => [
[
'className' => CreateNewsFileRelationsPropertyHelper::class
]
],
'related' => [
[
'className' => CreateNewsRelatedRelationsPropertyHelper::class
]
],
'path_segment' => [
[
'className' => SlugPropertyHelper::class,
'configuration' => [
'conditions' => [
'deleted' => [
'0'
]
]
]
]
]
];
}
Datei EXT:migration_extend/Classes/Migration/Migrator/CategoriesMigrator.php (Erweiterte Migration der Kategorien):
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\Migrator;
use In2code\Migration\Migration\Migrator\AbstractMigrator;
use In2code\Migration\Migration\Migrator\MigratorInterface;
use In2code\MigrationExtend\Migration\PropertyHelpers\GetParentCategoryPropertyHelper;
/**
* Class CategoriesMigrator
*/
class CategoriesMigrator extends AbstractMigrator implements MigratorInterface
{
/**
* @var string
*/
protected $tableName = 'sys_category';
/**
* @var bool
*/
protected $enforce = true;
/**
* Filter selection of old records like "and pid > 0" (to prevent elements in a workflow e.g.)
*
* @var string
*/
protected $additionalWhere = 'and _migrated=1 and _migrated_table="tt_news_cat" and _migrated_twice=0';
/**
* @var array
*/
protected $values = [
'_migrated_twice' => 1 // Don't migrate a second time (for other branches that should also be migrated)
];
/**
* @var array
*/
protected $propertyHelpers = [
'parent' => [
[
'className' => GetParentCategoryPropertyHelper::class
]
]
];
}
4. PropertyHelpers
Datei EXT:migration_extend/Classes/Migration/PropertyHelpers/CreateSortingNumberFromPropertyPropertyHelper.php:
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\PropertyHelpers;
use Doctrine\DBAL\DBALException;
use In2code\Migration\Migration\PropertyHelpers\AbstractPropertyHelper;
use In2code\Migration\Migration\PropertyHelpers\PropertyHelperInterface;
use In2code\Migration\Utility\DatabaseUtility;
/**
* Class CreateSortingNumberFromPropertyPropertyHelper
*/
class CreateSortingNumberFromPropertyPropertyHelper extends AbstractPropertyHelper implements PropertyHelperInterface
{
/**
* @return void
* @throws DBALException
*/
public function manipulate(): void
{
$sortingArray = $this->getAllOldCategoriesSortedByProperty('sorting');
$sorting = 10000;
if (array_key_exists($this->getPropertyFromRecord('uid'), $sortingArray)) {
$sorting = $sortingArray[$this->getPropertyFromRecordOld('uid')];
} else {
$this->log->addError('Category not sortable: ' . $this->getPropertyFromRecordOld('title'));
}
$this->setProperty($sorting);
}
/**
* @param string $property
* @return array
* @throws DBALException
*/
protected function getAllOldCategoriesSortedByProperty(string $property): array
{
$connection = DatabaseUtility::getConnectionForTable('tt_news_cat');
$rows = (array)$connection->executeQuery(
'select uid from tt_news_cat where deleted=0 order by "' . $property . '"'
)->fetchAll();
$categories = [];
$sorting = 100;
foreach ($rows as $row) {
$categories[$sorting] = $row['uid'];
$sorting += 100;
}
return array_flip($categories);
}
}
Datei EXT:migration_extend/Classes/Migration/PropertyHelpers/GetParentCategoryPropertyHelper.php:
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\PropertyHelpers;
use Doctrine\DBAL\DBALException;
use In2code\Migration\Migration\PropertyHelpers\AbstractPropertyHelper;
use In2code\Migration\Migration\PropertyHelpers\PropertyHelperInterface;
use In2code\Migration\Utility\DatabaseUtility;
/**
* Class GetParentCategoryPropertyHelper
*/
class GetParentCategoryPropertyHelper extends AbstractPropertyHelper implements PropertyHelperInterface
{
/**
* @return void
* @throws DBALException
*/
public function manipulate(): void
{
$queryBuilder = DatabaseUtility::getConnectionForTable($this->table);
$sql = 'select uid from sys_category where _migrated_uid=' . (int)$this->getProperty();
$value = (string)$queryBuilder->executeQuery($sql)->fetchColumn(0);
if ($value > 0) {
$this->log->addMessage('Replace ' . $this->getProperty() . ' with ' . $value . ' in ' . __CLASS__);
$this->setProperty($value);
}
}
/**
* @return bool
*/
public function shouldMigrate(): bool
{
return $this->getProperty() > 0;
}
}
Datei EXT:migration_extend/Classes/Migration/PropertyHelpers/CreateNewsCategoryRelationPropertyHelper.php:
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\PropertyHelpers;
use Doctrine\DBAL\DBALException;
use In2code\Migration\Migration\Helper\DatabaseHelper;
use In2code\Migration\Migration\PropertyHelpers\AbstractPropertyHelper;
use In2code\Migration\Migration\PropertyHelpers\PropertyHelperInterface;
use In2code\Migration\Utility\DatabaseUtility;
use In2code\Migration\Utility\ObjectUtility;
/**
* Class CreateNewsCategoryRelationPropertyHelper
*/
class CreateNewsCategoryRelationPropertyHelper extends AbstractPropertyHelper implements PropertyHelperInterface
{
/**
* @var string
*/
protected $newTableName = 'sys_category_record_mm';
/**
* @var string
*/
protected $oldTableName = 'tt_news_cat_mm';
/**
* @return void
* @throws DBALException
*/
public function manipulate(): void
{
$databaseHelper = ObjectUtility::getObjectManager()->get(DatabaseHelper::class);
$newsUid = (int)$this->getPropertyFromRecord('uid');
$newsUidOld = (int)$this->getPropertyFromRecord('_migrated_uid');
$rows = $this->getOldProperties($newsUidOld);
foreach ($rows as $row) {
if ((int)$row['uid_foreign'] > 0) {
$newCategoryUid = $this->getNewCategoryIdentifier((int)$row['uid_foreign']);
if ($newCategoryUid > 0) {
$newRow = [
'uid_foreign' => $newsUid,
'uid_local' => $newCategoryUid,
'sorting' => $row['sorting'],
'tablenames' => 'tx_news_domain_model_news',
'fieldname' => $this->propertyName
];
$databaseHelper->createRecord($this->newTableName, $newRow);
$this->log->addMessage('New relation to category with uid ' . $row['uid_foreign'] . ' created');
}
}
}
}
/**
* @param int $newsUidOld
* @return array
* @throws DBALException
*/
protected function getOldProperties(int $newsUidOld): array
{
$connection = DatabaseUtility::getConnectionForTable($this->oldTableName);
$rows = (array)$connection->executeQuery(
'select * from ' . $this->oldTableName . ' where uid_local=' . (int)$newsUidOld
)->fetchAll();
return $rows;
}
/**
* @param int $oldIdentifier
* @return int
*/
protected function getNewCategoryIdentifier(int $oldIdentifier): int
{
$queryBuilder = DatabaseUtility::getQueryBuilderForTable('sys_category');
return (int)$queryBuilder
->select('uid')
->from('sys_category')
->where('_migrated_uid=' . $oldIdentifier . ' and _migrated_table="tt_news_cat"')
->setMaxResults(1)
->execute()
->fetchColumn(0);
}
}
Datei EXT:migration_extend/Classes/Migration/PropertyHelpers/CreateNewsImageRelationAndMoveImagePropertyHelper.php:
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\PropertyHelpers;
use Doctrine\DBAL\DBALException;
use In2code\Migration\Migration\Helper\FileHelper;
use In2code\Migration\Migration\PropertyHelpers\AbstractPropertyHelper;
use In2code\Migration\Migration\PropertyHelpers\PropertyHelperInterface;
use In2code\Migration\Utility\DatabaseUtility;
use In2code\Migration\Utility\ObjectUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class CreateNewsImageRelationAndMoveImagePropertyHelper
*/
class CreateNewsImageRelationAndMoveImagePropertyHelper extends AbstractPropertyHelper implements PropertyHelperInterface
{
/**
* @var string
*/
protected $targetFolder = 'files/_migrated/news_images/';
/**
* @var string
*/
protected $oldFolder = 'uploads/pics/';
/**
* @return void
* @throws DBALException
*/
public function manipulate(): void
{
$fileHelper = ObjectUtility::getObjectManager()->get(FileHelper::class);
$imageNames = $this->getImageNames();
foreach ($imageNames as $key => $imageName) {
$fileHelper->copyFileAndCreateReference(
$this->oldFolder . $imageName,
$this->targetFolder,
$this->table,
$this->propertyName,
$this->getPropertyFromRecord('uid'),
$this->getAdditionalProperties($key)
);
$this->log->addMessage('Image copied and created relation to it (' . $imageName . ')');
}
}
/**
* @param int $key
* @return array
*/
protected function getAdditionalProperties(int $key): array
{
$titleTexts = $this->getTitleTexts();
$altTexts = $this->getAltTexts();
$imageCaptions = $this->getImageCaptions();
$links = $this->getImageLinks();
$additionalProperties = ['showinpreview' => 1];
if (array_key_exists($key, $titleTexts)) {
$additionalProperties['title'] = $titleTexts[$key];
}
if (array_key_exists($key, $altTexts)) {
$additionalProperties['alternative'] = $altTexts[$key];
}
if (array_key_exists($key, $imageCaptions)) {
$additionalProperties['description'] = $imageCaptions[$key];
}
if (array_key_exists($key, $links)) {
$additionalProperties['link'] = $links[$key];
}
return $additionalProperties;
}
/**
* @return array
*/
protected function getImageNames(): array
{
return GeneralUtility::trimExplode(',', $this->getPropertyFromRecordOld('image'), true);
}
/**
* @return array
*/
protected function getTitleTexts(): array
{
return GeneralUtility::trimExplode(PHP_EOL, $this->getPropertyFromRecordOld('imagetitletext'), true);
}
/**
* @return array
*/
protected function getAltTexts(): array
{
return GeneralUtility::trimExplode(PHP_EOL, $this->getPropertyFromRecordOld('imagealttext'), true);
}
/**
* @return array
*/
protected function getImageCaptions(): array
{
return GeneralUtility::trimExplode(PHP_EOL, $this->getPropertyFromRecordOld('imagecaption'), true);
}
/**
* @return array
*/
protected function getImageLinks(): array
{
return GeneralUtility::trimExplode(PHP_EOL, $this->getPropertyFromRecordOld('links'), true);
}
/**
* Overrule original function and get values from original tt_news record
*
* @param string $propertyName
* @return int|string
*/
protected function getPropertyFromRecordOld(string $propertyName)
{
$propertiesOld = $this->getPropertiesFromOldRecord();
if (array_key_exists($propertyName, $propertiesOld)) {
return $propertiesOld[$propertyName];
} else {
throw new \LogicException('Property does not exist in ' . __CLASS__, 1569587312);
}
}
/**
* @return array
*/
protected function getPropertiesFromOldRecord(): array
{
$queryBuilder = DatabaseUtility::getQueryBuilderForTable('tt_news');
return (array)$queryBuilder->select('*')
->from('tt_news')
->where('uid=' . (int)$this->getPropertyFromRecord('_migrated_uid'))
->execute()
->fetch();
}
}
Datei EXT:migration_extend/Classes/Migration/PropertyHelpers/CreateNewsFileRelationsPropertyHelper.php:
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\PropertyHelpers;
use Doctrine\DBAL\DBALException;
use In2code\Migration\Migration\Helper\FileHelper;
use In2code\Migration\Migration\PropertyHelpers\AbstractPropertyHelper;
use In2code\Migration\Migration\PropertyHelpers\PropertyHelperInterface;
use In2code\Migration\Utility\DatabaseUtility;
use In2code\Migration\Utility\ObjectUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class CreateNewsFileRelationsPropertyHelper
*/
class CreateNewsFileRelationsPropertyHelper extends AbstractPropertyHelper implements PropertyHelperInterface
{
/**
* @var string
*/
protected $targetFolder = 'files/_migrated/news_files/';
/**
* @var string
*/
protected $oldFolder = 'uploads/media/';
/**
* @return void
* @throws DBALException
*/
public function manipulate(): void
{
$fileHelper = ObjectUtility::getObjectManager()->get(FileHelper::class);
$fileNames = GeneralUtility::trimExplode(',', $this->getPropertyFromRecordOld('news_files'), true);
foreach ($fileNames as $fileName) {
if (is_file(GeneralUtility::getFileAbsFileName($this->oldFolder . $fileName)) === true) {
$fileHelper->copyFileAndCreateReference(
$this->oldFolder . $fileName,
$this->targetFolder,
'tx_news_domain_model_news',
$this->propertyName,
$this->getPropertyFromRecord('uid')
);
$this->log->addMessage('Related file moved and created relation to it (' . $fileName . ')');
}
}
}
/**
* Overrule original function and get values from original tt_news record
*
* @param string $propertyName
* @return int|string
*/
protected function getPropertyFromRecordOld(string $propertyName)
{
$propertiesOld = $this->getPropertiesFromOldRecord();
if (array_key_exists($propertyName, $propertiesOld)) {
return $propertiesOld[$propertyName];
} else {
throw new \LogicException('Property does not exist in ' . __CLASS__, 1569920259);
}
}
/**
* @return array
*/
protected function getPropertiesFromOldRecord(): array
{
$queryBuilder = DatabaseUtility::getQueryBuilderForTable('tt_news');
return (array)$queryBuilder->select('*')
->from('tt_news')
->where('uid=' . (int)$this->getPropertyFromRecord('_migrated_uid'))
->execute()
->fetch();
}
}
Datei EXT:migration_extend/Classes/Migration/PropertyHelpers/CreateNewsRelatedRelationsPropertyHelper.php:
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\Migration\PropertyHelpers;
use Doctrine\DBAL\DBALException;
use In2code\Migration\Migration\Helper\DatabaseHelper;
use In2code\Migration\Migration\PropertyHelpers\AbstractPropertyHelper;
use In2code\Migration\Migration\PropertyHelpers\PropertyHelperInterface;
use In2code\Migration\Utility\DatabaseUtility;
use In2code\Migration\Utility\ObjectUtility;
/**
* Class CreateNewsRelatedRelationsPropertyHelper
*/
class CreateNewsRelatedRelationsPropertyHelper extends AbstractPropertyHelper implements PropertyHelperInterface
{
/**
* @var string
*/
protected $mmTableName = 'tx_news_domain_model_news_related_mm';
/**
* @return void
* @throws DBALException
*/
public function manipulate(): void
{
$identifiersOld = $this->getRelatedTtNews();
if ($identifiersOld !== []) {
foreach ($identifiersOld as $key => $identifierOld) {
$identifier = $this->changeIdentifierFromOldToNew($identifierOld);
$properties = [
'uid_foreign' => (int)$this->getPropertyFromRecord('uid'),
'uid_local' => $identifier,
'sorting' => $key
];
$databaseHelper = ObjectUtility::getObjectManager()->get(DatabaseHelper::class);
$databaseHelper->createRecord($this->mmTableName, $properties);
$this->log->addMessage(
'new news relation added to news ' . $this->getPropertyFromRecord('uid') . '<=>' . $identifier
);
}
}
}
/**
* @return int[]
*/
protected function getRelatedTtNews(): array
{
$queryBuilder = DatabaseUtility::getQueryBuilderForTable('tt_news_related_mm');
$rows = (array)$queryBuilder->select('*')
->from('tt_news_related_mm')
->where('uid_local=' . (int)$this->getPropertyFromRecord('_migrated_uid'))
->execute()
->fetchAll();
$identifiers = [];
foreach ($rows as $row) {
if ($row['uid_foreign'] > 0) {
$identifiers[] = (int)$row['uid_foreign'];
}
}
return $identifiers;
}
/**
* @param int $oldIdentifier
* @return int
*/
protected function changeIdentifierFromOldToNew(int $oldIdentifier): int
{
$queryBuilder = DatabaseUtility::getQueryBuilderForTable('tx_news_domain_model_news');
return (int)$queryBuilder->select('uid')
->from('tx_news_domain_model_news')
->where('_migrated_uid=' . (int)$oldIdentifier)
->execute()
->fetchColumn(0);
}
}
5. FlexForm Templates
Datei EXT:migration_extend/Resources/Private/FlexForms/News.xml:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<T3FlexForms>
<data>
<sheet index="sDEF">
<language index="lDEF">
<field index="settings.orderDirection">
<value index="vDEF">{flexForm.ascDesc}</value>
</field>
<field index="settings.categories">
<value index="vDEF">{migrationextend:convertNewsCategoryListToNewCategoryList(list:flexForm.categorySelection)}</value>
</field>
<field index="settings.categoryConjunction">
<value index="vDEF">{additionalMapping.categorySetting}</value>
</field>
<field index="settings.startingpoint">
<value index="vDEF">{flexForm.pages}</value>
</field>
<field index="switchableControllerActions">
<value index="vDEF">{additionalMapping.switchableControllerActions}</value>
</field>
<field index="settings.includeSubCategories">
<value index="vDEF">{flexForm.useSubCategories}</value>
</field>
<field index="settings.archiveRestriction">
<value index="vDEF">{additionalMapping.archiveSetting}</value>
</field>
</language>
</sheet>
<sheet index="additional">
<language index="lDEF">
<field index="settings.detailPid">
<value index="vDEF">{flexForm.PIDitemDisplay}</value>
</field>
<field index="settings.listPid">
<value index="vDEF">{flexForm.backPid}</value>
</field>
<field index="settings.limit">
<value index="vDEF">{flexForm.listLimit}</value>
</field>
<field index="settings.backPid">
<value index="vDEF">{flexForm.backPid}</value>
</field>
</language>
</sheet>
<sheet index="template">
<language index="lDEF">
<field index="settings.templateLayout">
<value index="vDEF"></value>
</field>
</language>
</sheet>
</data>
</T3FlexForms>
6. ViewHelpers
Datei EXT:migration_extend/Classes/ViewHelpers/ConvertNewsCategoryListToNewCategoryListViewHelper.php:
<?php
declare(strict_types=1);
namespace In2code\MigrationExtend\ViewHelpers;
use In2code\Migration\Utility\DatabaseUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Class ConvertNewsCategoryListToNewCategoryListViewHelper
*/
class ConvertNewsCategoryListToNewCategoryListViewHelper extends AbstractViewHelper
{
/**
* @return void
*/
public function initializeArguments()
{
$this->registerArgument('list', 'string', 'list with tt_news_cat uids', true);
}
/**
* @return string
*/
public function render(): string
{
$newList = [];
$categoriesOld = GeneralUtility::intExplode(',', $this->arguments['list']);
$queryBuilder = DatabaseUtility::getQueryBuilderForTable('sys_category');
foreach ($categoriesOld as $categoryOld) {
$newList[] = (int)$queryBuilder
->select('uid')
->from('sys_category')
->where('_migrated_uid=' . $categoryOld . ' and _migrated_table="tt_news_cat"')
->setMaxResults(1)
->orderBy('uid', 'desc')
->execute()
->fetchColumn(0);
}
return implode(',', $newList);
}
}
Hinweis: Die vielen Dateien haben wir aus einem alten Projekt kopiert und ich hoffe, dass wir nichts Wichtiges vergessen haben. Es kann sein, das sich der Namespace, etc.. an der einen oder anderen Stelle leicht geändert hat. Auch sind nicht alle Funktionen hier aufgelistet: So gibt es immer wieder eine DatabaseUtility Klasse, die jedoch nur eine QueryBuilder oder Connection Klasse zurückliefert.