5 Commits

6 changed files with 313 additions and 15 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/vendor
/composer.lock

224
bin/dbmodel-files Normal file
View File

@ -0,0 +1,224 @@
#!/usr/bin/env php
<?php
include $_composer_autoload_path ?? __DIR__ . '/../vendor/autoload.php';
echo " DBmodel files\n\n";
$project_root = findProjectRoot(__DIR__);
if (!$project_root) {
echo "⛔ Project root not found\n";
exit(1);
}
$composer_arr = json_decode(file_get_contents($project_root . '/composer.json'), true);
echo "Project root: {$project_root}\n";
$source_dirs = array_values($composer_arr['autoload']['psr-4']);
foreach ($source_dirs as $index => $source_dir) {
echo "➡️ [$index]: $project_root/$source_dir\n";
}
$selected = readline("❔ Select source dir [default: 0]: ");
$selected = empty($selected) ? 0 : $selected;
if (!isset($source_dirs[$selected])) {
echo "⛔ Invalid selection\n";
exit(2);
}
$source_dir = $project_root . '/' . $source_dirs[$selected];
$source_dir = rtrim($source_dir, '/') . '/';
$namespaces = array_keys($composer_arr['autoload']['psr-4']);
$namespace = $namespaces[$selected];
echo "✔️ Source dir: {$source_dir} with namespace: {$namespace}\n";
// Copy files
echo " Copy files\n";
copyFile($project_root . '/config/Configuration.php', '<' . '?php
$others_config = scandir(__DIR__);
$loaded = false;
foreach ($others_config as $file) {
if ($file == basename(__FILE__)) continue;
if (substr($file, -4) == \'.php\') {
require_once __DIR__ . \'/\' . $file;
$loaded = true;
}
}
if (!$loaded) {
class Configuration
{
public const DB_TYPE = \'sqlite\';
// MySQL
public const DB_HOST = \'localhost\';
public const DB_USER = \'username\';
public const DB_PASS = \'****************\';
public const DB_NAME = \'databasename\';
// SQLite
public const DB_FILEPATH = __DIR__ . \'/../../data/database.db\';
}
}
$protocol = (!empty($_SERVER[\'HTTPS\']) && $_SERVER[\'HTTPS\'] !== \'off\') ? "https://" : "http://";
$host = $_SERVER[\'HTTP_HOST\'];
$uri = $_SERVER[\'REQUEST_URI\']; // obsahuje aj query string
define(\'URL_PREFIX\', $protocol.$host.str_replace(basename($_SERVER[\'SCRIPT_NAME\']), \'\', $_SERVER[\'SCRIPT_NAME\']));
');
copyFile($source_dir . 'Maintenance.php', '<' . '?php
namespace ' . $namespace . ';
class Maintenance extends \TPsoft\DBmodel\Maintenance
{
public function database()
{
if (!$this->existsTable(\'options\')) {
$this->checkDBTable(\'options\', \'
`key` VARCHAR(255) NOT NULL PRIMARY KEY,
`value` VARCHAR(255) NOT NULL
\');
$this->dbver(1);
}
$dbver = $this->dbver();
if ($dbver == 1) {
// create new
}
}
protected function settings(string $key, ?string $value = null): string|false
{
if (is_null($value)) {
return $this->dbh->getOne(sprintf(\'SELECT `value` FROM `options` WHERE `key` = %s\', $this->dbh->quote($key)));
} else {
$db_type = $this->dbh->getDBtype();
switch ($db_type) {
case \'mysql\':
return $this->dbh->query(sprintf(
\'INSERT INTO `options` (`key`, `value`) VALUES (%s, %s) ON DUPLICATE KEY UPDATE `value` = %s\',
$this->dbh->quote($key),
$this->dbh->quote($value),
$this->dbh->quote($value)
)) !== false;
break;
case \'sqlite\':
return $this->dbh->query(sprintf(
\'INSERT INTO `options` (`key`, `value`) VALUES (%s, %s) ON CONFLICT(`key`) DO UPDATE SET `value` = %s\',
$this->dbh->quote($key),
$this->dbh->quote($value),
$this->dbh->quote($value)
)) !== false;
break;
default:
new \Exception(\'Unknown DB type: \' . $db_type);
return false;
break;
}
}
}
protected function dbver(?string $ver = null)
{
return $this->settings(\'version\', $ver);
}
}
');
copyFile($source_dir . 'Init.php', '<' . '?php
require __DIR__ . \'/../vendor/autoload.php\';
use \Exception;
use \TPsoft\DBmodel\DBmodel;
use \\' . $namespace . 'Maintenance;
global $dbh;
if (Configuration::DB_TYPE == \'mysql\') {
$dbh = new DBmodel(sprintf(\'mysql:host=%s;dbname=%s;charset=utf8mb4\', Configuration::DB_HOST, Configuration::DB_NAME), Configuration::DB_USER, Configuration::DB_PASS);
} else if (Configuration::DB_TYPE == \'sqlite\') {
$dbh = new DBmodel(sprintf(\'sqlite:%s\', Configuration::DB_FILEPATH));
} else {
throw new Exception(\'Unknown database type\');
}
$maintenance = new Maintenance($dbh);
$maintenance->database();
');
copyFile($project_root . '/scripts/createModel.php', '<' . '?php
require_once __DIR__ . \'/../src/Init.php\';
use \TPsoft\DBmodel\Creator;
global $dbh;
$creator = new Creator($dbh);
$creator->rootDir(realpath(__DIR__.\'/../src/\'));
$creator->interact();
');
// Change composer.json
if (!isset($composer_arr['autoload']['files'])) {
$composer_arr['autoload']['files'] = [];
}
$composer_arr['autoload']['files'][] = 'config/Configuration.php';
if (!isset($composer_arr['scripts'])) {
$composer_arr['scripts'] = [];
}
if (!isset($composer_arr['scripts']['model'])) {
$composer_arr['scripts']['model'] = 'php scripts/createModel.php';
}
$composer_json = json_encode($composer_arr, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$composer_json = preg_replace_callback(
'/^( +)/m',
fn($m) => str_repeat("\t", intdiv(strlen($m[1]), 4)),
$composer_json
);
$suc = file_put_contents($project_root . '/composer.json', $composer_json);
if ($suc) {
echo "✅ File changed: $project_root/composer.json\n";
} else {
echo "🔴 Failed to change file: $project_root/composer.json\n";
}
/**
* Functions
*/
function findProjectRoot(string $startDir): ?string
{
$dir = realpath($startDir);
if (!$dir) return null;
while ($dir && $dir !== dirname($dir)) {
if (is_file($dir . '/vendor/autoload.php')) {
return $dir;
}
$dir = dirname($dir);
}
return null;
}
function copyFile(string $destination, string $content): bool
{
$dir = dirname($destination);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
if (file_exists($destination)) {
echo "⚠️ File already exists: $destination\n";
$confirm = readline("❔ Overwrite? (y - yes, other - no): ");
if ($confirm != 'y') {
return false;
}
}
$suc = file_put_contents($destination, $content) !== false;
if ($suc) {
echo "✅ File created: $destination\n";
} else {
echo "🔴 Failed to create file: $destination\n";
}
return $suc;
}

View File

@ -4,7 +4,7 @@
"description": "This library extends the builtin PDO object by several useful features. ", "description": "This library extends the builtin PDO object by several useful features. ",
"type": "library", "type": "library",
"readme": "README.md", "readme": "README.md",
"keywords": ["db", "model", "pdo"], "keywords": ["db", "model", "pdo", "mysql", "sqlite"],
"authors": [ "authors": [
{ {
"name": "Igor Mino", "name": "Igor Mino",
@ -27,6 +27,9 @@
"psr-4": { "psr-4": {
"TPsoft\\DBmodel\\": "src/" "TPsoft\\DBmodel\\": "src/"
} }
} },
"bin": [
"bin/dbmodel-files"
]
} }

View File

@ -21,8 +21,9 @@ class Creator {
public function interact() { public function interact() {
$name = $this->readline('New model name: '); $name = $this->readline('New model name: ');
$table = $this->readline('Table name: '); $table = strtolower($name);
$entity = $this->readline('Entity name: '); $table = $this->readline('Table name ['.$table.']: ', $table);
$entity = $this->readline('Entity name ['.$table.']: ', $table);
$namespace = $this->readline('Namespace [App\Models]: ', 'App\Models'); $namespace = $this->readline('Namespace [App\Models]: ', 'App\Models');
if (strlen($entity) <= 0) { if (strlen($entity) <= 0) {
$entity = $table; $entity = $table;
@ -151,15 +152,19 @@ class ".$name." extends \TPsoft\DBmodel\DBmodel {
->toArray(); ->toArray();
} }
public function getListByID($"."search = array(), $"."reverse = false, $"."concat_or = false) { public function getListOrganize($"."cola_name, $"."search = array(), $"."reverse = false, $"."concat_or = false) {
$"."all = $"."this->getList($"."search, $"."reverse, $"."concat_or); $"."all = $"."this->getList($"."search, $"."reverse, $"."concat_or);
$"."ret = array(); $"."ret = array();
if (is_array($"."all)) foreach ($"."all as $"."key => $"."row) { if (is_array($"."all)) foreach ($"."all as $"."key => $"."row) {
$"."ret[$"."row[$primary_key_name]] = $"."row; $"."ret[$"."row[$"."cola_name]] = $"."row;
} }
return $"."ret; return $"."ret;
} }
public function getListByID($"."search = array(), $"."reverse = false, $"."concat_or = false) {
return $"."this->getListOrganize($primary_key_name, $"."search, $"."reverse, $"."concat_or);
}
public function ".$entity_one."Combo($"."col_key, $"."col_value, $"."add_empty = false) { public function ".$entity_one."Combo($"."col_key, $"."col_value, $"."add_empty = false) {
return $"."this->search('".$entity."') return $"."this->search('".$entity."')
->toCombo($"."col_key, $"."col_value, $"."add_empty); ->toCombo($"."col_key, $"."col_value, $"."add_empty);

View File

@ -154,6 +154,11 @@ class DBmodel
return $where; return $where;
} }
public function getDBtype()
{
return $this->dbh->getAttribute(\PDO::ATTR_DRIVER_NAME);
}
public function existTable($table_id) public function existTable($table_id)
{ {
$query = sprintf( $query = sprintf(
@ -269,11 +274,14 @@ class DBmodel
return false; return false;
} }
if (count($primary_key_names) > 1) return true; if (count($primary_key_names) > 1) return true;
$last_id = $this->getLastInsertID();
/*
if (method_exists($this->dbh, 'getLastInsertID')) { if (method_exists($this->dbh, 'getLastInsertID')) {
$last_id = $this->getLastInsertID(); $last_id = $this->getLastInsertID();
} else { } else {
$last_id = $this->getOne('SELECT last_insert_id()'); $last_id = $this->getOne('SELECT last_insert_id()');
} }
*/
return $last_id; return $last_id;
} }
@ -304,12 +312,14 @@ class DBmodel
$query = sprintf( $query = sprintf(
'UPDATE %s' 'UPDATE %s'
. ' SET %s' . ' SET %s'
. ' WHERE %s' . ' WHERE %s',
. ' LIMIT 1',
$table, $table,
$set, $set,
$this->buildWherePrimaryKey($table_id, $primary_key) $this->buildWherePrimaryKey($table_id, $primary_key)
); );
if ($this->getDBtype() == 'mysql') {
$query .= ' LIMIT 1';
}
$this->_debug('[DBmodel][record][UPDATE]: ' . $query); $this->_debug('[DBmodel][record][UPDATE]: ' . $query);
$ret = $this->query($query); $ret = $this->query($query);
if ($ret === false) { if ($ret === false) {
@ -331,11 +341,13 @@ class DBmodel
} }
$query = sprintf( $query = sprintf(
'DELETE FROM %s' 'DELETE FROM %s'
. ' WHERE %s' . ' WHERE %s',
. ' LIMIT 1',
$table, $table,
$this->buildWherePrimaryKey($table_id, $primary_key) $this->buildWherePrimaryKey($table_id, $primary_key)
); );
if ($this->getDBtype() == 'mysql') {
$query .= ' LIMIT 1';
}
$this->_debug('[DBmodel][record][DELETE]: ' . $query); $this->_debug('[DBmodel][record][DELETE]: ' . $query);
$ret = $this->query($query); $ret = $this->query($query);
if ($ret === false) { if ($ret === false) {
@ -548,6 +560,15 @@ class DBmodel
} }
public function getTableColumns($table_name) { public function getTableColumns($table_name) {
$db_type = $this->getDBtype();
switch ($db_type) {
case 'mysql': return $this->getTableColumnsMYSQL($table_name);
case 'sqlite': return $this->getTableColumnsSQLITE($table_name);
default: new \Exception('Unknown DB type: ' . $db_type);
}
}
public function getTableColumnsMYSQL($table_name) {
$desc = $this->getAll(sprintf('DESC %s', $table_name)); $desc = $this->getAll(sprintf('DESC %s', $table_name));
$desc = $this->arrayKeysToLowerCase($desc); $desc = $this->arrayKeysToLowerCase($desc);
$pks = array(); $pks = array();
@ -567,6 +588,26 @@ class DBmodel
return array('pks' => $pks, 'columns' => $columns, 'types' => $types); return array('pks' => $pks, 'columns' => $columns, 'types' => $types);
} }
public function getTableColumnsSQLITE($table_name) {
$desc = $this->getAll(sprintf('PRAGMA table_info(%s)', $table_name));
$desc = $this->arrayKeysToLowerCase($desc);
$pks = array();
$columns = array();
foreach ($desc as $d) {
$colname = $d['name'];
$types[$colname] = $d['type'];
if ($d['pk'] == 1) {
$pks[] = $colname;
if (strtoupper($d['type']) != 'INTEGER') {
$columns[] = $colname;
}
} else {
$columns[] = $colname;
}
}
return array('pks' => $pks, 'columns' => $columns, 'types' => $types);
}
/* ---------------------------------------------------- /* ----------------------------------------------------
* HELPER METHODS * HELPER METHODS
*/ */

View File

@ -81,10 +81,21 @@ class Maintenance
public function existsTable($table_name) public function existsTable($table_name)
{ {
$db_type = $this->dbh->getDBtype();
switch ($db_type) {
case 'mysql':
return $this->testDB( return $this->testDB(
'SHOW TABLES LIKE "' . $table_name . '"', 'SHOW TABLES LIKE "' . $table_name . '"',
$table_name $table_name
); );
case 'sqlite':
return $this->testDB(
'SELECT name FROM sqlite_master WHERE TYPE = "table" AND name LIKE "' . $table_name . '"',
$table_name
);
default:
new \Exception('Unknown DB type: ' . $db_type);
}
} }
public function checkDBTable($table_name, $definition, $after_definition = '') public function checkDBTable($table_name, $definition, $after_definition = '')
@ -93,9 +104,21 @@ class Maintenance
$this->lastMessage = 'Table ' . strtoupper($table_name) . ' - invalid index'; $this->lastMessage = 'Table ' . strtoupper($table_name) . ' - invalid index';
return Maintenance::ABNORMAL; return Maintenance::ABNORMAL;
} }
$check_query = '';
$db_type = $this->dbh->getDBtype();
switch ($db_type) {
case 'mysql':
$check_query = 'SHOW TABLES LIKE "' . $table_name . '"';
break;
case 'sqlite':
$check_query = 'SELECT name FROM sqlite_master WHERE TYPE = "table" AND name LIKE "' . $table_name . '"';
break;
default:
new \Exception('Unknown DB type: ' . $db_type);
}
$status = $this->checkDB( $status = $this->checkDB(
// TEST query // TEST query
'SHOW TABLES LIKE "' . $table_name . '"', $check_query,
// TEST output // TEST output
$table_name, $table_name,
// FIX query // FIX query