5 Commits

Author SHA1 Message Date
2dbaa2bf77 added bin/dbmodel-files for generate files as best-practice for new project 2026-02-09 06:08:51 +01:00
80e889946b improved Maintenance for MySQL and SQLite DB type 2025-10-13 23:26:19 +02:00
cf59109a47 extend compatibility for SQLite3 2025-10-01 01:12:46 +02:00
ae59ffaa97 added getListOrganize for created new model 2025-06-15 19:07:42 +02:00
616dc01c21 added namespace for created new model,
added method getListByID for new model,
added type of object for import method,
extends definition tor checkDBTable,
fixed typo in parameters of method existsColumn
2025-06-12 07:48:08 +02:00
6 changed files with 330 additions and 21 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);
while ($dir && $dir !== dirname($dir)) {
if (file_exists($dir . '/composer.json')) {
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. ",
"type": "library",
"readme": "README.md",
"keywords": ["db", "model", "pdo"],
"keywords": ["db", "model", "pdo", "mysql", "sqlite"],
"authors": [
{
"name": "Igor Mino",
@ -27,6 +27,9 @@
"psr-4": {
"TPsoft\\DBmodel\\": "src/"
}
}
},
"bin": [
"bin/dbmodel-files"
]
}

View File

@ -21,12 +21,14 @@ class Creator {
public function interact() {
$name = $this->readline('New model name: ');
$table = $this->readline('Table name: ');
$entity = $this->readline('Entity name: ');
$table = strtolower($name);
$table = $this->readline('Table name ['.$table.']: ', $table);
$entity = $this->readline('Entity name ['.$table.']: ', $table);
$namespace = $this->readline('Namespace [App\Models]: ', 'App\Models');
if (strlen($entity) <= 0) {
$entity = $table;
}
$model_filepath = $this->rootDir().'/models/'.$name.'.php';
$model_filepath = $this->rootDir().'/Models/'.$name.'.php';
if ($this->readline("Create MODEL class '$model_filepath'? (y - yes, other - no) ") != 'y') {
echo "Creating of MODEL class is skipped\n";
return false;
@ -47,11 +49,11 @@ class Creator {
}
}
$table_columns = $this->dbh->getTableColumns($table);
$suc = $this->modelSave($model_filepath, $name, $table, $entity, $table_columns['pks'], $table_columns['columns'], $table_columns['types']);
$suc = $this->modelSave($model_filepath, $name, $table, $entity, $table_columns['pks'], $table_columns['columns'], $table_columns['types'], $namespace);
echo $suc ? "MODEL class created" : 'Error: MODEL class not created';
}
private function modelSave($filepath, $name, $tablename, $entity, $pks, $columns, $types) {
private function modelSave($filepath, $name, $tablename, $entity, $pks, $columns, $types, $namespace) {
if (is_array($pks) && count($pks) > 1) {
$primary_key_name = "array('".implode("', '", $pks)."')";
$columns = array_merge($pks, $columns);
@ -93,6 +95,7 @@ class Creator {
$data[\'changed_dt\'] = date(\'Y-m-d H:i:s\');
}'
: '';
$namespace_str = strlen($namespace) > 0 ? "\nnamespace $namespace;\n" : '';
$content = '<?'."php
/*
TPsoft.org 2000-".date('Y')."
@ -101,7 +104,7 @@ class Creator {
Milestones:
".date('Y-m-d H:i')." Created
*/
".$namespace_str."
class ".$name." extends \TPsoft\DBmodel\DBmodel {
public $"."tables = array(
@ -149,6 +152,19 @@ class ".$name." extends \TPsoft\DBmodel\DBmodel {
->toArray();
}
public function getListOrganize($"."cola_name, $"."search = array(), $"."reverse = false, $"."concat_or = false) {
$"."all = $"."this->getList($"."search, $"."reverse, $"."concat_or);
$"."ret = array();
if (is_array($"."all)) foreach ($"."all as $"."key => $"."row) {
$"."ret[$"."row[$"."cola_name]] = $"."row;
}
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) {
return $"."this->search('".$entity."')
->toCombo($"."col_key, $"."col_value, $"."add_empty);

View File

@ -154,6 +154,11 @@ class DBmodel
return $where;
}
public function getDBtype()
{
return $this->dbh->getAttribute(\PDO::ATTR_DRIVER_NAME);
}
public function existTable($table_id)
{
$query = sprintf(
@ -269,11 +274,14 @@ class DBmodel
return false;
}
if (count($primary_key_names) > 1) return true;
$last_id = $this->getLastInsertID();
/*
if (method_exists($this->dbh, 'getLastInsertID')) {
$last_id = $this->getLastInsertID();
} else {
$last_id = $this->getOne('SELECT last_insert_id()');
}
*/
return $last_id;
}
@ -304,12 +312,14 @@ class DBmodel
$query = sprintf(
'UPDATE %s'
. ' SET %s'
. ' WHERE %s'
. ' LIMIT 1',
. ' WHERE %s',
$table,
$set,
$this->buildWherePrimaryKey($table_id, $primary_key)
);
if ($this->getDBtype() == 'mysql') {
$query .= ' LIMIT 1';
}
$this->_debug('[DBmodel][record][UPDATE]: ' . $query);
$ret = $this->query($query);
if ($ret === false) {
@ -331,11 +341,13 @@ class DBmodel
}
$query = sprintf(
'DELETE FROM %s'
. ' WHERE %s'
. ' LIMIT 1',
. ' WHERE %s',
$table,
$this->buildWherePrimaryKey($table_id, $primary_key)
);
if ($this->getDBtype() == 'mysql') {
$query .= ' LIMIT 1';
}
$this->_debug('[DBmodel][record][DELETE]: ' . $query);
$ret = $this->query($query);
if ($ret === false) {
@ -548,6 +560,15 @@ class DBmodel
}
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->arrayKeysToLowerCase($desc);
$pks = array();
@ -567,6 +588,26 @@ class DBmodel
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
*/
@ -851,7 +892,7 @@ class DBmodel
return $this->allowNextError();
}
public function import($objModel)
public function import(DBmodel $objModel)
{
if (is_null($objModel)) return false;
if (

View File

@ -81,25 +81,48 @@ class Maintenance
public function existsTable($table_name)
{
$db_type = $this->dbh->getDBtype();
switch ($db_type) {
case 'mysql':
return $this->testDB(
'SHOW TABLES LIKE "' . $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)
public function checkDBTable($table_name, $definition, $after_definition = '')
{
if (strlen($table_name) <= 0) {
$this->lastMessage = 'Table ' . strtoupper($table_name) . ' - invalid index';
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(
// TEST query
'SHOW TABLES LIKE "' . $table_name . '"',
$check_query,
// TEST output
$table_name,
// FIX query
'CREATE TABLE `' . $table_name . '` (' . $definition . ')'
'CREATE TABLE `' . $table_name . '` (' . $definition . ') ' . $after_definition
);
$this->lastMessage = 'Table ' . strtoupper($table_name) . ' (' . $table_name . ')';
return $status;
@ -146,7 +169,7 @@ class Maintenance
public function checkDBRetype($table_name, $column, $new_type, $definition)
{
if (!$this->existsColumn($table_name, $column_old)) {
if (!$this->existsColumn($table_name, $column)) {
return Maintenance::ABNORMAL;
}
$status = $this->checkDB(