Completed
Push — master ( 59250f...2c6719 )
by Gaetano
10:09
created

MigrationService::getMigrationsDefinitions()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 18
rs 8.8571
cc 6
eloc 9
nc 8
nop 1
1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core;
4
5
use Kaliop\eZMigrationBundle\API\Collection\MigrationDefinitionCollection;
6
use Kaliop\eZMigrationBundle\API\StorageHandlerInterface;
7
use Kaliop\eZMigrationBundle\API\LoaderInterface;
8
use Kaliop\eZMigrationBundle\API\DefinitionParserInterface;
9
use Kaliop\eZMigrationBundle\API\ExecutorInterface;
10
use Kaliop\eZMigrationBundle\API\Value\Migration;
11
use Kaliop\eZMigrationBundle\API\Value\MigrationDefinition;
12
use eZ\Publish\API\Repository\Repository;
13
14
class MigrationService
15
{
16
    /**
17
     * @var LoaderInterface $loader
18
     */
19
    protected $loader;
20
    /**
21
     * @var StorageHandlerInterface $storageHandler
22
     */
23
    protected $storageHandler;
24
25
    /** @var DefinitionParserInterface[] $DefinitionParsers */
26
    protected $DefinitionParsers = array();
27
28
    /** @var ExecutorInterface[] $executors */
29
    protected $executors = array();
30
31
    protected $repository;
32
33
    public function __construct(LoaderInterface $loader, StorageHandlerInterface $storageHandler, Repository $repository)
34
    {
35
        $this->loader = $loader;
36
        $this->storageHandler = $storageHandler;
37
        $this->repository = $repository;
38
    }
39
40
    public function addDefinitionParser(DefinitionParserInterface $DefinitionParser)
41
    {
42
        $this->DefinitionParsers[] = $DefinitionParser;
43
    }
44
45
    public function addExecutor(ExecutorInterface $executor)
46
    {
47
        foreach($executor->supportedTypes() as $type) {
48
            $this->executors[$type] = $executor;
49
        }
50
    }
51
52
    /**
53
     * NB: returns UNPARSED definitions
54
     *
55
     * @param string[] $paths
56
     * @return MigrationDefinitionCollection key: migration name, value: migration definition as binary string
57
     */
58
    public function getMigrationsDefinitions(array $paths = array())
59
    {
60
        // we try to be flexible in file types we support, and the same time avoid loading all files in a directory
61
        $handledDefinitions = array();
62
        foreach($this->loader->listAvailableDefinitions($paths) as $migrationName => $definitionPath) {
63
            foreach($this->DefinitionParsers as $definitionParser) {
64
                if ($definitionParser->supports($migrationName)) {
65
                    $handledDefinitions[] = $definitionPath;
66
                }
67
            }
68
        }
69
70
        // we can not call loadDefinitions with an empty array, or it will start looking in bundles...
71
        if (empty($handledDefinitions) && !empty($paths)) {
72
            return new MigrationDefinitionCollection();
73
        }
74
        return $this->loader->loadDefinitions($handledDefinitions);
75
    }
76
77
    /**
78
     * Return the list of all the migrations which where executed or attempted so far
79
     *
80
     * @return \Kaliop\eZMigrationBundle\API\Collection\MigrationCollection
81
     */
82
    public function getMigrations()
83
    {
84
        return $this->storageHandler->loadMigrations();
85
    }
86
87
    /**
88
     * @param string $migrationName
89
     * @return Migration|null
90
     */
91
    public function getMigration($migrationName)
92
    {
93
        return $this->storageHandler->loadMigration($migrationName);
94
    }
95
96
    /**
97
     * @param Migration $migration
0 ignored issues
show
Documentation introduced by
There is no parameter named $migration. Did you maybe mean $migrationDefinition?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
98
     */
99
    public function addMigration(MigrationDefinition $migrationDefinition)
100
    {
101
        return $this->storageHandler->addMigration($migrationDefinition);
102
    }
103
104
    /**
105
     * @param Migration $migration
106
     */
107
    public function deleteMigration(Migration $migration)
108
    {
109
        return $this->storageHandler->deleteMigration($migration);
110
    }
111
112
    /**
113
     * Parses a migration definition, return a parsed definition.
114
     * If there is a parsing error, the definition status will be updated accordingly
115
     *
116
     * @param MigrationDefinition $migrationDefinition
117
     * @return MigrationDefinition
118
     * @throws \Exception if the migrationDefinition has no suitable parser for its source format
119
     */
120
    public function parseMigrationDefinition(MigrationDefinition $migrationDefinition)
121
    {
122
        foreach($this->DefinitionParsers as $definitionParser) {
123
            if ($definitionParser->supports($migrationDefinition->name)) {
124
                // parse the source file
125
                $migrationDefinition = $definitionParser->parseMigrationDefinition($migrationDefinition);
126
127
                // and make sure we know how to handle all steps
128
                foreach($migrationDefinition->steps as $step) {
129
                    if (!isset($this->executors[$step->type])) {
130
                        return new MigrationDefinition(
131
                            $migrationDefinition->name,
132
                            $migrationDefinition->path,
133
                            $migrationDefinition->rawDefinition,
134
                            MigrationDefinition::STATUS_INVALID,
135
                            array(),
136
                            "Can not handle migration step of type '{$step->type}'"
137
                        );
138
                    }
139
                }
140
141
                return $migrationDefinition;
142
            }
143
        }
144
145
        throw new \Exception("No parser available to parse migration definition '$migrationDefinition'");
146
    }
147
148
    /**
149
     * @param MigrationDefinition $migrationDefinition
150
     * @throws \Exception
151
     *
152
     * @todo add support for skipped migrations, partially executed migrations
153
     */
154
    public function executeMigration(MigrationDefinition $migrationDefinition)
155
    {
156
        if ($migrationDefinition->status == MigrationDefinition::STATUS_TO_PARSE) {
157
            $migrationDefinition = $this->parseMigrationDefinition($migrationDefinition);
158
        }
159
160
        if ($migrationDefinition->status == MigrationDefinition::STATUS_INVALID) {
161
            throw new \Exception("Can not execute migration '{$migrationDefinition->name}': {$migrationDefinition->parsingError}");
162
        }
163
164
        // set migration as begun - has to be in own db transaction
165
        $migration = $this->storageHandler->startMigration($migrationDefinition);
166
167
        $this->repository->beginTransaction();
168
        try {
169
170
            foreach($migrationDefinition->steps as $step) {
171
                // we validated the fact that we have a good executor at parsing time
172
                $executor = $this->executors[$step->type];
173
                $executor->execute($step);
174
            }
175
176
            $status = Migration::STATUS_DONE;
177
178
            // set migration as done
179
            $this->storageHandler->endMigration(new Migration(
180
                $migration->name,
181
                $migration->md5,
182
                $migration->path,
183
                $migration->executionDate,
184
                $status
185
            ));
186
187
            $this->repository->commit();
188
189
        } catch(\Exception $e) {
190
            $this->repository->rollBack();
191
192
            /// set migration as failed
193
            $this->storageHandler->endMigration(new Migration(
194
                $migration->name,
195
                $migration->md5,
196
                $migration->path,
197
                $migration->executionDate,
198
                Migration::STATUS_FAILED,
199
                $e->getMessage()
200
            ));
201
202
            throw $e;
203
        }
204
    }
205
}