Completed
Push — master ( 348067...ae37c1 )
by Zach
03:29 queued 01:39
created

FileDateMigrator::setUp()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Yarak\Migrations\FileDate;
4
5
use Yarak\Helpers\Str;
6
use Yarak\Config\Config;
7
use Yarak\Helpers\Loggable;
8
use Yarak\Helpers\Filesystem;
9
use Yarak\Migrations\Migrator;
10
use Yarak\DB\ConnectionResolver;
11
use Yarak\Migrations\Repositories\MigrationRepository;
12
13
class FileDateMigrator implements Migrator
14
{
15
    use Filesystem, Loggable;
16
17
    /**
18
     * Yarak config.
19
     *
20
     * @var Config
21
     */
22
    protected $config;
23
24
    /**
25
     * Database connection resolver.
26
     *
27
     * @var ConnectionResolver
28
     */
29
    protected $resolver;
30
31
    /**
32
     * Repository for logging migration activity.
33
     *
34
     * @var MigrationRepository
35
     */
36
    protected $repository;
37
38
    /**
39
     * The active database connection.
40
     *
41
     * @var \Phalcon\Db\Adapter\Pdo
42
     */
43
    protected $connection = null;
44
45
    /**
46
     * Construct.
47
     *
48
     * @param Config              $config
49
     * @param ConnectionResolver  $resolver
50
     * @param MigrationRepository $repository
51
     */
52
    public function __construct(
53
        Config $config,
54
        ConnectionResolver $resolver,
55
        MigrationRepository $repository
56
    ) {
57
        $this->config = $config;
58
        $this->resolver = $resolver;
59
        $this->repository = $repository;
60
    }
61
62
    /**
63
     * Run migrations.
64
     *
65
     * @return array
66
     */
67
    public function run()
68
    {
69
        $this->setUp();
70
71
        $pendingMigrations = $this->getPendingMigrations();
72
73
        return $this->runPending($pendingMigrations);
74
    }
75
76
    /**
77
     * Get all migration filenames that have not been run.
78
     *
79
     * @return array
80
     */
81
    protected function getPendingMigrations()
82
    {
83
        return array_diff(
84
            $this->getMigrationFiles(),
85
            $this->repository->getRanMigrations()
86
        );
87
    }
88
89
    /**
90
     * Get array of migration file names from directory listed in config.
91
     *
92
     * @return array
93
     */
94
    protected function getMigrationFiles()
95
    {
96
        $files = scandir($this->config->getMigrationDirectory());
97
98
        $files = array_filter($files, function ($file) {
99
            return strpos($file, '.php') !== false;
100
        });
101
102
        $files = array_map(function ($file) {
103
            return str_replace('.php', '', $file);
104
        }, $files);
105
106
        return array_values($files);
107
    }
108
109
    /**
110
     * Run pending migrations.
111
     *
112
     * @param array $migrations
113
     *
114
     * @return array
115
     */
116
    protected function runPending(array $migrations)
117
    {
118
        if (count($migrations) === 0) {
119
            $this->log('<info>No pending migrations to run.</info>');
120
121
            return [];
122
        }
123
124
        $batch = $this->repository->getNextBatchNumber();
125
126
        $this->connection->begin();
127
128
        foreach ($migrations as $migration) {
129
            $this->runUp($migration, $batch);
130
        }
131
132
        $this->connection->commit();
133
134
        return $migrations;
135
    }
136
137
    /**
138
     * Run the migration.
139
     *
140
     * @param string $migration
141
     * @param int    $batch
142
     */
143
    protected function runUp($migration, $batch)
144
    {
145
        if ($this->performRun($migration, 'up') === true) {
146
            $this->log("<info>Migrated {$migration}.</info>");
147
148
            $this->repository->insertRecord($migration, $batch);
149
        }
150
    }
151
152
    /**
153
     * Perform a migration run operation.
154
     *
155
     * @param string $migration
156
     * @param string $method
157
     *
158
     * @return bool
159
     */
160
    protected function performRun($migration, $method)
161
    {
162
        $migrationClass = $this->resolveMigrationClass($migration);
163
164
        try {
165
            $migrationClass->$method($this->connection);
166
167
            return true;
168
        } catch (\Exception $e) {
169
            $this->log("<error>{$e->getMessage()}</error>");
170
171
            return false;
172
        }
173
    }
174
175
    /**
176
     * Resolve the migration class from the file name.
177
     *
178
     * @param string $migration
179
     *
180
     * @return Yarak\Migrations\Migration
181
     */
182
    protected function resolveMigrationClass($migration)
183
    {
184
        require_once $this->config->getMigrationDirectory().$migration.'.php';
185
186
        $class = Str::studly(implode('_', array_slice(explode('_', $migration), 4)));
187
188
        return new $class();
189
    }
190
191
    /**
192
     * Rollback migrations.
193
     *
194
     * @param int $steps
195
     *
196
     * @return array
197
     */
198
    public function rollback($steps = 1)
199
    {
200
        $this->setUp();
201
202
        $toRollback = $this->repository->getRanMigrations(null, $steps);
203
204
        return $this->runRollback($toRollback);
205
    }
206
207
    /**
208
     * Rollback given migrations.
209
     *
210
     * @param array $migrations
211
     *
212
     * @return array
213
     */
214
    protected function runRollback(array $migrations)
215
    {
216
        if (count($migrations) === 0) {
217
            $this->log('<info>Nothing to rollback.</info>');
218
219
            return [];
220
        }
221
222
        $this->connection->begin();
223
224
        foreach (array_reverse($migrations) as $migration) {
225
            $this->runDown($migration);
226
        }
227
228
        $this->connection->commit();
229
230
        return $migrations;
231
    }
232
233
    /**
234
     * Rollback the migration.
235
     *
236
     * @param string $migration
237
     */
238
    protected function runDown($migration)
239
    {
240
        if ($this->performRun($migration, 'down') === true) {
241
            $this->log("<info>Rolled back {$migration}.</info>");
242
243
            $this->repository->deleteRecord($migration);
244
        }
245
    }
246
247
    /**
248
     * Reset the database by rolling back all migrations.
249
     *
250
     * @return array
251
     */
252
    public function reset()
253
    {
254
        $this->setUp();
255
256
        $toRollback = $this->repository->getRanMigrations();
257
258
        return $this->runRollback($toRollback);
259
    }
260
261
    /**
262
     * Reset the database and run all migrations.
263
     *
264
     * @return array
265
     */
266
    public function refresh()
267
    {
268
        $this->setUp();
269
270
        $toRollback = $this->repository->getRanMigrations();
271
272
        $this->runRollback($toRollback);
273
274
        $pendingMigrations = $this->getPendingMigrations();
275
276
        return $this->runPending($pendingMigrations);
277
    }
278
279
    /**
280
     * Perform setup procedures for migrations.
281
     */
282
    protected function setUp()
283
    {
284
        if (!$this->connection) {
285
            $this->setConnection();
286
        }
287
288
        $this->createMigrationsRepository();
289
290
        $this->makeDirectoryStructure($this->config->getAllDatabaseDirectories());
291
    }
292
293
    /**
294
     * Set connection to database on object.
295
     *
296
     * @return $this
297
     */
298
    public function setConnection()
299
    {
300
        $dbConfig = $this->config->get('database');
301
302
        $this->connection = $this->resolver->getConnection($dbConfig);
303
304
        $this->repository->setConnection($this->connection);
305
306
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Yarak\Migrations\FileDate\FileDateMigrator) is incompatible with the return type declared by the interface Yarak\Migrations\Migrator::setConnection of type Yarak\Migrations\Pdo.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
307
    }
308
309
    /**
310
     * Return the connection.
311
     *
312
     * @return \Phalcon\Db\Adapter\Pdo
313
     */
314
    public function getConnection()
315
    {
316
        return $this->connection;
317
    }
318
319
    /**
320
     * Create the migrations table if it doesn't exist.
321
     */
322
    protected function createMigrationsRepository()
323
    {
324
        if (!$this->repository->exists()) {
325
            $this->repository->create();
326
        }
327
328
        return $this;
329
    }
330
}
331