diff --git a/UPGRADE.md b/UPGRADE.md
index 66c0b356..594ec93e 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -58,7 +58,116 @@ ALTER TABLE test_example_dimension_contents DROP dimension_id;
DROP TABLE cn_dimensions;
```
-TODO provide here a general doctrine migration which support up/down.
+If you are using the `DoctrineMigrationBundle` you can also reuse the following migration class
+to migrate the data of you entity. Make sure to provide the correct `tableName`, `foreignKey`
+and `indexName` before executing the migration.
+
+
+DoctrineMigrationBundle Example
+
+```php
+>
+ */
+ private function tableData(): array
+ {
+ return [
+ [
+ // TODO replace the following values with the ones by your table
+ 'tableName' => 'my_entity_dimension_content', # name of the table of your DimensionContent entity
+ 'foreignKey' => 'FK_61A94F1277428AD', # dimension foreign key on the DimensionContent table
+ 'indexName' => 'IDX_61A94F1277428AD', # dimension index on the DimensionContent table
+ ],
+ ];
+ }
+
+ public function up(Schema $schema): void
+ {
+ foreach ($this->tableData() as $tableConfig) {
+ $tableName = $tableConfig['tableName'];
+ $foreignKey = $tableConfig['foreignKey'];
+ $indexName = $tableConfig['indexName'];
+
+ // Create stage and locale fields
+ $this->addSql(\sprintf('ALTER TABLE %s ADD stage VARCHAR(16) DEFAULT NULL, ADD locale VARCHAR(7) DEFAULT NULL;', $tableName));
+
+ // Migrate data to new fields
+ $this->addSql(\sprintf('UPDATE %s myContentDimension INNER JOIN cn_dimensions dimension ON dimension.no = myContentDimension.dimension_id SET myContentDimension.stage = dimension.stage, myContentDimension.locale = dimension.locale;', $tableName));
+
+ // Remove dimension relation
+ $this->addSql(\sprintf('ALTER TABLE %s DROP FOREIGN KEY %s', $tableName, $foreignKey));
+ $this->addSql(\sprintf('DROP INDEX %s ON %s', $indexName, $tableName));
+ $this->addSql(\sprintf('ALTER TABLE %s DROP dimension_id;', $tableName));
+ }
+
+ // Drop Dimension Table
+ $this->addSql('DROP TABLE cn_dimensions');
+ }
+
+ public function down(Schema $schema): void
+ {
+ // create old dimension table
+ $this->addSql('CREATE TABLE cn_dimensions (no INT AUTO_INCREMENT NOT NULL, id CHAR(36) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci` COMMENT \'(DC2Type:guid)\', locale VARCHAR(5) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, stage VARCHAR(16) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, INDEX IDX_979F85354180C698 (locale), UNIQUE INDEX UNIQ_979F8535BF396750 (id), INDEX IDX_979F8535C27C9369 (stage), PRIMARY KEY(no)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB COMMENT = \'\' ');
+
+ foreach ($this->tableData() as $tableConfig) {
+ $tableName = $tableConfig['tableName'];
+ $foreignKey = $tableConfig['foreignKey'];
+ $indexName = $tableConfig['indexName'];
+
+ // Create dimension relation
+ $this->addSql(\sprintf('ALTER TABLE %s ADD dimension_id INT NOT NULL', $tableName));
+
+ // migrate data into the old table
+ $this->addSql(\sprintf('
+ INSERT INTO cn_dimensions (id, locale, stage)
+ (SELECT
+ UUID() as id,
+ myContentDimension.locale,
+ myContentDimension.stage
+ FROM ec_product_line_dimension_content myContentDimension
+ LEFT JOIN cn_dimensions ON (cn_dimensions.stage = myContentDimension.stage AND cn_dimensions.locale = myContentDimension.locale OR (cn_dimensions.locale IS NULL AND myContentDimension.locale IS NULL))
+ WHERE cn_dimensions.id IS NULL
+ GROUP BY myContentDimension.locale, myContentDimension.stage)
+ ', $tableName));
+
+ $this->addSql(\sprintf('
+ UPDATE %s myContentDimension
+ INNER JOIN cn_dimensions dimension
+ ON myContentDimension.stage = dimension.stage AND (myContentDimension.locale = dimension.locale OR (myContentDimension.locale IS NULL AND dimension.locale IS NULL))
+ SET myContentDimension.dimension_id = dimension.no
+ ', $tableName));
+
+ // Add dimension relation
+ $this->addSql(\sprintf('ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (dimension_id) REFERENCES cn_dimensions (no) ON DELETE CASCADE', $tableName, $foreignKey));
+ $this->addSql(\sprintf('CREATE INDEX %s ON %s (dimension_id)', $indexName, $tableName));
+
+ // remove stage and locale
+ $this->addSql(\sprintf('ALTER TABLE %s DROP stage, DROP locale', $tableName));
+ }
+ }
+}
+```
+
+
#### Update your ContentRichEntity class and DimensionContent class
@@ -114,7 +223,6 @@ If you use the dimension data in your list configuration, you need to change it
-
- %sulu.model.dimension.class%
- %sulu.model.dimension.class%.locale = :locale AND %sulu.model.dimension.class%.stage = 'draft'
-+ dimensionContent.locale = :locale AND dimensionContent.stage = 'draft'
-
-
@@ -145,7 +253,7 @@ If you use the dimension data in your list configuration, you need to change it
### ContentTeaserProvider constructor changed
-The constructor of the `ContentTeaserProvider` requires like the `ContentDataProviderRepository` the `show_drafts`
+The constructor of the `ContentTeaserProvider` requires like the `ContentDataProviderRepository` the `show_drafts`
parameter. In this case also the `getShowDrafts` was removed from the `ContentTeaserProvider` class.
**before**:
@@ -215,13 +323,13 @@ parameter. In this case also the `getShowDrafts` was removed from the `ContentTe
### Rename getContentRichEntity method of DimensionContentInterface to getResource
-The `getContentRichEntity` method of the `DimensionContentInterface` was renamed to `getResource`.
-This makes the naming consistent with the `getResourceKey` method of the `DimensionContentInterface` and
+The `getContentRichEntity` method of the `DimensionContentInterface` was renamed to `getResource`.
+This makes the naming consistent with the `getResourceKey` method of the `DimensionContentInterface` and
the `getResourceId` method of the `RoutableInterface`.
### Rename getRoutableId method of RoutableInterface to getResourceId
-The `getRoutableId` method of the `RoutableInterface` was renamed to `getResourceId`. This makes the naming consistent
+The `getRoutableId` method of the `RoutableInterface` was renamed to `getResourceId`. This makes the naming consistent
with the `getResourceKey` method of the `RoutableInterface` and the `DimensionContentInterface`.
### Add contentRichEntityClass parameter to getDefaultToolbarActions method of ContentViewBuilderFactory
@@ -239,14 +347,14 @@ This makes it consistent with the `getTemplateType` method and the `getWorkflowN
The bundle now uses the `route_schema` that is configured via `sulu_route.mappings` for generating the route for an
entity instead of a hardcoded value. If no `route_schema` is configured, no route will be generated.
-Therefore, the `getContentId` method of the `RoutableInterface` was renamed to `getRoutableId` and the
+Therefore, the `getContentId` method of the `RoutableInterface` was renamed to `getRoutableId` and the
`getContentClass` method was replaced with a `getResourceKey` method.
### Moved automation bundle services
-The services related to the `SuluAutomationBundle` were moved to the
-`Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Automation` namespace.
-Furthermore the `ContentEntityPublishHandler` was renamed to `ContentPublishTaskHandler` and
+The services related to the `SuluAutomationBundle` were moved to the
+`Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Automation` namespace.
+Furthermore the `ContentEntityPublishHandler` was renamed to `ContentPublishTaskHandler` and
the `ContentEntityUnpublishHandler` was renamed to `ContentUnpublishTaskHandler`.
### Removed ContentProjection concept
@@ -255,7 +363,7 @@ To simplify the usage of the bundle, the ContentProjection concept was removed f
Therefore, the `ContentProjectionInterface` and the `ContentProjectionFactoryInterface` were removed.
Services that returned a `ContentProjectionInterface` instance were adjusted to return a merged
-`DimensionContentInterface` instance. Furthermore, the `ContentMergerInterface::merge` method
+`DimensionContentInterface` instance. Furthermore, the `ContentMergerInterface::merge` method
was refactored to accept a `DimensionContentCollectionInterface` parameter.
### Renamed merger services
@@ -269,7 +377,7 @@ was refactored to accept a `DimensionContentCollectionInterface` parameter.
### Refactored the ContentViewBuilder
-The class and its interface was renamed from `ContentViewBuilder` & `ContentViewBuilderInterface`
+The class and its interface was renamed from `ContentViewBuilder` & `ContentViewBuilderInterface`
to `ContentViewBuilderFactory` & `ContentViewBuilderFactoryInterface`.
The service has been renamed from `sulu_content.content_view_builder` to `sulu_content.content_view_builder_factory`.
@@ -277,7 +385,7 @@ The service has been renamed from `sulu_content.content_view_builder` to `sulu_c
The function `build` was replaced by `createViews` and additional functions has been introduced.
The behaviour of the `createViews` function detects now the needed views: the template-view if the `TemplateInterface`
-is implemented, the seo-view if the `SeoInterface` is implemented, and the excerpt-view if the `ExcerptInterface` is
+is implemented, the seo-view if the `SeoInterface` is implemented, and the excerpt-view if the `ExcerptInterface` is
implemented.
**before**: