Node Migrations [v9]

Neos 9.x Content

This is content for the upcoming Neos 9.0 release with the rewritten Content Repository based on Event Sourcing.

We heavily work on these docs right now, so expect rough edges.

Node Migrations provide a programmatic method to perform bulk updates on nodes within Neos. They are essential when you need to rename NodeTypes, modify property names, or update Content Dimensions, ensuring that your content structure remains consistent and up-to-date across the entire site.

#The Core Idea 

Node migrations are defined using YAML files, which are located in the Migrations/ContentRepository/ folder of a package.

Each node migration involves two key steps:

  1. Filters: These are used to specify the conditions under which a node should be transformed. A node is only transformed if it meets all the defined filter criteria.
  2. Transformations: Once nodes are filtered, a series of transformations are applied to update those specific nodes.

#What’s New in Neos 9 

In Neos 9, each migration now only requires a "forward" migration, simplifying the process. Unlike previous versions, there’s no need to define a reverse migration. If you need to revert changes, you can simply create a new migration specifically for rolling back to a previous state.

#Running a Migration

To view all available migrations across all packages, use the ./flow nodemigration:list command.

bash
$ ./flow nodemigration:list

Available migrations

+----------------+---------------------+-----------+-----------------------------------------------+
| Version        | Date                | Package   | Comments                                      |
+----------------+---------------------+-----------+-----------------------------------------------+
| 20240816175911 | 16-08-2024 17:59:11 | Neos.Demo | Add shine through from german to swiss german |
| 20240820111336 | 20-08-2024 11:13:36 | Neos.Demo | Add shine through from german to swiss german |
| 20240830151741 | 30-08-2024 15:17:41 | Neos.Demo | Rename authorName property to publisher for   |
|                |                     |           | Neos.Demo:Document.BlogPosting                |
+----------------+---------------------+-----------+-----------------------------------------------+

To execute a specific migration, run the ./flow nodemigration:execute [version] command, replacing [version] with the appropriate migration version identifier.

bash
$ ./flow nodemigration:execute 20240816175911

Comments
  Add shine through from german to swiss german

Successfully applied migration.

#Command Options

You can customize the migration process using the following options:

--content-repository=[contentRepositoryId]
Specifies the content repository where the migration should be applied. By default, the migration targets the "default" content repository.

--source-workspace=[workspace]
Defines the workspace for applying the migration. By default, this is the "live" workspace.

--publish-on-success=[true|false]
Determines whether changes are automatically published to the source workspace upon successful migration. By default, this is set to true. If set to false, changes remain in a temporary workspace and must be manually published or discarded.

--force
Forces the migration to execute, bypassing all warnings.

#Writing a Custom Migration

Pro Tip:

Creating a new node migration can be done directly from the console. Simply execute the nodemigration:kickstart command, and it will automatically generate a correctly named, empty migration file in the appropriate directory, ready for you to customize.

bash
./flow nodemigration:kickstart

Place a new YAML file inside the Migrations/ContentRepository folder of your package.

The migration YAML file must be named Version[YYYYMMDDHHmmss].yaml, where the timestamp reflects the exact time the migration was created. This naming convention ensures that migrations are executed in the correct chronological order.

Below is an example of a migration that targets nodes of the Neos.Demo:Document.BlogPosting NodeType and renames the existing authorName property to publisher.

Migrations/ContentRepository/Version20240830151741.yaml
comments: 'Rename authorName property to publisher for Neos.Demo:Document.BlogPosting'
migration:
  - filters:
      - type: 'NodeType'
        settings:
          nodeType: 'Neos.Demo:Document.BlogPosting'

    transformations:
      - type: 'RenameProperty'
        settings:
          from: 'authorName'
          to: 'publisher'

#Available Filters

The Content Repository provides several built-in filters, including:

Node scoped filter

  • DimensionSpacePoints
  • PropertyNotEmpty
  • PropertyValue

NodeAggregate scoped filter

  • NodeName
  • NodeType

All of these filters implement the Neos\ContentRepository\Migration\Filters\FilterInterface. You can also create custom filters by implementing this interface. When defining a custom filter, use its fully qualified class name to specify which filter to apply.

#Available Transformations

The Content Repository includes a variety of common transformations, such as:

Node scoped transformations

  • AddNewProperty
  • ChangePropertyValue
  • RemoveNode
  • RemoveProperty
  • RenameProperty
  • StripTagsOnProperty

NodeAggregate scoped transformations

  • ChangeNodeType
  • RenameNodeAggregate

Global scoped transformations

  • AddDimensionShineThrough
  • MoveDimensionSpacePoint

All of these transformations implement the Neos\ContentRepository\NodeMigration\Transformation\TransformationFactoryInterface. If you need custom transformations, you can develop them using this interface. When specifying a custom transformation, use its fully qualified class name.

#Filter and Transition Scopes

Filters and transitions in node migrations operate within different scopes. Understanding these scopes is crucial for applying the correct operations. Here’s a breakdown of the different scopes:

Global Scoped Operations: These operate across the entire content repository and are not tied to any specific node or node aggregate. They are typically used for operations affecting dimensions.

NodeAggregate Scoped Operations: These operations affect NodeAggregates, such as their NodeType or NodeName. 

Node Scoped Operations: These operations are applied to individual nodes. While they can affect nodes within a NodeAggregate, they operate on each node separately rather than the aggregate as a whole.

Rules for Applying Transformations

  • Transformation Scopes: Different transformation scopes cannot be mixed. Ensure that each migration adheres to a single scope.
  • Global Scoped Transformations: When performing a Global Scoped Transformation, you are not permitted to use any filters.
  • NodeAggregate Scoped Transformations: For NodeAggregate Scoped Transformations, node-scoped filters cannot be used.

#Common Use Cases

#Adding a Content Dimension ShineThrough 

If you add a new content dimension value and want to apply a fallback for this dimension, you’ll need to migrate all affected nodes. For a detailed example of how to perform this migration, please refer to the chapter titled "Migrating dimension config"