d3508943a2
fix typos
501 lines
16 KiB
PHP
501 lines
16 KiB
PHP
<?php
|
|
|
|
namespace App\Service;
|
|
|
|
use App\Entity\Bible;
|
|
use App\Entity\Note;
|
|
use App\Entity\Reference;
|
|
use App\Entity\Series;
|
|
use App\Entity\Speaker;
|
|
use App\Entity\Template;
|
|
use App\Entity\User;
|
|
use Doctrine\ORM\Tools\SchemaTool;
|
|
use Doctrine\Persistence\ManagerRegistry;
|
|
use PDO;
|
|
use PDOException;
|
|
use Symfony\Component\Uid\Uuid;
|
|
|
|
class DatabaseTransferService
|
|
{
|
|
private PDO $srcDB;
|
|
private PDO $destDB;
|
|
|
|
public function __construct(
|
|
private string $projectDir,
|
|
private ManagerRegistry $doctrine
|
|
) {
|
|
// connect the source database
|
|
switch (explode(":", $_ENV['DATABASE_URL'])[0]) {
|
|
case 'pdo-sqlite':
|
|
list($driverName, $dbFile) = explode(":/", $_ENV['DATABASE_URL']);
|
|
$this->srcDB = new PDO('sqlite:'.$dbFile);
|
|
break;
|
|
case 'pdo-mysql':
|
|
case 'pdo-pgsql':
|
|
$dsn = $this->convertDBURLString($_ENV['DATABASE_URL']);
|
|
$this->srcDB = new PDO($dsn['dsn'], $dsn['username'], $dsn['password']);
|
|
}
|
|
|
|
// connect the destination database
|
|
switch(explode(':', $_ENV['XFER_DATABASE_URL'])[0]) {
|
|
case 'pdo-sqlite':
|
|
list($driverName, $dbFile) = explode(":/", $_ENV['XFER_DATABASE']);
|
|
$this->destDB = new PDO('sqlite:'.$dbFile);
|
|
break;
|
|
case 'pdo-mysql':
|
|
case 'pdo-pgsql':
|
|
$dsn = $this->convertDBURLString($_ENV['XFER_DATABASE_URL']);
|
|
$this->destDB = new PDO($dsn['dsn'], $dsn['username'], $dsn['password']);
|
|
}
|
|
}
|
|
|
|
public function createSchema(): void
|
|
{
|
|
$destEm = $this->doctrine->getManager('transfer');
|
|
$metadata = $destEm->getMetadataFactory()->getAllMetadata();
|
|
$tool = new SchemaTool($destEm);
|
|
|
|
$tool->dropSchema($metadata);
|
|
$tool->createSchema($metadata);
|
|
}
|
|
|
|
public function getEntityClasses(): array
|
|
{
|
|
return [
|
|
User::class,
|
|
Bible::class,
|
|
Reference::class,
|
|
Template::class,
|
|
Series::class,
|
|
Speaker::class,
|
|
Note::class
|
|
];
|
|
}
|
|
|
|
public function transferUserTable(): int
|
|
{
|
|
$sql = "SELECT * FROM user";
|
|
$stmt = $this->srcDB->prepare($sql);
|
|
$stmt->execute();
|
|
$insQuery = "INSERT INTO user (id, email, roles, password, name, meta_data, home_church_rss) ".
|
|
"VALUES ".
|
|
"(:id, :email, :roles, :password, :name, :meta_data, :home_church)";
|
|
$destStmt = $this->destDB->prepare($insQuery);
|
|
$skippedCount = 0;
|
|
|
|
$this->destDB->beginTransaction();
|
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$params = [
|
|
'id' => $row['id'],
|
|
'email' => $row['email'],
|
|
'roles' => $row['roles'],
|
|
'password' => $row['password'],
|
|
'name' => $row['name'],
|
|
'meta_data' => $row['meta_data'],
|
|
'home_church' => $row['home_church_rss'],
|
|
];
|
|
|
|
$this->destDB->exec("SAVEPOINT row_insert");
|
|
|
|
try {
|
|
$destStmt->execute($params);
|
|
|
|
$this->destDB->exec("RELEASE SAVEPOINT row_insert");
|
|
} catch (PDOException $e) {
|
|
$skipCodes = [
|
|
'23000',
|
|
'23503',
|
|
'23505',
|
|
];
|
|
if (in_array($e->getCode(), $skipCodes, true)) {
|
|
$this->destDB->exec("ROLLBACK TO SAVEPOINT row_insert");
|
|
$skippedCount++; // 4. Increment the counter
|
|
continue;
|
|
}
|
|
|
|
$this->destDB->rollBack();
|
|
print_r($params);
|
|
die("Critical Error: ".$e->getMessage());
|
|
}
|
|
}
|
|
|
|
$this->destDB->commit();
|
|
|
|
return $skippedCount;
|
|
}
|
|
|
|
public function transferBibleTable(): int
|
|
{
|
|
$sql = "SELECT * FROM bible";
|
|
$stmt = $this->srcDB->prepare($sql);
|
|
$stmt->execute();
|
|
$insQuery = "INSERT INTO bible (id, book, chapter, verse, content, book_index, label) ".
|
|
"VALUES ".
|
|
"(:id, :book, :chapter, :verse, :content, :book_index, :label)";
|
|
$destStmt = $this->destDB->prepare($insQuery);
|
|
$skippedCount = 0;
|
|
|
|
$this->destDB->beginTransaction();
|
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$uuid = Uuid::fromString($row['id']);
|
|
|
|
$params = [
|
|
'id' => $uuid->toBinary(),
|
|
'book' => $row['book'],
|
|
'chapter' => $row['chapter'],
|
|
'verse' => $row['verse'],
|
|
'content' => $row['content'],
|
|
'book_index' => $row['book_index'],
|
|
'label' => $row['label'],
|
|
];
|
|
|
|
$this->destDB->exec("SAVEPOINT row_insert");
|
|
|
|
try {
|
|
$destStmt->execute($params);
|
|
|
|
$this->destDB->exec("RELEASE SAVEPOINT row_insert");
|
|
} catch (PDOException $e) {
|
|
$skipCodes = [
|
|
'23000',
|
|
'23503',
|
|
'23505',
|
|
];
|
|
if (in_array($e->getCode(), $skipCodes, true)) {
|
|
$this->destDB->exec("ROLLBACK TO SAVEPOINT row_insert");
|
|
$skippedCount++; // 4. Increment the counter
|
|
continue;
|
|
}
|
|
|
|
$this->destDB->rollBack();
|
|
print_r($params);
|
|
die("Critical Error: ".$e->getMessage());
|
|
}
|
|
}
|
|
$this->destDB->commit();
|
|
|
|
return $skippedCount;
|
|
}
|
|
|
|
public function transferReferenceTable(): int
|
|
{
|
|
$sql = "SELECT * FROM reference";
|
|
$stmt = $this->srcDB->prepare($sql);
|
|
$stmt->execute();
|
|
$insQuery = "INSERT INTO reference (id, type, name, label, ndx, content) ".
|
|
"VALUES ".
|
|
"(:id, :type, :name, :label, :ndx, :content)";
|
|
$destStmt = $this->destDB->prepare($insQuery);
|
|
$skippedCount = 0;
|
|
|
|
$this->destDB->beginTransaction();
|
|
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$params = [
|
|
'id' => $row['id'],
|
|
'type' => $row['type'],
|
|
'name' => $row['name'],
|
|
'label' => $row['label'],
|
|
'ndx' => $row['ndx'],
|
|
'content' => $row['content'],
|
|
];
|
|
|
|
$this->destDB->exec("SAVEPOINT row_insert");
|
|
|
|
try {
|
|
$destStmt->execute($params);
|
|
|
|
$this->destDB->exec("RELEASE SAVEPOINT row_insert");
|
|
} catch (PDOException $e) {
|
|
$skipCodes = [
|
|
'23000',
|
|
'23503',
|
|
'23505',
|
|
];
|
|
if (in_array($e->getCode(), $skipCodes, true)) {
|
|
$this->destDB->exec("ROLLBACK TO SAVEPOINT row_insert");
|
|
$skippedCount++; // 4. Increment the counter
|
|
continue;
|
|
}
|
|
|
|
$this->destDB->rollBack();
|
|
print_r($params);
|
|
die("Critical Error: ".$e->getMessage());
|
|
}
|
|
}
|
|
|
|
$this->destDB->commit();
|
|
|
|
return $skippedCount;
|
|
}
|
|
|
|
public function transferTemplateTable(): int
|
|
{
|
|
$sql = "SELECT * FROM template";
|
|
$stmt = $this->srcDB->prepare($sql);
|
|
$stmt->execute();
|
|
$insQuery = "INSERT INTO template (id, user_id, name, content) ".
|
|
"VALUES ".
|
|
"(:id, :user_id, :name, :content)";
|
|
$destStmt = $this->destDB->prepare($insQuery);
|
|
$skippedCount = 0;
|
|
|
|
$this->destDB->beginTransaction();
|
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$params = [
|
|
'id' => $row['id'],
|
|
'user_id' => $row['user_id'],
|
|
'name' => $row['name'],
|
|
'content' => $row['content'],
|
|
];
|
|
|
|
$this->destDB->exec("SAVEPOINT row_insert");
|
|
|
|
try {
|
|
$destStmt->execute($params);
|
|
|
|
$this->destDB->exec("RELEASE SAVEPOINT row_insert");
|
|
} catch (PDOException $e) {
|
|
$skipCodes = [
|
|
'23000',
|
|
'23503',
|
|
'23505',
|
|
];
|
|
if (in_array($e->getCode(), $skipCodes, true)) {
|
|
$this->destDB->exec("ROLLBACK TO SAVEPOINT row_insert");
|
|
$skippedCount++; // 4. Increment the counter
|
|
continue;
|
|
}
|
|
|
|
$this->destDB->rollBack();
|
|
print_r($params);
|
|
die("Critical Error: ".$e->getMessage());
|
|
}
|
|
}
|
|
|
|
$this->destDB->commit();
|
|
|
|
return $skippedCount;
|
|
}
|
|
|
|
public function transferSpeakerTable(): int
|
|
{
|
|
$sql = "SELECT * FROM speaker";
|
|
$stmt = $this->srcDB->prepare($sql);
|
|
$stmt->execute();
|
|
$insQuery = "INSERT INTO speaker (id, name, user_id) ".
|
|
"VALUES ".
|
|
"(:id, :name, :user_id)";
|
|
$destStmt = $this->destDB->prepare($insQuery);
|
|
$skippedCount = 0;
|
|
|
|
$this->destDB->beginTransaction();
|
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$params = [
|
|
'id' => $row['id'],
|
|
'name' => $row['name'],
|
|
'user_id' => $row['user_id'],
|
|
];
|
|
|
|
$this->destDB->exec("SAVEPOINT row_insert");
|
|
|
|
try {
|
|
$destStmt->execute($params);
|
|
|
|
$this->destDB->exec("RELEASE SAVEPOINT row_insert");
|
|
} catch (PDOException $e) {
|
|
$skipCodes = [
|
|
'23000',
|
|
'23503',
|
|
'23505',
|
|
];
|
|
if (in_array($e->getCode(), $skipCodes, true)) {
|
|
$this->destDB->exec("ROLLBACK TO SAVEPOINT row_insert");
|
|
$skippedCount++; // 4. Increment the counter
|
|
continue;
|
|
}
|
|
|
|
$this->destDB->rollBack();
|
|
print_r($params);
|
|
die("Critical Error: ".$e->getMessage());
|
|
}
|
|
}
|
|
|
|
$this->destDB->commit();
|
|
|
|
return $skippedCount;
|
|
}
|
|
|
|
public function transferSeriesTable(): int
|
|
{
|
|
$sql = "SELECT * FROM series";
|
|
$stmt = $this->srcDB->prepare($sql);
|
|
$stmt->execute();
|
|
$insQuery = "INSERT INTO series (id, name, user_id, template_id) ".
|
|
"VALUES ".
|
|
"(:id, :name, :user_id, :template_id)";
|
|
$destStmt = $this->destDB->prepare($insQuery);
|
|
$skippedCount = 0;
|
|
|
|
$this->destDB->beginTransaction();
|
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$params = [
|
|
'id' => $row['id'],
|
|
'name' => $row['name'],
|
|
'user_id' => $row['user_id'],
|
|
'template_id' => $row['template_id'],
|
|
];
|
|
|
|
$this->destDB->exec("SAVEPOINT row_insert");
|
|
|
|
try {
|
|
$destStmt->execute($params);
|
|
|
|
$this->destDB->exec("RELEASE SAVEPOINT row_insert");
|
|
} catch (PDOException $e) {
|
|
$skipCodes = [
|
|
'23000',
|
|
'23503',
|
|
'23505',
|
|
];
|
|
if (in_array($e->getCode(), $skipCodes, true)) {
|
|
$this->destDB->exec("ROLLBACK TO SAVEPOINT row_insert");
|
|
$skippedCount++; // 4. Increment the counter
|
|
continue;
|
|
}
|
|
|
|
$this->destDB->rollBack();
|
|
print_r($params);
|
|
die("Critical Error: ".$e->getMessage());
|
|
}
|
|
}
|
|
|
|
$this->destDB->commit();
|
|
|
|
return $skippedCount;
|
|
}
|
|
|
|
public function transferNoteTable(): int
|
|
{
|
|
$sql = "SELECT * FROM note";
|
|
$stmt = $this->srcDB->prepare($sql);
|
|
$stmt->execute();
|
|
$insQuery = "INSERT INTO note (id, title, date, passage, refs, text, speaker_id, series_id, user_id, recording) ".
|
|
"VALUES ".
|
|
"(:id, :title, :date, :passage, :refs, :text, :speaker_id, :series_id, :user_id, :recording)";
|
|
$destStmt = $this->destDB->prepare($insQuery);
|
|
$skippedCount = 0;
|
|
|
|
$this->destDB->beginTransaction();
|
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$params = [
|
|
'id' => $row['id'],
|
|
'speaker_id' => $row['speaker_id'],
|
|
'series_id' => $row['series_id'],
|
|
'user_id' => $row['user_id'],
|
|
'title' => $row['title'],
|
|
'date' => $row['date'],
|
|
'passage' => $row['passage'],
|
|
'refs' => $row['refs'],
|
|
'text' => $row['text'],
|
|
'recording' => $row['recording'],
|
|
];
|
|
|
|
$this->destDB->exec("SAVEPOINT row_insert");
|
|
|
|
try {
|
|
$destStmt->execute($params);
|
|
|
|
$this->destDB->exec("RELEASE SAVEPOINT row_insert");
|
|
} catch (PDOException $e) {
|
|
$skipCodes = [
|
|
'23000',
|
|
'23503',
|
|
'23505',
|
|
];
|
|
if (in_array($e->getCode(), $skipCodes, true)) {
|
|
$this->destDB->exec("ROLLBACK TO SAVEPOINT row_insert");
|
|
$skippedCount++; // 4. Increment the counter
|
|
continue;
|
|
}
|
|
|
|
$this->destDB->rollBack();
|
|
print_r($params);
|
|
die("Critical Error: ".$e->getMessage());
|
|
}
|
|
}
|
|
|
|
$this->destDB->commit();
|
|
|
|
return $skippedCount;
|
|
}
|
|
|
|
public function finalizeEnvSwap(): void
|
|
{
|
|
$envPath = $this->projectDir . '/.env';
|
|
|
|
if (!file_exists($envPath)) {
|
|
throw new \Exception(".env file not found at $envPath");
|
|
}
|
|
|
|
$lines = file($envPath);
|
|
$newLines = [];
|
|
$xferUrlValue = null;
|
|
|
|
// 1. First pass: Find the value of XFER_DATABASE_URL
|
|
foreach ($lines as $line) {
|
|
if (preg_match('/^XFER_DATABASE_URL=(.*)/', trim($line), $matches)) {
|
|
$xferUrlValue = $matches[1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$xferUrlValue) {
|
|
throw new \Exception("XFER_DATABASE_URL not found in .env file.");
|
|
}
|
|
|
|
// 2. Second pass: Perform the swap
|
|
foreach ($lines as $line) {
|
|
$trimmed = trim($line);
|
|
|
|
// Prepend #OLD_ to the existing DATABASE_URL
|
|
if (preg_match('/^DATABASE_URL=/', $trimmed)) {
|
|
$newLines[] = "# OLD_" . $line;
|
|
continue;
|
|
}
|
|
|
|
// Rename XFER_DATABASE_URL to DATABASE_URL
|
|
if (preg_match('/^XFER_DATABASE_URL=/', $trimmed)) {
|
|
$newLines[] = "DATABASE_URL=" . $xferUrlValue . PHP_EOL;
|
|
continue;
|
|
}
|
|
|
|
$newLines[] = $line;
|
|
}
|
|
|
|
file_put_contents($envPath, implode('', $newLines));
|
|
}
|
|
|
|
public static function isTransferEnabled(): bool
|
|
{
|
|
return (isset($_ENV['XFER_DATABASE_URL']) && !empty($_ENV['XFER_DATABASE_URL']));
|
|
}
|
|
|
|
private function convertDBURLString(string $DBURL): array
|
|
{
|
|
$dsn = preg_split("/[:|\/|\@]+/", $DBURL);
|
|
if (count($dsn) > 6) {
|
|
die('You cannot have special characters in your password for the database entry during this process. Change the database user password to just use alpha-numeric characters, then run again. You can change it back to more secure after this transfer is complete');
|
|
}
|
|
|
|
$res = [
|
|
'dsn' => substr($dsn[0], 4).':'.
|
|
'host='.$dsn[3].';'.
|
|
'port='.$dsn[4].';'.
|
|
'dbname='.$dsn[5],
|
|
'username' => $dsn[1],
|
|
'password' => $dsn[2]
|
|
];
|
|
|
|
return $res;
|
|
}
|
|
} |