Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions code_samples/data_migration/examples/sql_execute.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type: sql
mode: execute
query:
-
driver: mysql
sql: 'INSERT INTO test_table (test_value) VALUES ("foo");'
-
driver: sqlite
sql: 'INSERT INTO test_table (test_value) VALUES ("foo");'
-
driver: postgresql
sql: "INSERT INTO test_table (test_value) VALUES ('foo');"
14 changes: 14 additions & 0 deletions code_samples/data_migration/examples/try_catch_step.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally:

09:15:17 NOTICE    [app] Executing migration: "l5"
09:15:17 INFO      [app] Processing step: 0
09:15:17 INFO      [app] Type "try_catch" | Mode: "execute" | Matching step: "Ibexa\Migration\ValueObject\Step\TryCatchStep"
09:15:17 INFO      [app] Processing step: 0
09:15:17 INFO      [app] Type "language" | Mode: "create" | Matching step: "Ibexa\Migration\ValueObject\Step\LanguageCreateStep"
✅ Migration l5 finished executing.

type: try_catch
mode: execute
allowed_exceptions:
- Throwable
stop_after_first_exception: true
steps:
-
type: language
mode: create
metadata:
languageCode: ger-DE
name: German
enabled: true
60 changes: 60 additions & 0 deletions docs/content_management/data_migration/importing_data.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@
| `user` | ✔ | ✔ | | | |
| `user_group` | ✔ | ✔ | ✔ | | |

Additionally, the following special migration types are available:

| `type` | `execute` |
|------------------------|:---------:|
| `repeatable` | ✔ |
| `sql` | ✔ |
| `try_catch` | ✔ |

### Repeatable steps

You can run a set of one or more similar migration steps multiple times by using the special `repeatable` migration type.
Expand Down Expand Up @@ -122,6 +130,58 @@

This step generates field values with fake personal names.

### SQL migrations

Check notice on line 133 in docs/content_management/data_migration/importing_data.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/content_management/data_migration/importing_data.md#L133

[Ibexa.SentenceCapitalizationInHeadings] Use sentence-style capitalization in headings
Raw output
{"message": "[Ibexa.SentenceCapitalizationInHeadings] Use sentence-style capitalization in headings", "location": {"path": "docs/content_management/data_migration/importing_data.md", "range": {"start": {"line": 133, "column": 5}}}, "severity": "INFO"}

You can execute raw SQL queries directly in migrations by using the `sql` migration type.
Use it for custom database operations that don't fit into standard entity migrations, such as creating custom tables or performing bulk updates.

Each query requires a `driver` property that specifies which database system the query is for.
The migration system automatically filters queries and executes only those matching your current database driver.

```yaml
[[= include_file('code_samples/data_migration/examples/sql_execute.yaml') =]]
```

The supported database drivers are:

- `mysql` - MySQL/MariaDB
- `postgresql` - PostgreSQL
- `sqlite` - SQLite

You can define queries for multiple database drivers in a single migration step.
The system executes only the queries that match your configured database platform.
If no matching queries are found, the migration throws an error.

Check notice on line 153 in docs/content_management/data_migration/importing_data.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/content_management/data_migration/importing_data.md#L153

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/content_management/data_migration/importing_data.md", "range": {"start": {"line": 153, "column": 24}}}, "severity": "INFO"}

!!! caution

SQL migrations bypass the content model abstraction layer and directly modify the database.
Use them with caution and ensure your queries are compatible with your target database system.

### Error handling with try-catch

You can wrap one or more migration steps with a `try_catch` step to handle exceptions gracefully.

Use it for migrations that may fail under specific conditions but should not halt the entire migration process.

For example, you can ensure a language creation migration succeeds even if the language already exists.
If the migration fails for this reason, the exception is suppressed, allowing the remaining migrations to proceed without interruption.

Check notice on line 167 in docs/content_management/data_migration/importing_data.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/content_management/data_migration/importing_data.md#L167

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/content_management/data_migration/importing_data.md", "range": {"start": {"line": 167, "column": 55}}}, "severity": "INFO"}

A `try_catch` migration requires the `steps` property and accepts optional `allowed_exceptions` and `stop_after_first_exception` settings.

Default values are:

- `allowed_exceptions`: empty list
- `stop_after_first_exception`: `true`

```yaml
[[= include_file('code_samples/data_migration/examples/try_catch_step.yaml') =]]
```

When an exception is thrown within a try-catch step, it's compared against the list of `allowed_exceptions`.

Check notice on line 180 in docs/content_management/data_migration/importing_data.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/content_management/data_migration/importing_data.md#L180

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/content_management/data_migration/importing_data.md", "range": {"start": {"line": 180, "column": 19}}}, "severity": "INFO"}
If the exception matches, it's caught and the migration continues or stops depending on the `stop_after_first_exception` configuration setting.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In both cases migration is treated as if it succeeded.

Non-matching exceptions throw immediately just like before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Added in af4288e

The migration is marked as successful.

Check notice on line 182 in docs/content_management/data_migration/importing_data.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/content_management/data_migration/importing_data.md#L182

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/content_management/data_migration/importing_data.md", "range": {"start": {"line": 182, "column": 15}}}, "severity": "INFO"}
Non-matching exceptions throw immediately, halting the migration process and returning an error.

### Expression syntax

You can use [Symfony expression syntax]([[= symfony_doc =]]/reference/formats/expression_language.html) in data migrations, like in [repeatable steps](#repeatable-steps), where you can use it to generate varied content in migration steps.
Expand Down
16 changes: 16 additions & 0 deletions docs/release_notes/ibexa_dxp_v5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ month_change: false

<div class="release-notes" markdown="1">

<draft release notes entry>
#### Try-catch support in data migrations

Data migrations now support try-catch error handling, allowing you to wrap migration steps with exception handling logic.
You can use it for migrations that might fail under certain conditions but should not break the entire migration process.

For example, you can create languages without checking if they already exist:

``` yaml
[[= include_file('code_samples/data_migration/examples/try_catch_step.yaml') =]]
```

The try-catch step allows you to specify which exceptions to catch and whether to continue executing remaining steps after an exception occurs.

For more information, see [Error handling with try-catch](importing_data.md#error-handling-with-try-catch).

[[% set version = 'v5.0.5' %]]

[[= release_note_entry_begin("Ibexa DXP " + version, '2026-01-15', ['Headless', 'Experience', 'Commerce']) =]]
Expand Down
Loading