286 lines
10 KiB
PHP
286 lines
10 KiB
PHP
<?php
|
|
|
|
namespace Database\migrations;
|
|
|
|
use App\Exceptions\AbortRowImportException;
|
|
use Exception;
|
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
use Illuminate\Database\Migrations\Migration;
|
|
use Illuminate\Support\Facades\DB;
|
|
use ReflectionFunction;
|
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
|
use Symfony\Component\Console\Terminal;
|
|
|
|
/**
|
|
* Migration Intructions
|
|
* @author Jérémy Munsch <jeremy.munsch@ldc.fr>
|
|
* Examples
|
|
*[
|
|
* 'content_ocb' => [
|
|
* 'model' => OCBContent::class,
|
|
* 'bind' => [
|
|
* 'id' => 'ocb_id',
|
|
* 'picture' => 'ocb_img',
|
|
* 'content' => 'ocb_content',
|
|
* 'status' => 'ocb_is_published',
|
|
* 'type' => [
|
|
* 'apply' => function ($value) {
|
|
* switch ($value) {
|
|
* case 'VOLAILLE':
|
|
* return OCBType::poultry();
|
|
* case 'LAPIN':
|
|
* return OCBType::rabbit();
|
|
* }
|
|
* },
|
|
* 'name' => 'ocb_type',
|
|
* 'default' => ''
|
|
* ],
|
|
* 'order' => 'ocb_position',
|
|
* 'created_at' => [
|
|
* 'name' => '',
|
|
* 'default' => date('Y-m-d H:i:s')
|
|
* ],
|
|
* 'updated_at' => [
|
|
* 'name' => '',
|
|
* 'default' => date('Y-m-d H:i:s')
|
|
* ]
|
|
* ]
|
|
* ],
|
|
* 'Recipe ratings' => [
|
|
* 'table' => 'recipe',
|
|
* 'comment' => 'Add ratings to recipes',
|
|
* 'model' => Rating::class,
|
|
* 'bind' => [
|
|
* 'anyprop' => [
|
|
* 'apply' => function ($recipe_id) {
|
|
* $recipe_rating = $this->oldDB->table('recipe_rating')
|
|
* ->select('recipe_rating_value')
|
|
* ->where('recipe_rating_recipe_id', '=', $recipe_id)
|
|
* ->get();
|
|
* $count = $recipe_rating->count();
|
|
* $sum = $recipe_rating->sum(function ($obj) {
|
|
* return $obj->recipe_rating_value;
|
|
* });
|
|
* $this->newDB->table('ratings')
|
|
* ->insert([
|
|
* 'total' => $sum,
|
|
* 'count' => $count,
|
|
* 'rating' => $count ? floatval($sum / $count) : 0,
|
|
* 'recipes_id' => $recipe_id,
|
|
* 'created_at' => date('Y-m-d H:i:s'),
|
|
* 'updated_at' => date('Y-m-d H:i:s'),
|
|
* ]);
|
|
* throw new AbortRowImportException();
|
|
* },
|
|
* 'name' => 'recipe_id'
|
|
* ]
|
|
* ]
|
|
* ],
|
|
*]
|
|
*
|
|
* @param array $instructions
|
|
* @return void
|
|
*/
|
|
|
|
|
|
/**
|
|
* Helps to migration from legaulois2016 to legaulois2020
|
|
* @author Jérémy Munsch <jeremy.munsch@ldc.fr>
|
|
*/
|
|
abstract class LeGaulois2016Migration extends Migration
|
|
{
|
|
|
|
/** @var ConsoleOutput */
|
|
private $output = null;
|
|
|
|
/** @var ConsoleSectionOutput */
|
|
private $outputSection = null;
|
|
|
|
/** @var \Illuminate\Database\ConnectionInterface */
|
|
private $oldDB = null;
|
|
|
|
/** @var \Illuminate\Database\ConnectionInterface */
|
|
private $newDB = null;
|
|
|
|
/** @var array */
|
|
private $instructions = null;
|
|
|
|
public function __construct()
|
|
{
|
|
$oldDB = config(sprintf('database.connections.%s', config('database.oldDB')));
|
|
$newDB = config(sprintf('database.connections.%s', config('database.newDB')));
|
|
if (!$oldDB) {
|
|
throw new Exception(sprintf('Unknown database connection %s for newDB', config('database.newDB')));
|
|
}
|
|
if (!$newDB) {
|
|
throw new Exception(sprintf('Unknown database connection %s for oldDB', config('database.oldDB')));
|
|
}
|
|
$this->oldDB = DB::connection(config('database.oldDB'));
|
|
$this->newDB = DB::connection(config('database.newDB'));
|
|
}
|
|
|
|
public function up()
|
|
{
|
|
$this->instructions = $this->instruct();
|
|
$this->processData();
|
|
}
|
|
|
|
public function down()
|
|
{
|
|
// Do nothing
|
|
}
|
|
|
|
public function __get(string $name)
|
|
{
|
|
if ($name === 'oldDB') {
|
|
return $this->oldDB;
|
|
}
|
|
if ($name === 'newDB') {
|
|
return $this->newDB;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set migration instructions to execute on "up" only
|
|
* @param array $instructions
|
|
* see Instructions in \App\Database\Migrations\LeGaulois2016Migration
|
|
*/
|
|
abstract public function instruct(): array;
|
|
|
|
/**
|
|
* Core execution on instruction
|
|
*
|
|
* @return void
|
|
*/
|
|
private function processData()
|
|
{
|
|
$getOldData = function ($keyOrKeys, $array) {
|
|
if (is_array($keyOrKeys)) {
|
|
$o = [];
|
|
foreach ($keyOrKeys as $key) {
|
|
$o[] = strval($array[$key]);
|
|
}
|
|
return implode('_', $o);
|
|
}
|
|
return $array[$keyOrKeys] ?? null;
|
|
};
|
|
|
|
foreach ($this->instructions as $oldTableName => $modelData) {
|
|
$migrationName = $oldTableName;
|
|
$oldTableName = $modelData['table'] ?? $oldTableName;
|
|
$rowNbrs = $this->oldDB->table($oldTableName)->count();
|
|
$rowInserted = 0;
|
|
|
|
$this->consoleLog($this->renderProgress($migrationName, $rowInserted, $rowNbrs));
|
|
|
|
$i = 0;
|
|
$page = 1;
|
|
$perPage = env('MIGRATIONPAGINATION', 300);
|
|
do {
|
|
$results = $this->oldDB->table($oldTableName)->paginate($perPage, ['*'], 'page', $page);
|
|
$i += $perPage;
|
|
$page++;
|
|
foreach ($results as $oldData) {
|
|
$model = null;
|
|
try { // Try to get exisiting model ID (to avoid SQL Integrity violations doing delete and create)
|
|
$model = $modelData['model']::findOrFail(
|
|
$getOldData($modelData['bind']['id'] ?? 0, (array)$oldData)
|
|
);
|
|
} catch (ModelNotFoundException $e) {
|
|
$model = new $modelData['model']();
|
|
}
|
|
try { // bind each property to it's data and save
|
|
foreach ($modelData['bind'] as $modelAttr => $oldModelAttr) {
|
|
if (is_array($oldModelAttr)) {
|
|
if (array_key_exists('apply', $oldModelAttr)) {
|
|
$argC = (new ReflectionFunction($oldModelAttr['apply']))->getNumberOfParameters();
|
|
$argV = [
|
|
$getOldData($oldModelAttr['name'], (array)$oldData)
|
|
];
|
|
if ($argC > 1) {
|
|
$argV[] = $model;
|
|
}
|
|
$model[$modelAttr] =
|
|
call_user_func_array($oldModelAttr['apply'], $argV) ??
|
|
$oldModelAttr['default'] ??
|
|
null;
|
|
} else {
|
|
$model[$modelAttr] =
|
|
$getOldData($oldModelAttr['name'], (array)$oldData) ??
|
|
$oldModelAttr['default'] ??
|
|
null;
|
|
}
|
|
} else {
|
|
$model[$modelAttr] = ((array)$oldData)[$oldModelAttr];
|
|
}
|
|
}
|
|
$model->save();
|
|
} catch (AbortRowImportException $e) {
|
|
// DO nothing
|
|
} finally {
|
|
++$rowInserted;
|
|
if (rand(0, 15) == 10) { // prevent screen ficklering
|
|
$this->consoleLog($this->renderProgress($migrationName, $rowInserted, $rowNbrs));
|
|
}
|
|
}
|
|
}
|
|
} while ($i <= $rowNbrs);
|
|
if (array_key_exists('comment', $modelData)) {
|
|
$migrationName .= sprintf(' <%s> ', $modelData['comment']);
|
|
}
|
|
$terminal = new Terminal();
|
|
$consoleLength = $terminal->getWidth();
|
|
|
|
$r = round(80 * 100 / $consoleLength) - strlen($migrationName);
|
|
$r = $r >= 0 ? $r : 0;
|
|
$this->consoleLog(sprintf('%s%smigrated', $migrationName, str_repeat(' ', $r)));
|
|
$this->consoleLog("", true); // Cariage return
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prints a message to the console
|
|
*
|
|
* @param string $message
|
|
* @param boolean $newLine
|
|
* @return void
|
|
*/
|
|
private function consoleLog(string $message, bool $newLine = false)
|
|
{
|
|
if (!$this->output) {
|
|
$this->output = new ConsoleOutput();
|
|
}
|
|
if ($newLine) {
|
|
$this->outputSection = null;
|
|
$this->output->write(sprintf("<info>%s</info>", $message));
|
|
} else {
|
|
if (!$this->outputSection) {
|
|
$this->outputSection = $this->output->section();
|
|
}
|
|
$this->outputSection->overwrite(sprintf('<info>%s</info>', $message));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render the progressbar to a string
|
|
*
|
|
* @param string $oldTableName
|
|
* @param integer $count
|
|
* @param integer $total
|
|
* @return string
|
|
*/
|
|
private function renderProgress(string $oldTableName, int $count, int $total)
|
|
{
|
|
$total = !$total ? 1 : $total;
|
|
$oldTableName = sprintf('%s ', $oldTableName);
|
|
$percent = $count * 100 / $total;
|
|
$percentText = sprintf(' [%d of %d]', $count, $total);
|
|
$terminal = new Terminal();
|
|
$consoleLength = $terminal->getWidth();
|
|
$bar = $consoleLength - strlen($oldTableName) - strlen($percentText);
|
|
$bar = $bar < 0 ? 0 : $bar;
|
|
$prog = round($percent / 100 * $bar);
|
|
$rep = $bar - $prog;
|
|
return $oldTableName . str_repeat('#', $prog) . str_repeat('_', $rep >= 0 ? $rep : 0) . $percentText;
|
|
}
|
|
}
|