- Documentation >
- Content management >
- Data migration >
- Create data migration step
Create data migration step
Besides the built-in migrations steps, you can also create custom ones.
To create a custom migration step, you need:
- A step class, to store any additional data that you might require.
- A step normalizer, to convert YAML definition into your step class.
- A step executor, to handle the step.
The following example shows how to create a step that replaces all ezstring
fields that have an old company name with "New Company Name".
Create step class
First, create a step class, in src/Migrations/Step/ReplaceNameStep.php
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | <?php
declare(strict_types=1);
namespace App\Migrations\Step;
use Ibexa\Migration\ValueObject\Step\StepInterface;
final class ReplaceNameStep implements StepInterface
{
private string $replacement;
public function __construct(?string $replacement = null)
{
$this->replacement = $replacement ?? 'New Company Name';
}
public function getReplacement(): string
{
return $this->replacement;
}
}
|
Create normalizer
Then you need a normalizer to convert data that comes from YAML into a step object,
in src/Migrations/Step/ReplaceNameStepNormalizer.php
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 | <?php
declare(strict_types=1);
namespace App\Migrations\Step;
use Ibexa\Contracts\Migration\Serializer\AbstractStepNormalizer;
use Ibexa\Migration\ValueObject\Step\StepInterface;
/**
* @extends \Ibexa\Contracts\Migration\Serializer\AbstractStepNormalizer<\App\Migrations\Step\ReplaceNameStep>
*/
final class ReplaceNameStepNormalizer extends AbstractStepNormalizer
{
protected function normalizeStep(
StepInterface $object,
string $format = null,
array $context = []
): array {
assert($object instanceof ReplaceNameStep);
return [
'replacement' => $object->getReplacement(),
];
}
protected function denormalizeStep(
$data,
string $type,
string $format,
array $context = []
): ReplaceNameStep {
return new ReplaceNameStep($data['replacement'] ?? null);
}
public function getHandledClassType(): string
{
return ReplaceNameStep::class;
}
public function getType(): string
{
return 'company_name';
}
public function getMode(): string
{
return 'replace';
}
}
|
Then, tag the step normalizer, so it's recognized by the serializer used for migrations.
| App\Migrations\Step\ReplaceNameStepNormalizer:
tags:
- 'ibexa.migrations.serializer.step_normalizer'
- 'ibexa.migrations.serializer.normalizer'
|
Create executor
And finally, create an executor to perform the step, in src/Migrations/Step/ReplaceNameExecutor.php
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 | <?php
declare(strict_types=1);
namespace App\Migrations\Step;
use Ibexa\Contracts\Core\Repository\ContentService;
use Ibexa\Contracts\Core\Repository\Values\Filter\Filter;
use Ibexa\Contracts\Migration\StepExecutor\AbstractStepExecutor;
use Ibexa\Core\FieldType\TextLine\Value;
use Ibexa\Migration\ValueObject\Step\StepInterface;
final class ReplaceNameStepExecutor extends AbstractStepExecutor
{
private ContentService $contentService;
public function __construct(
ContentService $contentService
) {
$this->contentService = $contentService;
}
protected function doHandle(StepInterface $step)
{
assert($step instanceof ReplaceNameStep);
$contentItems = $this->contentService->find(new Filter());
foreach ($contentItems as $contentItem) {
$struct = $this->contentService->newContentUpdateStruct();
foreach ($contentItem->getFields() as $field) {
if ($field->fieldTypeIdentifier !== 'ezstring') {
continue;
}
if ($field->fieldDefIdentifier === 'identifier') {
continue;
}
if (str_contains($field->value, 'Company Name')) {
$newValue = str_replace('Company Name', $step->getReplacement(), $field->value);
$struct->setField($field->fieldDefIdentifier, new Value($newValue));
}
}
try {
$content = $this->contentService->createContentDraft($contentItem->contentInfo);
$content = $this->contentService->updateContent($content->getVersionInfo(), $struct);
$this->contentService->publishVersion($content->getVersionInfo());
} catch (\Throwable $e) {
// Ignore
}
}
return null;
}
public function canHandle(StepInterface $step): bool
{
return $step instanceof ReplaceNameStep;
}
}
|
Tag the executor with ibexa.migrations.step_executor
tag.
| App\Migrations\Step\ReplaceNameStepExecutor:
tags:
- 'ibexa.migrations.step_executor'
|
Then you can create a migration file that represents this step in your application:
| - type: company_name
mode: replace
replacement: 'New Company Name' # as declared in normalizer, this is optional
|