Skip to content

Commit

Permalink
add Loop steps
Browse files Browse the repository at this point in the history
  • Loading branch information
gggeek committed May 4, 2018
1 parent 001b4a8 commit e1fc17c
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 3 deletions.
84 changes: 84 additions & 0 deletions Core/Executor/LoopExecutor.php
@@ -0,0 +1,84 @@
<?php

namespace Kaliop\eZMigrationBundle\Core\Executor;

use Kaliop\eZMigrationBundle\API\Value\MigrationStep;
use Kaliop\eZMigrationBundle\Core\MigrationService;
use Kaliop\eZMigrationBundle\Core\ReferenceResolver\LoopResolver;

class LoopExecutor extends AbstractExecutor
{
use IgnorableStepExecutorTrait;

protected $supportedStepTypes = array('loop');

/** @var MigrationService $migrationService */
protected $migrationService;

/** @var LoopResolver $loopResolver */
protected $loopResolver;

public function __construct($migrationService, $loopResolver)
{
$this->migrationService = $migrationService;
$this->loopResolver = $loopResolver;
}

/**
* @param MigrationStep $step
* @return mixed
* @throws \Exception
*/
public function execute(MigrationStep $step)
{
parent::execute($step);

if (!isset($step->dsl['repeat']) || $step->dsl['repeat'] < 0) {
throw new \Exception("Invalid step definition: missing 'repeat' or not a positive integer");
}

if (!isset($step->dsl['steps']) || !is_array($step->dsl['steps'])) {
throw new \Exception("Invalid step definition: missing 'steps' or not an array");
}

// no need for a 'mode' for now
/*$action = $step->dsl['mode'];
if (!in_array($action, $this->supportedActions)) {
throw new \Exception("Invalid step definition: value '$action' is not allowed for 'mode'");
}*/

$this->skipStepIfNeeded($step);

// before engaging in the loop, check that all steps are valid
$stepExecutors = array();
foreach ($step->dsl['steps'] as $i => $stepDef) {
$type = $stepDef['type'];
try {
$stepExecutors[$i] = $this->migrationService->getExecutor($type);
} catch (\InvalidArgumentException $e) {
throw new \InvalidArgumentException($e->getMessage() . " in sub-step of a loop step");
}
}

$this->loopResolver->beginLoop();
$result = null;

// NB: we are *not* firing events for each pass of the loop... it might be worth making that optionally happen ?
for ($i = 0; $i < $step->dsl['repeat']; $i++) {

$this->loopResolver->loopStep();

foreach ($step->dsl['steps'] as $j => $stepDef) {
$type = $stepDef['type'];
unset($stepDef['type']);
$subStep = new MigrationStep($type, $stepDef, array_merge($step->context, array()));
$result = $stepExecutors[$j]->execute($subStep);
}
}

$this->loopResolver->endLoop();
return $result;
}

}
44 changes: 44 additions & 0 deletions Core/ReferenceResolver/LoopResolver.php
@@ -0,0 +1,44 @@
<?php

namespace Kaliop\eZMigrationBundle\Core\ReferenceResolver;

class LoopResolver extends AbstractResolver
{
protected $referencePrefixes = array('loop:');

protected $stack = array();

public function beginLoop()
{
$this->stack[] = 0;
}

public function endLoop()
{
array_pop($this->stack);
}

public function loopStep()
{
$idx = count($this->stack) - 1;
$this->stack[$idx] = $this->stack[$idx] + 1;
}

/**
* @param string $identifier format: 'loop:index', 'loop:depth'
* @return int
* @throws \Exception When trying to retrieve anything else but index and depth
*/
public function getReferenceValue($identifier)
{
switch(substr($identifier, 5)) {
case 'iteration':
return end($this->stack);
case 'depth':
return count($this->stack);
default:
throw new \Exception("Can not resolve loop value '$identifier'");
}
}

}
17 changes: 16 additions & 1 deletion Resources/config/services.yml
Expand Up @@ -47,6 +47,7 @@ parameters:

ez_migration_bundle.executor.file.class: Kaliop\eZMigrationBundle\Core\Executor\FileExecutor
ez_migration_bundle.executor.http.class: Kaliop\eZMigrationBundle\Core\Executor\HTTPExecutor
ez_migration_bundle.executor.loop.class: Kaliop\eZMigrationBundle\Core\Executor\LoopExecutor
ez_migration_bundle.executor.mail.class: Kaliop\eZMigrationBundle\Core\Executor\MailExecutor
ez_migration_bundle.executor.migration.class: Kaliop\eZMigrationBundle\Core\Executor\MigrationExecutor
ez_migration_bundle.executor.migration_definition.class: Kaliop\eZMigrationBundle\Core\Executor\MigrationDefinitionExecutor
Expand Down Expand Up @@ -107,6 +108,7 @@ parameters:
ez_migration_bundle.reference_resolver.customreference.class: Kaliop\eZMigrationBundle\Core\ReferenceResolver\CustomReferenceResolver
ez_migration_bundle.reference_resolver.content.class: Kaliop\eZMigrationBundle\Core\ReferenceResolver\ContentResolver
ez_migration_bundle.reference_resolver.location.class: Kaliop\eZMigrationBundle\Core\ReferenceResolver\LocationResolver
ez_migration_bundle.reference_resolver.loop.class: Kaliop\eZMigrationBundle\Core\ReferenceResolver\LoopResolver
ez_migration_bundle.reference_resolver.tag.class: Kaliop\eZMigrationBundle\Core\ReferenceResolver\TagResolver

ez_migration_bundle.helper.limitation_converter.class: Kaliop\eZMigrationBundle\Core\Helper\LimitationConverter
Expand Down Expand Up @@ -216,6 +218,16 @@ services:
tags:
- { name: ez_migration_bundle.executor }

ez_migration_bundle.executor.loop:
class: '%ez_migration_bundle.executor.loop.class%'
arguments:
- '@ez_migration_bundle.migration_service'
- '@ez_migration_bundle.reference_resolver.loop'
calls:
- ['setReferenceMatcher', ['@ez_migration_bundle.reference_matcher']]
tags:
- { name: ez_migration_bundle.executor }

ez_migration_bundle.executor.mail:
class: '%ez_migration_bundle.executor.mail.class%'
arguments:
Expand Down Expand Up @@ -664,6 +676,9 @@ services:
arguments:
- '@ez_migration_bundle.location_matcher'

ez_migration_bundle.reference_resolver.loop:
class: '%ez_migration_bundle.reference_resolver.loop.class%'

ez_migration_bundle.reference_resolver.tag:
class: '%ez_migration_bundle.reference_resolver.tag.class%'
arguments:
Expand All @@ -683,7 +698,7 @@ services:
ez_migration_bundle.reference_resolver.customreference.flexible:
class: '%ez_migration_bundle.reference_resolver.chain_prefix.class%'
arguments:
- [ '@ez_migration_bundle.reference_resolver.customreference.base' ]
- [ '@ez_migration_bundle.reference_resolver.customreference.base', '@ez_migration_bundle.reference_resolver.loop' ]

### misc

Expand Down
51 changes: 51 additions & 0 deletions Resources/doc/DSL/Loops.yml
@@ -0,0 +1,51 @@
# Loop steps allow to execute maultiple times in a row (a sequence of) other steps.
# Nested loops are supported.
# NB: step execution events are triggered only for the outhermost loop step

-
type: loop
repeat: int # number of times to repeat execution of the sequence of sub-steps
steps: # the migration steps that you want to be executed repeatedly
-
type: ...
mode: ...
etc: ... # New references that can be resolved anywhere inside the nested step definitions are `loop:index`
# and `loop:depth`.
# loop:iteration is the counter of the current loop iteration, starting at 1
# loop:depth is used to tell apart nested loops. I starts at depth 1
# f.e. you could use the following to create contents with different names: "Article [loop:depth].[loop:iteration]"
-
type: ...
mode: ...
etc: ...

if: # Optional. If set, the loop will be skipped unless the condition is matched
"reference:_ref_name": # name of a reference to be used for the test
_operator_: value # allowed operators: eq, gt, gte, lt, lte, ne, count, length, regexp

# Example: how to create a reference that holds a composite index of the current step in nested loops:

-
type: loop
repeat: 2
steps:
-
type: reference
mode: set
identifier: loopindex1
value: "[loop:iteration]"
overwrite: true
-
type: loop
repeat: 2
steps:
-
type: reference
mode: set
identifier: loopindex2
value: "[reference:loopindex1].[loop:iteration]"
overwrite: true
-
type: reference
mode: dump
identifier: reference:loopindex2
6 changes: 4 additions & 2 deletions WHATSNEW.md
Expand Up @@ -28,7 +28,7 @@ Version 4.8
type: reference
mode: set
identifier: three
value: "[reference:one].[loop:two]"
value: "both [reference:one] and [reference:two]"
```

Expand All @@ -44,6 +44,8 @@ Version 4.8
...
```

* New: added migration step `loop`. More details in [Resources/doc/DSL/Loops.yml](Resources/doc/DSL/Loops.yml)


Version 4.7
===========
Expand Down Expand Up @@ -230,7 +232,7 @@ Version 4.0 RC-3
* New: allow to run migrations without changing into an admin user (only for developers, not yet from the command line)

* New: it is now possible to execute external processes as migration steps.
More details in [Resources/doc/DSL/Processes.yml](Resources/doc/DSL/Mails.yml)
More details in [Resources/doc/DSL/Processes.yml](Resources/doc/DSL/Processes.yml)


Version 4.0 RC-2
Expand Down

0 comments on commit e1fc17c

Please sign in to comment.