first commit
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use Flight;
|
||||
use flight\commands\AbstractBaseCommand;
|
||||
use Webapp\Data\Cache;
|
||||
|
||||
/**
|
||||
* Generate all js app cache for all user
|
||||
*
|
||||
* Usage: php runway user:cache
|
||||
*
|
||||
*/
|
||||
|
||||
class CacheCommand extends AbstractBaseCommand {
|
||||
/**
|
||||
* Construct
|
||||
* @param array<string,mixed> $config From .runway-config.json
|
||||
*/
|
||||
public function __construct(array $config) {
|
||||
parent::__construct('user:cache', 'Generate all js app cache for all user', $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
$io = $this->io();
|
||||
|
||||
require ($this->projectRoot . '/app/config/runway.php');
|
||||
$asset = require($this->projectRoot . '/app/config/asset.php');
|
||||
|
||||
$cache = new Cache($app);
|
||||
$cache->set_asset($asset);
|
||||
$ares = $cache->Make_Cache();
|
||||
if (is_array($ares)) $io->info("Processed of {$ares['user_count']} user.", true);
|
||||
$io->ok("Done.", true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Utils\Config;
|
||||
use App\Utils\DBFactory;
|
||||
use App\Utils\Env;
|
||||
use flight\commands\AbstractBaseCommand;
|
||||
use flight\database\SimplePdo;
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Apply pending SQL migrations from migrations
|
||||
*
|
||||
* Usage: php runway migrate
|
||||
*
|
||||
* Files (driver selected from config / .env DB_DRIVER):
|
||||
* SQLite (default): migrations/{YYYYMMDDHHMMSS}_{description}.sql
|
||||
* MySQL: migrations/{YYYYMMDDHHMMSS}_{description}.mysql.sql
|
||||
*
|
||||
* Tracking table: _migrations (id, name, applied_at)
|
||||
*
|
||||
*/
|
||||
class MigrateCommand extends AbstractBaseCommand {
|
||||
/**
|
||||
* @param array<string,mixed> $config From .runway-config.json
|
||||
*/
|
||||
public function __construct(array $config) {
|
||||
parent::__construct('migrate', 'Apply pending SQL migrations from migrations', $config);
|
||||
}
|
||||
|
||||
public function execute(): void {
|
||||
$io = $this->io();
|
||||
$projectRoot = getcwd();
|
||||
if ($projectRoot === false) {
|
||||
$io->error('Unable to determine project root (getcwd failed).', true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$appConfig = $this->loadAppConfig($projectRoot);
|
||||
$db = DBFactory::create($appConfig);
|
||||
} catch (\Throwable $e) {
|
||||
$io->error($e->getMessage(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
$migrationsDir = $projectRoot . DIRECTORY_SEPARATOR . 'migrations';
|
||||
if (!is_dir($migrationsDir)) {
|
||||
$io->error('migrations/ directory not found.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
$driver = strtolower((string) $appConfig->get('database.driver', 'sqlite'));
|
||||
$this->ensureMigrationsTable($db, $driver);
|
||||
|
||||
$applied = $this->getAppliedNames($db);
|
||||
$files = $this->listMigrationFiles($migrationsDir, $driver);
|
||||
|
||||
$pending = 0;
|
||||
foreach ($files as $file) {
|
||||
$name = basename($file);
|
||||
if (isset($applied[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pending++;
|
||||
$sql = file_get_contents($file);
|
||||
if ($sql === false || trim($sql) === '') {
|
||||
$io->error("Skipping empty or unreadable migration: {$name}", true);
|
||||
continue;
|
||||
}
|
||||
|
||||
$io->info("Applying {$name}...", true);
|
||||
|
||||
try {
|
||||
$this->applyMigration($db, $name, $sql);
|
||||
$io->ok(" applied {$name}", true);
|
||||
} catch (PDOException $e) {
|
||||
$io->error(" failed {$name}: " . $e->getMessage(), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($pending === 0) {
|
||||
$io->info('No pending migrations.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
$io->ok("Done. Applied {$pending} migration(s).", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick migration files for the active driver.
|
||||
*
|
||||
* - sqlite (default): *.sql excluding *.mysql.sql
|
||||
* - mysql: *.mysql.sql only
|
||||
*
|
||||
* @return array<int,string> Absolute paths, sorted
|
||||
*/
|
||||
public static function listMigrationFiles(string $migrationsDir, string $driver): array {
|
||||
$driver = strtolower($driver);
|
||||
$all = glob($migrationsDir . DIRECTORY_SEPARATOR . '*.sql');
|
||||
if ($all === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$files = [];
|
||||
foreach ($all as $file) {
|
||||
$name = basename($file);
|
||||
$isMysql = self::isMysqlMigrationName($name);
|
||||
|
||||
if ($driver === 'mysql') {
|
||||
if ($isMysql) {
|
||||
$files[] = $file;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// sqlite and any other non-mysql default: plain .sql only
|
||||
if (!$isMysql) {
|
||||
$files[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
sort($files, SORT_STRING);
|
||||
return $files;
|
||||
}
|
||||
|
||||
public static function isMysqlMigrationName(string $basename): bool {
|
||||
return (bool) preg_match('/\.mysql\.sql$/', $basename);
|
||||
}
|
||||
|
||||
private function loadAppConfig(string $projectRoot): Config {
|
||||
Env::load($projectRoot . DIRECTORY_SEPARATOR . '.env');
|
||||
|
||||
$configFile = $projectRoot . DIRECTORY_SEPARATOR . 'app'
|
||||
. DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php';
|
||||
|
||||
if (!is_file($configFile)) {
|
||||
throw new \RuntimeException(
|
||||
'app/config/config.php not found. Copy config_sample.php first.'
|
||||
);
|
||||
}
|
||||
|
||||
$fileConfig = require $configFile;
|
||||
if (!is_array($fileConfig)) {
|
||||
throw new \RuntimeException('config.php must return an array.');
|
||||
}
|
||||
|
||||
$merged = Config::mergeEnv($fileConfig, $_ENV);
|
||||
return new Config($merged);
|
||||
}
|
||||
|
||||
private function ensureMigrationsTable(SimplePdo $db, string $driver): void {
|
||||
if ($driver === 'mysql') {
|
||||
$db->exec(
|
||||
'CREATE TABLE IF NOT EXISTS `_migrations` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`applied_at` VARCHAR(64) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_migrations_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$db->exec(
|
||||
'CREATE TABLE IF NOT EXISTS _migrations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
applied_at TEXT NOT NULL
|
||||
)'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,true>
|
||||
*/
|
||||
private function getAppliedNames(SimplePdo $db): array {
|
||||
$rows = $db->fetchAll('SELECT name FROM _migrations ORDER BY name ASC');
|
||||
$map = [];
|
||||
foreach ($rows as $row) {
|
||||
// SimplePdo::fetchAll returns Collection (ArrayAccess) or array rows
|
||||
$name = $row['name'] ?? null;
|
||||
if ($name !== null && $name !== '') {
|
||||
$map[(string) $name] = true;
|
||||
}
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
|
||||
private function applyMigration(SimplePdo $db, string $name, string $sql): void
|
||||
{
|
||||
$driver = $db->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||
$useTransaction = in_array($driver, ['sqlite', 'mysql', 'pgsql'], true);
|
||||
|
||||
if ($useTransaction) {
|
||||
$db->beginTransaction();
|
||||
}
|
||||
|
||||
try {
|
||||
// Split on semicolons at end of lines for multi-statement files
|
||||
$statements = $this->splitSql($sql);
|
||||
foreach ($statements as $statement) {
|
||||
$db->exec($statement);
|
||||
}
|
||||
|
||||
$stmt = $db->prepare(
|
||||
'INSERT INTO _migrations (name, applied_at) VALUES (?, ?)'
|
||||
);
|
||||
$stmt->execute([$name, date('c')]);
|
||||
|
||||
if ($useTransaction && $db->inTransaction()) {
|
||||
$db->commit();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
if ($useTransaction && $db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,string>
|
||||
*/
|
||||
private function splitSql(string $sql): array {
|
||||
// Strip line comments
|
||||
$lines = preg_split('/\R/', $sql);
|
||||
$cleaned = [];
|
||||
if ($lines !== false) {
|
||||
foreach ($lines as $line) {
|
||||
$trim = ltrim($line);
|
||||
if (strpos($trim, '--') === 0) {
|
||||
continue;
|
||||
}
|
||||
$cleaned[] = $line;
|
||||
}
|
||||
}
|
||||
$body = implode("\n", $cleaned);
|
||||
|
||||
$parts = preg_split('/;\s*[\r\n]+/', $body);
|
||||
$out = [];
|
||||
if ($parts !== false) {
|
||||
foreach ($parts as $part) {
|
||||
$part = trim($part);
|
||||
if ($part !== '') {
|
||||
$out[] = $part;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Utils\PPOBClient;
|
||||
use Flight;
|
||||
use flight\commands\AbstractBaseCommand;
|
||||
|
||||
/**
|
||||
* Check Status of PPOB Transaction from Server
|
||||
*
|
||||
* Usage: php runway ppob:check
|
||||
*
|
||||
*/
|
||||
|
||||
class PPOBCommand extends AbstractBaseCommand {
|
||||
/**
|
||||
* Construct
|
||||
* @param array<string,mixed> $config From .runway-config.json
|
||||
*/
|
||||
public function __construct(array $config) {
|
||||
parent::__construct('ppob:check', 'Check Status of PPOB Transaction from Server', $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
$io = $this->io();
|
||||
|
||||
require ($this->projectRoot . '/app/config/runway.php');
|
||||
|
||||
$ppob = new PPOBClient($app);
|
||||
$status = $ppob->CheckTrx();
|
||||
$io->comment("Status : ".$status['Status'], true);
|
||||
$io->comment("Message : ".$status['Message'], true);
|
||||
$io->ok("Done.", true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Utils\Inv;
|
||||
use Flight;
|
||||
use flight\commands\AbstractBaseCommand;
|
||||
|
||||
/**
|
||||
* Generate join all product parameter on single table
|
||||
*
|
||||
* Usage: php runway produk:join
|
||||
*
|
||||
*/
|
||||
|
||||
class ProdukCommand extends AbstractBaseCommand {
|
||||
/**
|
||||
* Construct
|
||||
* @param array<string,mixed> $config From .runway-config.json
|
||||
*/
|
||||
public function __construct(array $config) {
|
||||
parent::__construct('produk:join', 'Generate join all product parameter on single table', $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
$io = $this->io();
|
||||
|
||||
require ($this->projectRoot . '/app/config/runway.php');
|
||||
|
||||
$produk = new Inv($app);
|
||||
$produk->Produk_Join(0);
|
||||
$io->ok("Done.", true);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user