Compare commits
19 Commits
26ad7faab0
...
3dcf9d306b
Author | SHA1 | Date | |
---|---|---|---|
3dcf9d306b | |||
9a479e6428 | |||
dc3ef0a3bb | |||
ab08f13d2f | |||
4f992a9294 | |||
21c593c93b | |||
01e6cef219 | |||
3dc7259ccb | |||
a51c15cb25 | |||
c142a8975f | |||
211b6b0b94 | |||
0fddc3a310 | |||
03f91bd721 | |||
18742b3947 | |||
eb26f31ed6 | |||
ea205df76d | |||
916895e489 | |||
a274d03c91 | |||
a64421c564 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,4 +2,5 @@
|
|||||||
**/log?/*
|
**/log?/*
|
||||||
**/cache/*
|
**/cache/*
|
||||||
**/.idea
|
**/.idea
|
||||||
**/vendor/
|
**/vendor/
|
||||||
|
/app/composer.lock
|
||||||
|
8
app/bin/app
Normal file
8
app/bin/app
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
$app = require_once implode(DIRECTORY_SEPARATOR, [
|
||||||
|
dirname(__DIR__),
|
||||||
|
'bootstrap',
|
||||||
|
'app.php'
|
||||||
|
]);
|
||||||
|
$app->run();
|
119
app/bin/phinx
Normal file
119
app/bin/phinx
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../vendor/robmorgan/phinx/bin/phinx)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/vendor/autoload.php';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = $this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/vendor/robmorgan/phinx/bin/phinx');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/vendor/robmorgan/phinx/bin/phinx';
|
119
app/bin/php-parse
Normal file
119
app/bin/php-parse
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../vendor/nikic/php-parser/bin/php-parse)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/vendor/autoload.php';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = $this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/vendor/nikic/php-parser/bin/php-parse');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/vendor/nikic/php-parser/bin/php-parse';
|
122
app/bin/phpunit
Normal file
122
app/bin/phpunit
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../vendor/phpunit/phpunit/phpunit)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/vendor/autoload.php';
|
||||||
|
$GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'] = $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'] = array(realpath(__DIR__ . '/..'.'/vendor/phpunit/phpunit/phpunit'));
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = 'phpvfscomposer://'.$this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
$data = str_replace('__DIR__', var_export(dirname($this->realpath), true), $data);
|
||||||
|
$data = str_replace('__FILE__', var_export($this->realpath, true), $data);
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/vendor/phpunit/phpunit/phpunit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/vendor/phpunit/phpunit/phpunit';
|
32
app/bootstrap/app.php
Normal file
32
app/bootstrap/app.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
function buildApp(): Symfony\Component\Console\Application
|
||||||
|
{
|
||||||
|
$builder = new DI\ContainerBuilder();
|
||||||
|
|
||||||
|
$baseFolder = dirname(__DIR__);
|
||||||
|
$folders = [
|
||||||
|
'configs',
|
||||||
|
'setups'
|
||||||
|
];
|
||||||
|
foreach ($folders as $folderName) {
|
||||||
|
$folder = implode(DIRECTORY_SEPARATOR, [$baseFolder, $folderName]);
|
||||||
|
if (!isset($folder)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files = new FilesystemIterator($folder);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$builder->addDefinitions($file->getRealPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$app = (new ProVM\Extend\Application())
|
||||||
|
->setContainer($builder->build());
|
||||||
|
return require_once 'commands.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'composer.php';
|
||||||
|
|
||||||
|
return buildApp();
|
9
app/bootstrap/commands.php
Normal file
9
app/bootstrap/commands.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
function buildCommands(\Symfony\Component\Console\Application &$app): \Symfony\Component\Console\Application {
|
||||||
|
$commands = $app->getContainer()->get('commands');
|
||||||
|
foreach ($commands as $commandClass) {
|
||||||
|
$app->add($app->getContainer()->get($commandClass));
|
||||||
|
}
|
||||||
|
return $app;
|
||||||
|
}
|
||||||
|
return buildCommands($app);
|
6
app/bootstrap/composer.php
Normal file
6
app/bootstrap/composer.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
require_once implode(DIRECTORY_SEPARATOR, [
|
||||||
|
dirname(__DIR__),
|
||||||
|
'vendor',
|
||||||
|
'autoload.php'
|
||||||
|
]);
|
41
app/composer.json
Normal file
41
app/composer.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "provm/phinx-migration-generator",
|
||||||
|
"description": "Generate database migration files for Phinx for each table.",
|
||||||
|
"type": "project",
|
||||||
|
"require": {
|
||||||
|
"monolog/monolog": "^3.8",
|
||||||
|
"php-di/php-di": "^7.0",
|
||||||
|
"provm/database": "^2.3",
|
||||||
|
"provm/query_builder": "^1.1",
|
||||||
|
"symfony/console": "^7.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^11.5",
|
||||||
|
"robmorgan/phinx": "^0.16.6"
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Aldarien",
|
||||||
|
"email": "aldarien85@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"ProVM\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"sort-packages": true,
|
||||||
|
"bin-dir": "./bin"
|
||||||
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.provm.cl/ProVM/database"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.provm.cl/ProVM/query_builder"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
8
app/configs/commands.php
Normal file
8
app/configs/commands.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'commands' => [
|
||||||
|
ProVM\Command\Generate::class,
|
||||||
|
ProVM\Command\GenerateMigrations::class,
|
||||||
|
ProVM\Command\GenerateSeeds::class,
|
||||||
|
]
|
||||||
|
];
|
13
app/configs/env.php
Normal file
13
app/configs/env.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
...$_ENV,
|
||||||
|
'start_date' => '20141101080000',
|
||||||
|
'skips' => [
|
||||||
|
'monolog',
|
||||||
|
'phinxlog',
|
||||||
|
'personas',
|
||||||
|
'datos_personas',
|
||||||
|
'proveedores',
|
||||||
|
'datos_proveedores'
|
||||||
|
]
|
||||||
|
];
|
8
app/configs/paths.php
Normal file
8
app/configs/paths.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'paths.base' => dirname(__DIR__),
|
||||||
|
'paths.resources' => DI\String('{paths.base}/resources'),
|
||||||
|
'paths.database' => DI\String('{paths.resources}/database'),
|
||||||
|
'paths.migrations' => DI\String('{paths.database}/migrations'),
|
||||||
|
'paths.seeds' => DI\String('{paths.database}/seeds')
|
||||||
|
];
|
26
app/setups/concepts.php
Normal file
26
app/setups/concepts.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
return [
|
||||||
|
ProVM\Concept\Database::class => function(ContainerInterface $container) {
|
||||||
|
return (new ProVM\Database\MySQL())
|
||||||
|
->setHost($container->get('DB_HOST'))
|
||||||
|
->setName($container->get('DB_DATABASE'))
|
||||||
|
->setUser($container->get('DB_USER'))
|
||||||
|
->setPassword($container->get('DB_PASSWORD'));
|
||||||
|
},
|
||||||
|
ProVM\Concept\Database\Connection::class => function(ContainerInterface $container) {
|
||||||
|
return new ProVM\Database\Connection($container->get(ProVM\Concept\Database::class));
|
||||||
|
},
|
||||||
|
ProVM\Concept\Database\Query\Builder::class => function(ContainerInterface $container) {
|
||||||
|
return new ProVM\Database\Query\Builder([
|
||||||
|
ProVM\Concept\Database\Query\Select::class => ProVM\Database\Query\MySQL\Select::class,
|
||||||
|
ProVM\Concept\Database\Query\Insert::class => ProVM\Database\Query\MySQL\Insert::class,
|
||||||
|
ProVM\Concept\Database\Query\Update::class => ProVM\Database\Query\MySQL\Update::class,
|
||||||
|
ProVM\Concept\Database\Query\Delete::class => ProVM\Database\Query\MySQL\Delete::class,
|
||||||
|
ProVM\Concept\Database\Query\Create::class => ProVM\Database\Query\MySQL\Create::class,
|
||||||
|
ProVM\Concept\Database\Query\Drop::class => ProVM\Database\Query\MySQL\Drop::class,
|
||||||
|
ProVM\Concept\Database\Query\Truncate::class => ProVM\Database\Query\MySQL\Truncate::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
];
|
32
app/setups/generators.php
Normal file
32
app/setups/generators.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
return [
|
||||||
|
ProVM\Generator\Migration::class => function(ContainerInterface $container) {
|
||||||
|
return new ProVM\Generator\Migration(
|
||||||
|
$container->get(ProVM\Concept\Database::class),
|
||||||
|
$container->get(ProVM\Concept\Database\Connection::class),
|
||||||
|
$container->get(ProVM\Concept\Database\Query\Builder::class),
|
||||||
|
$container->get(ProVM\Repository\Table::class),
|
||||||
|
$container->get(Psr\Log\LoggerInterface::class),
|
||||||
|
new DateTimeImmutable($container->get('start_date')),
|
||||||
|
$container->get('DB_DATABASE'),
|
||||||
|
$container->get('paths.migrations'),
|
||||||
|
$container->get('skips')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
ProVM\Generator\Seed::class => function(ContainerInterface $container) {
|
||||||
|
return new ProVM\Generator\Seed(
|
||||||
|
$container->get(ProVM\Concept\Database::class),
|
||||||
|
$container->get(ProVM\Concept\Database\Connection::class),
|
||||||
|
$container->get(ProVM\Concept\Database\Query\Builder::class),
|
||||||
|
$container->get(ProVM\Repository\Table::class),
|
||||||
|
$container->get(ProVM\Repository\Data::class),
|
||||||
|
$container->get(Psr\Log\LoggerInterface::class),
|
||||||
|
$container->get('DB_DATABASE'),
|
||||||
|
$container->get('paths.seeds'),
|
||||||
|
new DateTimeImmutable($container->get('start_date')),
|
||||||
|
$container->get('skips')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
];
|
16
app/setups/logs.php
Normal file
16
app/setups/logs.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
return [
|
||||||
|
Psr\Log\LoggerInterface::class => function(ContainerInterface $container) {
|
||||||
|
return new Monolog\Logger('migrations', [
|
||||||
|
(new Monolog\Handler\RotatingFileHandler('/logs/migrations.log'))
|
||||||
|
->setFormatter(new Monolog\Formatter\LineFormatter(null, null, false, false, true))
|
||||||
|
], [
|
||||||
|
$container->get(Monolog\Processor\IntrospectionProcessor::class),
|
||||||
|
$container->get(Monolog\Processor\MemoryUsageProcessor::class),
|
||||||
|
$container->get(Monolog\Processor\MemoryPeakUsageProcessor::class),
|
||||||
|
$container->get(Monolog\Processor\PsrLogMessageProcessor::class)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
];
|
15
app/setups/repositories.php
Normal file
15
app/setups/repositories.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
return [
|
||||||
|
ProVM\Repository\Table::class => function(ContainerInterface $container) {
|
||||||
|
return new ProVM\Repository\Table(
|
||||||
|
$container->get(ProVM\Concept\Database\Connection::class),
|
||||||
|
$container->get('DB_DATABASE'));
|
||||||
|
},
|
||||||
|
ProVM\Repository\Data::class => function(ContainerInterface $container) {
|
||||||
|
return new ProVM\Repository\Data(
|
||||||
|
$container->get(ProVM\Concept\Database\Connection::class),
|
||||||
|
$container->get(ProVM\Concept\Database\Query\Builder::class));
|
||||||
|
}
|
||||||
|
];
|
40
app/src/Command/Generate.php
Normal file
40
app/src/Command/Generate.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Command;
|
||||||
|
|
||||||
|
use Symfony\Component\Console;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
#[Console\Attribute\AsCommand(name: 'generate', description: 'Generate database migrations and seeds.')]
|
||||||
|
class Generate extends Console\Command\Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
parent::configure();
|
||||||
|
$this->addOption('dry-run', 'd');
|
||||||
|
}
|
||||||
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new Console\Style\SymfonyStyle($input, $output);
|
||||||
|
$io->title('Generate');
|
||||||
|
|
||||||
|
$dryRun = $input->hasOption('dry-run');
|
||||||
|
|
||||||
|
$commands = [
|
||||||
|
'generate:migrations',
|
||||||
|
'generate:seeds'
|
||||||
|
];
|
||||||
|
foreach ($commands as $commandName) {
|
||||||
|
$command = $this->getApplication()->find($commandName);
|
||||||
|
$arguments = [
|
||||||
|
'command' => $commandName,
|
||||||
|
];
|
||||||
|
if ($dryRun) {
|
||||||
|
$arguments['--dry-run'] = true;
|
||||||
|
}
|
||||||
|
$command->run(new Console\Input\ArrayInput($arguments), $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Console\Command\Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
34
app/src/Command/GenerateMigrations.php
Normal file
34
app/src/Command/GenerateMigrations.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Command;
|
||||||
|
|
||||||
|
use Symfony\Component\Console;
|
||||||
|
use ProVM\Generator;
|
||||||
|
|
||||||
|
#[Console\Attribute\AsCommand(
|
||||||
|
name: 'generate:migrations'
|
||||||
|
)]
|
||||||
|
class GenerateMigrations extends Console\Command\Command
|
||||||
|
{
|
||||||
|
public function __construct(protected Generator\Migration $migrationGenerator, ?string $name = null)
|
||||||
|
{
|
||||||
|
parent::__construct($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
parent::configure();
|
||||||
|
$this->addOption('dry-run', 'd', Console\Input\InputOption::VALUE_OPTIONAL, default: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new Console\Style\SymfonyStyle($input, $output);
|
||||||
|
$io->title('Generate Migrations');
|
||||||
|
|
||||||
|
$dryRun = $input->getOption('dry-run');
|
||||||
|
|
||||||
|
$this->migrationGenerator->generate($io, $dryRun);
|
||||||
|
|
||||||
|
return Console\Command\Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
34
app/src/Command/GenerateSeeds.php
Normal file
34
app/src/Command/GenerateSeeds.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Command;
|
||||||
|
|
||||||
|
use Symfony\Component\Console;
|
||||||
|
use ProVM\Generator;
|
||||||
|
|
||||||
|
#[Console\Attribute\AsCommand(
|
||||||
|
name: 'generate:seeds'
|
||||||
|
)]
|
||||||
|
class GenerateSeeds extends Console\Command\Command
|
||||||
|
{
|
||||||
|
public function __construct(protected Generator\Seed $seedGenerator, ?string $name = null)
|
||||||
|
{
|
||||||
|
parent::__construct($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
parent::configure();
|
||||||
|
$this->addOption('dry-run', 'd', Console\Input\InputOption::VALUE_OPTIONAL, default: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new Console\Style\SymfonyStyle($input, $output);
|
||||||
|
$io->title('Generate Seeds');
|
||||||
|
|
||||||
|
$dryRun = $input->getOption('dry-run');
|
||||||
|
|
||||||
|
$this->seedGenerator->generate($io, $dryRun);
|
||||||
|
|
||||||
|
return Console\Command\Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
9
app/src/Concept/Generator.php
Normal file
9
app/src/Concept/Generator.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Concept;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Style\StyleInterface;
|
||||||
|
|
||||||
|
interface Generator
|
||||||
|
{
|
||||||
|
public function generate(StyleInterface $io, bool $dryRun = false): void;
|
||||||
|
}
|
7
app/src/Concept/Parser.php
Normal file
7
app/src/Concept/Parser.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Concept;
|
||||||
|
|
||||||
|
interface Parser
|
||||||
|
{
|
||||||
|
public function parse(): string;
|
||||||
|
}
|
19
app/src/Enforce/Generator.php
Normal file
19
app/src/Enforce/Generator.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Enforce;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Console\Style\StyleInterface;
|
||||||
|
use ProVM\Concept;
|
||||||
|
|
||||||
|
abstract class Generator implements Concept\Generator
|
||||||
|
{
|
||||||
|
public function __construct(protected LoggerInterface $logger) {}
|
||||||
|
|
||||||
|
protected function log(string $message, bool $output = false, ?StyleInterface $io = null): void
|
||||||
|
{
|
||||||
|
$this->logger->info($message);
|
||||||
|
if ($output) {
|
||||||
|
$io->note($message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
app/src/Extend/Application.php
Normal file
20
app/src/Extend/Application.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Extend;
|
||||||
|
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Symfony\Component\Console;
|
||||||
|
|
||||||
|
class Application extends Console\Application
|
||||||
|
{
|
||||||
|
protected ContainerInterface $container;
|
||||||
|
|
||||||
|
public function getContainer(): ContainerInterface
|
||||||
|
{
|
||||||
|
return $this->container;
|
||||||
|
}
|
||||||
|
public function setContainer(ContainerInterface $container): Console\Application
|
||||||
|
{
|
||||||
|
$this->container = $container;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
132
app/src/Generator/Migration.php
Normal file
132
app/src/Generator/Migration.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Generator;
|
||||||
|
|
||||||
|
use DateTimeInterface;
|
||||||
|
use DateInterval;
|
||||||
|
use PDOException;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Console\Style\StyleInterface;
|
||||||
|
use ProVM\Concept;
|
||||||
|
use ProVM\Enforce;
|
||||||
|
use ProVM\Repository;
|
||||||
|
|
||||||
|
class Migration extends Enforce\Generator
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected Concept\Database $database,
|
||||||
|
protected Concept\Database\Connection $connection,
|
||||||
|
protected Concept\Database\Query\Builder $queryBuilder,
|
||||||
|
public Repository\Table $tableRepository,
|
||||||
|
LoggerInterface $logger,
|
||||||
|
protected DateTimeInterface $startDate,
|
||||||
|
protected string $databaseName,
|
||||||
|
protected string $migrationsPath,
|
||||||
|
protected array $skips)
|
||||||
|
{
|
||||||
|
parent::__construct($logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate(StyleInterface $io, bool $dryRun = false): void
|
||||||
|
{
|
||||||
|
$this->log('Running generate migrations' . (($dryRun) ? ' [dry-run]' : ''), true, $io);
|
||||||
|
foreach ($this->tableRepository->getAll() as $tableName) {
|
||||||
|
if (in_array($tableName, $this->skips)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->log("Table: {$tableName}", true, $io);
|
||||||
|
$filename = $this->buildFilename($tableName);
|
||||||
|
$this->log("Filename: {$filename}", $dryRun, $io);
|
||||||
|
$content = $this->buildFile($tableName);
|
||||||
|
$this->log("Content: {$content}");
|
||||||
|
|
||||||
|
if ($dryRun) {
|
||||||
|
$status = file_put_contents($filename, $content);
|
||||||
|
$this->log("Saved: " . var_export($status, true));
|
||||||
|
try {
|
||||||
|
$this->registerMigration($tableName);
|
||||||
|
} catch (PDOException $exception) {
|
||||||
|
$this->logger->warning($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->log("Total tables migrated: " . count($this->tableRepository->getAll()), true, $io);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildFilename(string $table): string
|
||||||
|
{
|
||||||
|
$i = $this->tableRepository->getIndex($table);
|
||||||
|
$time = $this->startDate->add(new DateInterval("PT{$i}S"));
|
||||||
|
return implode(DIRECTORY_SEPARATOR, [
|
||||||
|
$this->migrationsPath,
|
||||||
|
"{$time->format('YmdHis')}_create_{$table}.php"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
protected function buildClassName(string $table): string
|
||||||
|
{
|
||||||
|
return 'Create' . str_replace(' ', '', ucwords(str_replace('_', ' ', $table)));
|
||||||
|
}
|
||||||
|
protected function buildHeader(): string
|
||||||
|
{
|
||||||
|
return "<?php
|
||||||
|
|
||||||
|
use Phinx\Db\Adapter\MysqlAdapter;
|
||||||
|
";
|
||||||
|
}
|
||||||
|
protected function buildClass(string $table): string
|
||||||
|
{
|
||||||
|
return "class {$this->buildClassName($table)} extends Phinx\Migration\AbstractMigration";
|
||||||
|
}
|
||||||
|
protected function buildFunction(string $table): string
|
||||||
|
{
|
||||||
|
$output = ["{", "\tpublic function change(): void", "\t{"];
|
||||||
|
$output []= $this->buildInitialSetup();
|
||||||
|
$this->tableRepository->getDefinition($table);
|
||||||
|
$output []= $this->tableRepository->parseDefinition($table);
|
||||||
|
$output []= $this->buildFinalSetup();
|
||||||
|
$output []= "\t}";
|
||||||
|
return implode(PHP_EOL, $output);
|
||||||
|
}
|
||||||
|
protected function buildInitialSetup(): string
|
||||||
|
{
|
||||||
|
return implode(PHP_EOL, [
|
||||||
|
"\t\t\$this->execute('SET unique_checks=0; SET foreign_key_checks=0;');",
|
||||||
|
"\t\t\$this->execute(\"ALTER DATABASE CHARACTER SET 'utf8mb4';\");",
|
||||||
|
"\t\t\$this->execute(\"ALTER DATABASE COLLATE='utf8mb4_general_ci';\");",
|
||||||
|
''
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
protected function buildFinalSetup(): string
|
||||||
|
{
|
||||||
|
return "\t\t\$this->execute('SET unique_checks=1; SET foreign_key_checks=1;');";
|
||||||
|
}
|
||||||
|
protected function buildFile(string $table): string
|
||||||
|
{
|
||||||
|
return implode(PHP_EOL, [
|
||||||
|
$this->buildHeader(),
|
||||||
|
$this->buildClass($table),
|
||||||
|
$this->buildFunction($table),
|
||||||
|
'}',
|
||||||
|
''
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
protected function registerMigration(string $table): void
|
||||||
|
{
|
||||||
|
$i = $this->tableRepository->getIndex($table);
|
||||||
|
$time = $this->startDate->add(new DateInterval("PT{$i}S"));
|
||||||
|
$this->logger->info("Registering migration: {$time->format('Y-m-d H:i:s')}");
|
||||||
|
$migrationName = $this->buildClassName($table);
|
||||||
|
|
||||||
|
$query = $this->queryBuilder
|
||||||
|
->insert()
|
||||||
|
->into('phinxlog')
|
||||||
|
->columns(['version', 'migration_name', 'start_time', 'end_time', 'breakpoint'])
|
||||||
|
->values(['?', '?', '?', '?', 0]);
|
||||||
|
|
||||||
|
$this->connection->execute($query, [
|
||||||
|
$time->format('YmdHis'),
|
||||||
|
$migrationName,
|
||||||
|
$time->format('Y-m-d H:i:s'),
|
||||||
|
$time->format('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
139
app/src/Generator/Seed.php
Normal file
139
app/src/Generator/Seed.php
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Generator;
|
||||||
|
|
||||||
|
use DateTimeInterface;
|
||||||
|
use DateInterval;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use ProVM\Concept;
|
||||||
|
use ProVM\Enforce;
|
||||||
|
use ProVM\Repository;
|
||||||
|
use Symfony\Component\Console\Style\StyleInterface;
|
||||||
|
|
||||||
|
class Seed extends Enforce\Generator
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected Concept\Database $database,
|
||||||
|
protected Concept\Database\Connection $connection,
|
||||||
|
protected Concept\Database\Query\Builder $queryBuilder,
|
||||||
|
protected Repository\Table $tableRepository,
|
||||||
|
protected Repository\Data $dataRepository,
|
||||||
|
LoggerInterface $logger,
|
||||||
|
protected string $databaseName,
|
||||||
|
protected string $seedsPath,
|
||||||
|
protected DateTimeInterface $startDate,
|
||||||
|
protected array $skips)
|
||||||
|
{
|
||||||
|
parent::__construct($logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate(StyleInterface $io, bool $dryRun = false): void
|
||||||
|
{
|
||||||
|
$this->log('Running generate seeds' . (($dryRun) ? ' [dry-run]' : ''), true, $io);
|
||||||
|
$tables = $this->tableRepository->getAll();
|
||||||
|
foreach ($tables as $table) {
|
||||||
|
if (in_array($table, $this->skips)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->log("Table: {$table}");
|
||||||
|
$filename = $this->buildFilename($table);
|
||||||
|
$this->log("Filename: {$filename}", $dryRun, $io);
|
||||||
|
$content = $this->buildFile($table);
|
||||||
|
$this->log("Content: {$content}");
|
||||||
|
|
||||||
|
if ($dryRun) {
|
||||||
|
$status = file_put_contents($filename, $content);
|
||||||
|
$this->log("Saved: " . var_export($status, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->log("Total tables seeded: " . count($this->tableRepository->getAll()), true, $io);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildFilename(string $table): string
|
||||||
|
{
|
||||||
|
$i = $this->tableRepository->getIndex($table);
|
||||||
|
$time = $this->startDate->add(new DateInterval("PT{$i}S"));
|
||||||
|
return implode(DIRECTORY_SEPARATOR, [
|
||||||
|
$this->seedsPath,
|
||||||
|
"{$time->format('YmdHis')}_{$table}_seeder.php"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
protected function buildFile(string $table): string
|
||||||
|
{
|
||||||
|
return implode(PHP_EOL, [
|
||||||
|
$this->buildHeader(),
|
||||||
|
$this->buildClass($table),
|
||||||
|
"{",
|
||||||
|
$this->buildFunction($table),
|
||||||
|
'}',
|
||||||
|
''
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
protected function buildHeader(): string
|
||||||
|
{
|
||||||
|
return "<?php
|
||||||
|
|
||||||
|
use Phinx\Seed\AbstractSeed;
|
||||||
|
";
|
||||||
|
}
|
||||||
|
protected function buildClass(string $table): string
|
||||||
|
{
|
||||||
|
return "class {$this->buildClassName($table)} extends AbstractSeed";
|
||||||
|
}
|
||||||
|
protected function buildClassName(string $table): string
|
||||||
|
{
|
||||||
|
return str_replace(' ', '', ucwords(str_replace('_', ' ', $table))) . 'Seeder';
|
||||||
|
}
|
||||||
|
protected function buildFunction(string $table): string
|
||||||
|
{
|
||||||
|
return implode(PHP_EOL, [
|
||||||
|
"\tpublic function run(): void",
|
||||||
|
"\t{",
|
||||||
|
$this->buildData($table),
|
||||||
|
"",
|
||||||
|
$this->buildInitialSetup(),
|
||||||
|
"\t\t\$this->table('{$table}')",
|
||||||
|
"\t\t\t->insert(\$data)",
|
||||||
|
"\t\t\t->saveData();",
|
||||||
|
$this->buildFinalSetup(),
|
||||||
|
"\t}"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
protected function buildData(string $table): string
|
||||||
|
{
|
||||||
|
$output = ["\t\t\$data = ["];
|
||||||
|
$dataGenerator = $this->dataRepository->getAll($table);
|
||||||
|
foreach ($dataGenerator as $row) {
|
||||||
|
$output []= "\t\t\t[";
|
||||||
|
foreach ($row as $key => $value) {
|
||||||
|
if (is_bool($value)) {
|
||||||
|
$value = $value ? 1 : 0;
|
||||||
|
}
|
||||||
|
if (!ctype_digit("{$value}") and $value !== null) {
|
||||||
|
if (str_contains($value, "'")) {
|
||||||
|
$value = str_replace("'", "\'", $value);
|
||||||
|
}
|
||||||
|
$value = "'{$value}'";
|
||||||
|
}
|
||||||
|
if ($value === null) {
|
||||||
|
$value = 'null';
|
||||||
|
}
|
||||||
|
if (strlen($value) > 2 and str_starts_with($value, '0')) {
|
||||||
|
$value = "'{$value}'";
|
||||||
|
}
|
||||||
|
$output []= "\t\t\t\t'{$key}' => {$value},";
|
||||||
|
}
|
||||||
|
$output []= "\t\t\t],";
|
||||||
|
}
|
||||||
|
$output []= "\t\t];";
|
||||||
|
$this->logger->debug("Total data: {$this->dataRepository->size}");
|
||||||
|
return implode(PHP_EOL, $output);
|
||||||
|
}
|
||||||
|
protected function buildInitialSetup(): string
|
||||||
|
{
|
||||||
|
return "\t\t\$this->execute('SET unique_checks=0; SET foreign_key_checks=0;');";
|
||||||
|
}
|
||||||
|
protected function buildFinalSetup(): string
|
||||||
|
{
|
||||||
|
return "\t\t\$this->execute('SET unique_checks=1; SET foreign_key_checks=1;');";
|
||||||
|
}
|
||||||
|
}
|
108
app/src/Parser/Column.php
Normal file
108
app/src/Parser/Column.php
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Parser;
|
||||||
|
|
||||||
|
use ProVM\Concept;
|
||||||
|
|
||||||
|
class Column implements Concept\Parser
|
||||||
|
{
|
||||||
|
public function __construct(protected string $line) {}
|
||||||
|
|
||||||
|
public function parse(): string
|
||||||
|
{
|
||||||
|
return "->addColumn('{$this->getName()}', '{$this->getType()}'{$this->getOptions()})";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int $ini;
|
||||||
|
protected int $end;
|
||||||
|
protected array $parts;
|
||||||
|
protected array $options;
|
||||||
|
|
||||||
|
protected function getName(): string
|
||||||
|
{
|
||||||
|
$ini = $this->getIni();
|
||||||
|
$end = $this->getEnd();
|
||||||
|
return substr($this->line, $ini, $end - $ini);
|
||||||
|
}
|
||||||
|
protected function getType(): string
|
||||||
|
{
|
||||||
|
$parts = $this->getParts();
|
||||||
|
$type = array_shift($parts);
|
||||||
|
|
||||||
|
if (str_contains($type, '(')) {
|
||||||
|
list($type, $length) = explode('(', $type);
|
||||||
|
$this->options []= "'length' => " . rtrim($length, ')');
|
||||||
|
}
|
||||||
|
return match($type) {
|
||||||
|
'int' => 'integer',
|
||||||
|
'tinyint' => 'boolean',
|
||||||
|
'varchar' => 'string',
|
||||||
|
default => $type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
protected function getOptions(): string
|
||||||
|
{
|
||||||
|
$parts = $this->getParts();
|
||||||
|
array_shift($parts);
|
||||||
|
$validOptions = [
|
||||||
|
'default' => function($parts) {
|
||||||
|
$i = array_search('default', array_map(function($part) {return strtolower($part);}, $parts));
|
||||||
|
return ['default', $parts[$i + 1]];
|
||||||
|
},
|
||||||
|
'null' => function($parts) {
|
||||||
|
$value = true;
|
||||||
|
$i = array_search('null', array_map(function($part) {return strtolower($part);}, $parts));
|
||||||
|
if (key_exists($i - 1, $parts) and strtolower($parts[$i - 1]) === 'not') {
|
||||||
|
$value = false;
|
||||||
|
}
|
||||||
|
return ['null', $value];
|
||||||
|
},
|
||||||
|
'unsigned' => function($parts) {
|
||||||
|
return ['signed', false];
|
||||||
|
},
|
||||||
|
'auto_increment' => function($parts) {
|
||||||
|
return ['auto_increment', true];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
foreach ($validOptions as $validOption => $callable) {
|
||||||
|
if (str_contains(strtolower($this->line), $validOption)) {
|
||||||
|
list($option, $value) = $callable($parts);
|
||||||
|
if (strtolower($value) === 'null') {
|
||||||
|
$value = 'null';
|
||||||
|
}
|
||||||
|
if ($value !== 'null' and !is_bool($value) and !ctype_digit($value)) {
|
||||||
|
$value = trim($value, "'");
|
||||||
|
$value = "'{$value}'";
|
||||||
|
}
|
||||||
|
if (is_bool($value)) {
|
||||||
|
$value = $value ? 'true' : 'false';
|
||||||
|
}
|
||||||
|
$this->options []= "'{$option}' => {$value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count($this->options) === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return ', [' . implode(', ', $this->options) . ']';
|
||||||
|
}
|
||||||
|
protected function getIni(): int
|
||||||
|
{
|
||||||
|
if (!isset($this->ini)) {
|
||||||
|
$this->ini = strpos($this->line, '`') + 1;
|
||||||
|
}
|
||||||
|
return $this->ini;
|
||||||
|
}
|
||||||
|
protected function getEnd(): int
|
||||||
|
{
|
||||||
|
if (!isset($this->end)) {
|
||||||
|
$this->end = strpos($this->line, '`', $this->getIni());
|
||||||
|
}
|
||||||
|
return $this->end;
|
||||||
|
}
|
||||||
|
protected function getParts(): array
|
||||||
|
{
|
||||||
|
if (!isset($this->parts)) {
|
||||||
|
$this->parts = explode(' ', trim(substr($this->line, $this->getEnd() + 1), ' ,'));
|
||||||
|
}
|
||||||
|
return $this->parts;
|
||||||
|
}
|
||||||
|
}
|
44
app/src/Parser/Constraint.php
Normal file
44
app/src/Parser/Constraint.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Parser;
|
||||||
|
|
||||||
|
use ProVM\Concept;
|
||||||
|
|
||||||
|
class Constraint implements Concept\Parser
|
||||||
|
{
|
||||||
|
public function __construct(protected string $line) {}
|
||||||
|
|
||||||
|
public function parse(): string
|
||||||
|
{
|
||||||
|
return "->addForeignKey({$this->getColumns()}, '{$this->getTable()}', {$this->getReferences()}, ['delete' => 'cascade', 'update' => 'cascade'])";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getColumns(): string
|
||||||
|
{
|
||||||
|
$ini = strpos(strtolower($this->line), 'foreign key') + strlen('FOREIGN KEY ');
|
||||||
|
$ini = strpos($this->line, '(', $ini) + 1;
|
||||||
|
$end = strpos(strtolower($this->line), ' references', $ini + 1) - strlen(')');
|
||||||
|
return $this->getNames($ini, $end);
|
||||||
|
}
|
||||||
|
protected function getTable(): string
|
||||||
|
{
|
||||||
|
$ini = strpos(strtolower($this->line), 'references') + strlen('REFERENCES ') + 1;
|
||||||
|
$end = strpos($this->line, '`', $ini + 1);
|
||||||
|
return substr($this->line, $ini, $end - $ini);
|
||||||
|
}
|
||||||
|
protected function getReferences(): string
|
||||||
|
{
|
||||||
|
$ini = strpos($this->line, '(', strpos(strtolower($this->line), 'references')) + 1;
|
||||||
|
$end = strpos($this->line, ')', $ini);
|
||||||
|
return $this->getNames($ini, $end);
|
||||||
|
}
|
||||||
|
protected function getNames($ini, $end): string
|
||||||
|
{
|
||||||
|
$names = substr($this->line, $ini, $end - $ini);
|
||||||
|
if (!str_contains($names, ',')) {
|
||||||
|
return str_replace('`', "'", $names);
|
||||||
|
}
|
||||||
|
$names = explode(', ', $names);
|
||||||
|
$columns = array_map(function($name) {return str_replace('`', "'", $name);}, $names);
|
||||||
|
return '[' . implode(', ', $columns) . ']';
|
||||||
|
}
|
||||||
|
}
|
25
app/src/Repository/Data.php
Normal file
25
app/src/Repository/Data.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use PDO;
|
||||||
|
use ProVM\Concept;
|
||||||
|
|
||||||
|
class Data
|
||||||
|
{
|
||||||
|
public function __construct(protected Concept\Database\Connection $connection, protected Concept\Database\Query\Builder $queryBuilder) {}
|
||||||
|
|
||||||
|
public int $size;
|
||||||
|
|
||||||
|
public function getAll(string $table): Generator
|
||||||
|
{
|
||||||
|
$query = $this->queryBuilder
|
||||||
|
->select()
|
||||||
|
->from($table);
|
||||||
|
$results = $this->connection->query($query);
|
||||||
|
$this->size = $results->rowCount();
|
||||||
|
while ($row = $results->fetch()) {
|
||||||
|
yield $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
app/src/Repository/Table.php
Normal file
95
app/src/Repository/Table.php
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
namespace ProVM\Repository;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
use ProVM\Concept;
|
||||||
|
use ProVM\Parser;
|
||||||
|
|
||||||
|
class Table
|
||||||
|
{
|
||||||
|
public function __construct(protected Concept\Database\Connection $connection, protected string $databaseName) {}
|
||||||
|
|
||||||
|
public function getAll(): array
|
||||||
|
{
|
||||||
|
if (!isset($this->tables)) {
|
||||||
|
$results = $this->connection->query('SHOW TABLES');
|
||||||
|
$rows = $results->fetchAll();
|
||||||
|
$this->tables = array_map(function(array $row) {
|
||||||
|
return $row["Tables_in_{$this->databaseName}"];
|
||||||
|
}, $rows);
|
||||||
|
}
|
||||||
|
return $this->tables;
|
||||||
|
}
|
||||||
|
public function getDefinition(string $table): string
|
||||||
|
{
|
||||||
|
if (!isset($this->definitions[$table])) {
|
||||||
|
$results = $this->connection->query("SHOW CREATE TABLE {$table}");
|
||||||
|
$rows = $results->fetchAll();
|
||||||
|
$this->definitions[$table] = $rows[0]["Create Table"];
|
||||||
|
}
|
||||||
|
return $this->definitions[$table];
|
||||||
|
}
|
||||||
|
public function parseDefinition(string $table, int $tabOffset = 2): string
|
||||||
|
{
|
||||||
|
$this->extractLines($table);
|
||||||
|
$tableLine = "\$this->table('{$table}'";
|
||||||
|
if (count($this->primary) === 1) {
|
||||||
|
$tableLine .= ", ['id' => '{$this->primary[0]}']";
|
||||||
|
} elseif (count($this->primary) > 1) {
|
||||||
|
$primaryString = implode(', ', array_map(function(string $key) {return "'{$key}'";}, $this->primary));
|
||||||
|
$tableLine .= ", ['id' => false, 'primary key' => [{$primaryString}]]";
|
||||||
|
}
|
||||||
|
$tableLine .= ')';
|
||||||
|
$output = [str_repeat("\t", $tabOffset) . $tableLine];
|
||||||
|
foreach ($this->columns as $column) {
|
||||||
|
$output[] = str_repeat("\t", $tabOffset + 1) . $column;
|
||||||
|
}
|
||||||
|
foreach ($this->constraints as $constraint) {
|
||||||
|
$output[] = str_repeat("\t", $tabOffset + 1) . $constraint;
|
||||||
|
}
|
||||||
|
return implode(PHP_EOL, $output);
|
||||||
|
}
|
||||||
|
public function getIndex(string $table): int
|
||||||
|
{
|
||||||
|
return array_search($table, $this->tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected array $tables = [];
|
||||||
|
protected array $definitions = [];
|
||||||
|
protected array $primary = [];
|
||||||
|
protected array $columns = [];
|
||||||
|
protected array $constraints = [];
|
||||||
|
|
||||||
|
protected function extractLines(string $table): void
|
||||||
|
{
|
||||||
|
$this->primary = [];
|
||||||
|
$this->columns = [];
|
||||||
|
$this->constraints = [];
|
||||||
|
|
||||||
|
$lines = explode(PHP_EOL, $this->definitions[$table]);
|
||||||
|
array_shift($lines);
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$trimmedLine = trim($line);
|
||||||
|
if (str_starts_with($trimmedLine, ') ENGINE')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (str_starts_with($trimmedLine, '`id`') or str_starts_with($trimmedLine, 'KEY')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (str_starts_with($trimmedLine, 'PRIMARY KEY')) {
|
||||||
|
if (str_contains($line, '`id`')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ini = strpos($line, '(') +1;
|
||||||
|
$end = strpos($line, ')', $ini);
|
||||||
|
$this->primary []= substr($line, $ini, $end - $ini);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (str_starts_with($trimmedLine, 'CONSTRAINT')) {
|
||||||
|
$this->constraints []= (new Parser\Constraint($line))->parse();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->columns []= (new Parser\Column($line))->parse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
cache/.gitkeep
vendored
Normal file
0
cache/.gitkeep
vendored
Normal file
21
compose.yml
Normal file
21
compose.yml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: pmg
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: php:cli
|
||||||
|
env_file: ./app/.env
|
||||||
|
restart: no
|
||||||
|
working_dir: /app
|
||||||
|
volumes:
|
||||||
|
- ./app:/app
|
||||||
|
- ./logs:/logs
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: mariadb
|
||||||
|
env_file: .db.env
|
||||||
|
volumes:
|
||||||
|
- pmg-data:/var/lib/mysql
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pmg-data: {}
|
0
logs/.gitkeep
Normal file
0
logs/.gitkeep
Normal file
Reference in New Issue
Block a user