Skip to content

Commit 1fb0bbf

Browse files
committed
maintenance: remove duplication in migrator classes
1 parent 70ea3ba commit 1fb0bbf

3 files changed

Lines changed: 150 additions & 235 deletions

File tree

src/Migration/AbstractMigrator.php

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
namespace Gt\Database\Migration;
3+
4+
use Gt\Database\Connection\Settings;
5+
use Gt\Database\Database;
6+
use SplFileInfo;
7+
use SplFileObject;
8+
9+
abstract class AbstractMigrator {
10+
const string STREAM_OUT = "out";
11+
const string STREAM_ERROR = "error";
12+
13+
protected ?SplFileObject $streamError = null;
14+
protected ?SplFileObject $streamOut = null;
15+
16+
protected string $driver;
17+
protected Database $dbClient;
18+
protected string $path;
19+
protected string $tableName;
20+
protected Settings $settings;
21+
22+
public function __construct(
23+
Settings $settings,
24+
string $path,
25+
?string $tableName = null
26+
) {
27+
$this->settings = clone $settings;
28+
$this->driver = $settings->getDriver();
29+
$this->path = $path;
30+
$this->tableName = $tableName ?? $this->getDefaultTableName();
31+
32+
if($this->driver !== Settings::DRIVER_SQLITE) {
33+
$settings = $settings->withoutSchema();
34+
}
35+
36+
$this->dbClient = new Database($settings);
37+
}
38+
39+
abstract protected function getDefaultTableName():string;
40+
41+
public function setOutput(
42+
SplFileObject $out,
43+
?SplFileObject $error = null
44+
):void {
45+
$this->streamOut = $out;
46+
$this->streamError = $error;
47+
}
48+
49+
/** @return array<string> */
50+
public function getMigrationFileList():array {
51+
if(!is_dir($this->path)) {
52+
return [];
53+
}
54+
55+
$fileList = glob("$this->path/*.sql");
56+
$fileList = array_values(array_filter($fileList, function(string $file):bool {
57+
return preg_match("/^\d+.*\.sql$/", basename($file)) === 1;
58+
}));
59+
sort($fileList);
60+
return $fileList;
61+
}
62+
63+
/** @param array<string> $fileList */
64+
public function checkFileListOrder(array $fileList):void {
65+
$previousNumber = null;
66+
67+
foreach($fileList as $file) {
68+
$migrationNumber = $this->extractNumberFromFilename($file);
69+
70+
if(!is_null($previousNumber)) {
71+
if($migrationNumber === $previousNumber) {
72+
throw new MigrationSequenceOrderException("Duplicate: $migrationNumber");
73+
}
74+
if($migrationNumber < $previousNumber) {
75+
throw new MigrationSequenceOrderException("Out of order: $migrationNumber before $previousNumber");
76+
}
77+
if($migrationNumber !== $previousNumber + 1) {
78+
throw new MigrationSequenceOrderException("Gap: $previousNumber before $migrationNumber");
79+
}
80+
}
81+
elseif($migrationNumber !== 1) {
82+
throw new MigrationSequenceOrderException("Gap: expected 1, got $migrationNumber");
83+
}
84+
85+
$previousNumber = $migrationNumber;
86+
}
87+
}
88+
89+
public function extractNumberFromFilename(string $pathName):int {
90+
$file = new SplFileInfo($pathName);
91+
$filename = $file->getFilename();
92+
preg_match("/^(\d+)-?.*\.sql$/", $filename, $matches);
93+
94+
if(!isset($matches[1])) {
95+
throw new MigrationFileNameFormatException($filename);
96+
}
97+
98+
return (int)$matches[1];
99+
}
100+
101+
protected function executeSqlFile(string $file):string {
102+
$md5 = md5_file($file);
103+
$sqlStatementSplitter = new SqlStatementSplitter();
104+
105+
foreach($sqlStatementSplitter->split(file_get_contents($file)) as $sql) {
106+
$this->dbClient->executeSql($sql);
107+
}
108+
109+
return $md5;
110+
}
111+
112+
protected function nowExpression():string {
113+
if($this->driver === Settings::DRIVER_SQLITE) {
114+
return "datetime('now')";
115+
}
116+
117+
return "now()";
118+
}
119+
120+
protected function output(
121+
string $message,
122+
string $streamName = self::STREAM_OUT
123+
):void {
124+
$stream = $this->streamOut ?? null;
125+
if($streamName === self::STREAM_ERROR) {
126+
$stream = $this->streamError;
127+
}
128+
129+
if(is_null($stream)) {
130+
return;
131+
}
132+
133+
$stream->fwrite($message . PHP_EOL);
134+
}
135+
}

src/Migration/DevMigrator.php

Lines changed: 5 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,13 @@
11
<?php /** @noinspection SqlNoDataSourceInspection */
22
namespace Gt\Database\Migration;
33

4-
use Gt\Database\Connection\Settings;
5-
use Gt\Database\Database;
6-
use SplFileInfo;
7-
use SplFileObject;
8-
9-
class DevMigrator {
4+
class DevMigrator extends AbstractMigrator {
105
const string COLUMN_FILE_NAME = "fileName";
116
const string COLUMN_QUERY_HASH = "queryHash";
127
const string COLUMN_MIGRATED_AT = "migratedAt";
138

14-
const string STREAM_OUT = "out";
15-
const string STREAM_ERROR = "error";
16-
17-
protected ?SplFileObject $streamError = null;
18-
protected ?SplFileObject $streamOut = null;
19-
20-
protected string $driver;
21-
protected Database $dbClient;
22-
protected string $path;
23-
protected string $tableName;
24-
25-
public function __construct(
26-
Settings $settings,
27-
string $path,
28-
string $tableName = "_migration_dev"
29-
) {
30-
$this->driver = $settings->getDriver();
31-
$this->path = $path;
32-
$this->tableName = $tableName;
33-
34-
if($this->driver !== Settings::DRIVER_SQLITE) {
35-
$settings = $settings->withoutSchema();
36-
}
37-
38-
$this->dbClient = new Database($settings);
39-
}
40-
41-
public function setOutput(
42-
SplFileObject $out,
43-
?SplFileObject $error = null
44-
):void {
45-
$this->streamOut = $out;
46-
$this->streamError = $error;
9+
protected function getDefaultTableName():string {
10+
return "_migration_dev";
4711
}
4812

4913
public function createMigrationTable():void {
@@ -55,46 +19,6 @@ public function createMigrationTable():void {
5519
]));
5620
}
5721

58-
/** @return array<string> */
59-
public function getMigrationFileList():array {
60-
if(!is_dir($this->path)) {
61-
return [];
62-
}
63-
64-
$fileList = glob("$this->path/*.sql");
65-
$fileList = array_values(array_filter($fileList, function(string $file):bool {
66-
return preg_match("/^\d+.*\.sql$/", basename($file)) === 1;
67-
}));
68-
sort($fileList);
69-
return $fileList;
70-
}
71-
72-
/** @param array<string> $fileList */
73-
public function checkFileListOrder(array $fileList):void {
74-
$previousNumber = null;
75-
76-
foreach($fileList as $file) {
77-
$migrationNumber = $this->extractNumberFromFilename($file);
78-
79-
if(!is_null($previousNumber)) {
80-
if($migrationNumber === $previousNumber) {
81-
throw new MigrationSequenceOrderException("Duplicate: $migrationNumber");
82-
}
83-
if($migrationNumber < $previousNumber) {
84-
throw new MigrationSequenceOrderException("Out of order: $migrationNumber before $previousNumber");
85-
}
86-
if($migrationNumber !== $previousNumber + 1) {
87-
throw new MigrationSequenceOrderException("Gap: $previousNumber before $migrationNumber");
88-
}
89-
}
90-
elseif($migrationNumber !== 1) {
91-
throw new MigrationSequenceOrderException("Gap: expected 1, got $migrationNumber");
92-
}
93-
94-
$previousNumber = $migrationNumber;
95-
}
96-
}
97-
9822
/** @param array<string> $migrationFileList */
9923
public function checkIntegrity(array $migrationFileList):void {
10024
foreach($migrationFileList as $file) {
@@ -108,22 +32,9 @@ public function checkIntegrity(array $migrationFileList):void {
10832
}
10933
}
11034

111-
public function extractNumberFromFilename(string $pathName):int {
112-
$file = new SplFileInfo($pathName);
113-
$filename = $file->getFilename();
114-
preg_match("/^(\d+)-?.*\.sql$/", $filename, $matches);
115-
116-
if(!isset($matches[1])) {
117-
throw new MigrationFileNameFormatException($filename);
118-
}
119-
120-
return (int)$matches[1];
121-
}
122-
12335
/** @param array<string> $migrationFileList */
12436
public function performMigration(array $migrationFileList):int {
12537
$numCompleted = 0;
126-
$sqlStatementSplitter = new SqlStatementSplitter();
12738

12839
foreach($migrationFileList as $file) {
12940
$fileName = basename($file);
@@ -133,12 +44,7 @@ public function performMigration(array $migrationFileList):int {
13344

13445
$fileNumber = $this->extractNumberFromFilename($file);
13546
$this->output("Dev migration $fileNumber: `$file`.");
136-
$md5 = md5_file($file);
137-
138-
foreach($sqlStatementSplitter->split(file_get_contents($file)) as $sql) {
139-
$this->dbClient->executeSql($sql);
140-
}
141-
47+
$md5 = $this->executeSqlFile($file);
14248
$this->recordMigrationSuccess($fileName, $md5);
14349
$numCompleted++;
14450
}
@@ -234,11 +140,7 @@ protected function createMergedFilename(string $fileName, int $number):string {
234140
}
235141

236142
protected function recordMigrationSuccess(string $fileName, string $hash):void {
237-
$now = "now()";
238-
239-
if($this->driver === Settings::DRIVER_SQLITE) {
240-
$now = "datetime('now')";
241-
}
143+
$now = $this->nowExpression();
242144

243145
$this->dbClient->executeSql(implode("\n", [
244146
"insert into `{$this->tableName}` (",
@@ -257,20 +159,4 @@ protected function deleteMigrationRecord(string $fileName):void {
257159
"where `" . self::COLUMN_FILE_NAME . "` = ?",
258160
]), [$fileName]);
259161
}
260-
261-
protected function output(
262-
string $message,
263-
string $streamName = self::STREAM_OUT
264-
):void {
265-
$stream = $this->streamOut ?? null;
266-
if($streamName === self::STREAM_ERROR) {
267-
$stream = $this->streamError;
268-
}
269-
270-
if(is_null($stream)) {
271-
return;
272-
}
273-
274-
$stream->fwrite($message . PHP_EOL);
275-
}
276162
}

0 commit comments

Comments
 (0)