Passed
Pull Request — master (#30)
by Mathieu
19:39 queued 09:38
created

MigrationService   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 141
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 74
c 5
b 0
f 0
dl 0
loc 141
rs 10
wmc 20

6 Methods

Rating   Name   Duplication   Size   Complexity  
A listMigrations() 0 19 4
A registerMigration() 0 4 1
A createMigration() 0 46 5
A scanForMigrations() 0 11 4
A doMigrations() 0 35 5
A initMigrationTable() 0 7 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Suricate\Migrations;
6
7
use Exception;
8
use Suricate\Interfaces\IMigration;
9
use Suricate\Service;
10
use Suricate\Suricate;
11
12
/**
13
 * DB Migration extension for Suricate
14
 *
15
 * @package Suricate
16
 * @author  Mathieu LESNIAK <[email protected]>
17
 *
18
 */
19
20
class MigrationService extends Service
21
{
22
    protected array $registeredMigrations = [];
23
24
    public function registerMigration(IMigration $migration)
25
    {
26
        // FIXME: test with migration outside of migration folder (eg: media module )
27
        $this->registeredMigrations[$migration->getName()] = $migration->getSQL();
28
    }
29
30
    public function scanForMigrations()
31
    {
32
        $files = glob(app_path('migrations/*.php'));
33
        foreach ($files as $file) {
34
            $migrationClassName = str_replace('.php', '', basename($file));
35
            include($file);
36
            // Class is defined inside the file
37
            if (class_exists($migrationClassName)) {
38
                $migration = new $migrationClassName();
39
                if ($migration instanceof IMigration) {
40
                    $this->registerMigration($migration);
41
                }
42
            }
43
            
44
        }
45
    }
46
47
    public function initMigrationTable(): int
48
    {
49
        $this->scanForMigrations();
50
        $migrationModel = new MigrationModel();
51
52
53
        return $migrationModel->createMigrationTable();
54
    }
55
56
    public function listMigrations()
57
    {
58
        $this->scanForMigrations();
59
60
        $migrations = MigrationModelList::loadAll();
61
        $alreadyMigrated = [];
62
        $result = [];
63
        foreach ($migrations as $migration) {
64
            $alreadyMigrated[$migration->name] = true;
65
            $result[$migration->name] = $migration->date_added;
66
        }
67
        foreach (array_keys($this->registeredMigrations) as $regMigrationName) {
68
            if (isset($alreadyMigrated[$regMigrationName])) {
69
                continue;
70
            }
71
            $result[$regMigrationName] = false;
72
        }
73
        ksort($result);
74
        return $result;
75
    }
76
77
    public function doMigrations()
78
    {
79
        echo "[Migration] Starting migrations\n";
80
81
        $migrations = $this->listMigrations();
82
        $migrationsToDo = array_filter($migrations, function ($item) {
83
            return $item === false;
84
        });
85
86
        if (count($migrationsToDo) === 0) {
87
            echo "[Migration] Nothing to migrate\n";
88
            return true;
89
        }
90
        foreach (array_keys($migrationsToDo) as $migrationName) {
91
            echo "[Migration] Migration $migrationName:\n";
92
            $migration = new $migrationName();
93
            $sql = trim($migration->getSQL());
94
            if ($sql === '') {
95
                // Ignore
96
                continue;
97
            }
98
            $db = Suricate::Database(true);
99
            $db->setConfig($migration->getConfigName());
100
            try {
101
                $db->query($migration->getSQL());
102
            } catch (Exception $e) {
103
                echo "[Migration] ❌ Failed to execute migration: ".$e->getMessage() . "\n";
104
                continue;
105
            }
106
107
            echo "[Migration] ✅ migration OK\n";
108
            $migrationCheck = new MigrationModel();
109
            $migrationCheck->setDBConfig($migration->getConfigName());
110
            $migrationCheck->name = $migration->getName();
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on Suricate\Migrations\MigrationModel. Since you implemented __set, consider adding a @property annotation.
Loading history...
111
            $migrationCheck->save();
112
        }
113
    }
114
115
    public function createMigration(): string|bool
116
    {
117
        $migrationName = 'v' . date('Ymdhis');
118
119
        $template = <<<EOD
120
<?php
121
122
use Suricate\Interfaces\IMigration;
123
124
class {$migrationName} implements IMigration
125
{
126
    public function getName(): string
127
    {
128
        return __CLASS__;
129
    }
130
131
    public function getSQL(): string
132
    {
133
        return '';
134
    }
135
136
    // Leave empty string if you want to use default config name
137
    public function getConfigName(): string
138
    {
139
        return '';
140
    }
141
}
142
EOD;
143
        $filename = app_path('migrations/' . $migrationName . '.php');
144
        $directory = pathinfo($filename, PATHINFO_DIRNAME);
145
        if (!is_dir($directory)) {
0 ignored issues
show
Bug introduced by
It seems like $directory can also be of type array; however, parameter $filename of is_dir() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

145
        if (!is_dir(/** @scrutinizer ignore-type */ $directory)) {
Loading history...
146
            $ret = mkdir($directory, 0755, true);
0 ignored issues
show
Bug introduced by
It seems like $directory can also be of type array; however, parameter $directory of mkdir() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

146
            $ret = mkdir(/** @scrutinizer ignore-type */ $directory, 0755, true);
Loading history...
147
            if (!$ret) {
148
                return false;
149
            }
150
        }
151
        $fp = fopen($filename, 'w');
152
        if ($fp === false) {
153
            return false;
154
        }
155
        $ret = fputs($fp, $template);
156
        fclose($fp);
157
        if ($ret !== false) {
158
            return $migrationName;
159
        }
160
        return false;
161
    }
162
}
163