Compare commits

...

10 Commits
v1.0.0 ... main

Author SHA1 Message Date
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
877ca38c0b added autochatc database handler from DBmodel instance,
added set rootDir for Creator,
added deduct numeric (unix timestamp) for datetime column,
added method existsTable for Maintenance
2025-06-01 19:22:50 +02:00
8a3dfbe9f0 added info to README about Maintenance 2025-05-29 23:13:18 +02:00
d055bd1132 removed test files 2025-05-29 23:00:34 +02:00
c2773db2dd fixed database handler for DBmodel in Maintenance,
added test4 for Maintenance
2025-05-29 22:55:10 +02:00
0642d7a8ca added tootl for Maintenance 2025-05-29 08:52:03 +02:00
55e67bd8c2 Merge branch 'main' of https://gitea.tpsoft.org/TPsoft.org/DBmodel 2025-05-29 07:53:33 +02:00
136e4905d6 renamed file creator.php to Creator.php 2025-05-29 07:52:21 +02:00
ae4f73ecb6 added code types into README 2025-05-28 18:14:39 +02:00
6 changed files with 386 additions and 22 deletions

View File

@ -6,8 +6,7 @@ This library extends the builtin PDO object by several useful features.
For testing create database `test` with user `test` and password `test`. In database create table `test` and insert same testing data. For testing create database `test` with user `test` and password `test`. In database create table `test` and insert same testing data.
``` ```sql
CREATE TABLE `test` ( CREATE TABLE `test` (
`id` int(11) PRIMARY KEY NOT NULL AUTO_INCREMENT, `id` int(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL,
@ -18,7 +17,7 @@ CREATE TABLE `test` (
For basic testing run this code For basic testing run this code
``` ```php
<?php <?php
require_once __DIR__.'/../src/DBmodel.php'; require_once __DIR__.'/../src/DBmodel.php';
@ -53,7 +52,7 @@ print_r($result);
Create file `creatorModel.php` and paste bellow code Create file `creatorModel.php` and paste bellow code
``` ```php
<?php <?php
require_once __DIR__.'/../src/DBmodel.php'; require_once __DIR__.'/../src/DBmodel.php';
@ -70,7 +69,7 @@ $creator->interact();
now for each table run `php createModel.php` for interactive creating class for table. now for each table run `php createModel.php` for interactive creating class for table.
``` ```bash
$ php test2.php $ php test2.php
New model name: Test New model name: Test
Table name: test Table name: test
@ -82,7 +81,7 @@ Creating MODEL class ... MODEL class created
Creator build this MODEL class file Creator build this MODEL class file
``` ```php
<?php <?php
/* /*
TPsoft.org 2000-2025 TPsoft.org 2000-2025
@ -155,7 +154,7 @@ class Test extends \TPsoft\DBmodel\DBmodel {
Illustrative example Illustrative example
``` ```php
<?php <?php
require_once __DIR__.'/../src/DBmodel.php'; require_once __DIR__.'/../src/DBmodel.php';
@ -196,3 +195,59 @@ print_r($result);
?> ?>
``` ```
## Maintenance of database schema
When developing a project, a situation arises where the DB schema needs to be modified and distributed to all installations. To ease these problems, the Maintenance class was created, which contains several useful methods. These first check if the schema contains the requested change and if it doesn't, they apply a FIX. Here is a short demonstration of what we can fix automatically in the project when changing the DB schema.
```php
<?php
require_once __DIR__.'/../src/DBmodel.php';
require_once __DIR__.'/../src/Maintenance.php';
$db = new \TPsoft\DBmodel\DBmodel('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'test', 'test');
$maintenance = new \TPsoft\DBmodel\Maintenance($db);
// Check if existing table, if not create it by definition
$suc = $maintenance->checkDBTable('users', '
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
');
echo "\nCheckDBTable: "; var_dump($suc);
// Check if existing column, if not add it by definition
$suc = $maintenance->checkDBAdd('users', 'password', 'varchar(255) DEFAULT NULL');
echo "\nCheckDBAdd: "; var_dump($suc);
// Check if existing column
$suc = $maintenance->existsColumn('users', 'email');
echo "\nExistsColumn: "; var_dump($suc);
// Check if existing column in new name, if not rename it
$suc = $maintenance->checkDBRename('users', 'email', 'email_new', 'varchar(255) DEFAULT NULL');
echo "\nCheckDBRename: "; var_dump($suc);
// Chceck type of column, if not matched change it
$suc = $maintenance->checkDBRetype('users', 'email_new', 'varchar(128)', 'varchar(128) DEFAULT NULL AFTER `password`');
echo "\nCheckDBRetype: "; var_dump($suc);
// Remove column from table
$suc = $maintenance->checkDBRemove('users', 'email_new');
echo "\nCheckDBRemove: "; var_dump($suc);
// Add index for column
$suc = $maintenance->addKey('users', 'name');
echo "\nAddKey: "; var_dump($suc);
// Drop index by column name
$suc = $maintenance->dropKey('users', 'name');
echo "\nDropKey: "; var_dump($suc);
echo "\nDone\n\n";
?>
```

View File

@ -4,20 +4,30 @@ namespace TPsoft\DBmodel;
class Creator { class Creator {
private $dbh; private DBmodel $dbh;
private string $rootDir = '';
public function __construct($dbh) { public function __construct(?DBmodel $dbh = null) {
$this->dbh = $dbh; if (is_null($dbh)) {
if (!is_null(DBmodel::$instance)) {
$this->dbh = DBmodel::$instance;
} else {
throw new \Exception('DB handler is null');
}
} else {
$this->dbh = $dbh;
}
} }
public function interact() { public function interact() {
$name = $this->readline('New model name: '); $name = $this->readline('New model name: ');
$table = $this->readline('Table name: '); $table = $this->readline('Table name: ');
$entity = $this->readline('Entity name: '); $entity = $this->readline('Entity name: ');
$namespace = $this->readline('Namespace [App\Models]: ', 'App\Models');
if (strlen($entity) <= 0) { if (strlen($entity) <= 0) {
$entity = $table; $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') { if ($this->readline("Create MODEL class '$model_filepath'? (y - yes, other - no) ") != 'y') {
echo "Creating of MODEL class is skipped\n"; echo "Creating of MODEL class is skipped\n";
return false; return false;
@ -38,11 +48,11 @@ class Creator {
} }
} }
$table_columns = $this->dbh->getTableColumns($table); $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'; 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) { if (is_array($pks) && count($pks) > 1) {
$primary_key_name = "array('".implode("', '", $pks)."')"; $primary_key_name = "array('".implode("', '", $pks)."')";
$columns = array_merge($pks, $columns); $columns = array_merge($pks, $columns);
@ -84,6 +94,7 @@ class Creator {
$data[\'changed_dt\'] = date(\'Y-m-d H:i:s\'); $data[\'changed_dt\'] = date(\'Y-m-d H:i:s\');
}' }'
: ''; : '';
$namespace_str = strlen($namespace) > 0 ? "\nnamespace $namespace;\n" : '';
$content = '<?'."php $content = '<?'."php
/* /*
TPsoft.org 2000-".date('Y')." TPsoft.org 2000-".date('Y')."
@ -92,12 +103,12 @@ class Creator {
Milestones: Milestones:
".date('Y-m-d H:i')." Created ".date('Y-m-d H:i')." Created
*/ */
".$namespace_str."
class ".$name." extends \TPsoft\DBmodel\DBmodel { class ".$name." extends \TPsoft\DBmodel\DBmodel {
public $"."tables = array( public $"."tables = array(
'".$entity."' => array( '".$entity."' => array(
'name' => '".$entity."', 'name' => '".$tablename."',
'primary_key_name' => ".$primary_key_name.", 'primary_key_name' => ".$primary_key_name.",
'allow_attributes' => array( 'allow_attributes' => array(
".implode(",\n\t\t\t\t", $allowed_attributes)." ".implode(",\n\t\t\t\t", $allowed_attributes)."
@ -140,6 +151,19 @@ class ".$name." extends \TPsoft\DBmodel\DBmodel {
->toArray(); ->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) { 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);
@ -166,11 +190,14 @@ class ".$name." extends \TPsoft\DBmodel\DBmodel {
return $line; return $line;
} }
public function rootDir() { public function rootDir(?string $dir = null) {
if (!is_null($dir)) {
$this->rootDir = $dir;
}
if (strlen($this->rootDir) > 0) return $this->rootDir;
return dirname(dirname(__FILE__)); return dirname(dirname(__FILE__));
} }
} }
?> ?>

View File

@ -27,7 +27,7 @@ class DBmodel
public $log = null; public $log = null;
public $tables = array(); public $tables = array();
public function __construct(string $dsn = null, ?string $username = null, #[\SensitiveParameter] ?string $password = null, ?array $options = null) public function __construct(?string $dsn = null, ?string $username = null, #[\SensitiveParameter] ?string $password = null, ?array $options = null)
{ {
if (is_null($dsn)) { if (is_null($dsn)) {
if (DBmodel::$instance) { if (DBmodel::$instance) {
@ -674,12 +674,12 @@ class DBmodel
foreach ($data[$column] as $key => $val) { foreach ($data[$column] as $key => $val) {
$prefix = $this->extractPrefix($val, $where_prefixes); $prefix = $this->extractPrefix($val, $where_prefixes);
$n_val = str_replace($prefix, '', $val); $n_val = str_replace($prefix, '', $val);
$data[$column] = $prefix . date('Y-m-d H:i:s', strtotime($n_val)); $data[$column] = $prefix . date('Y-m-d H:i:s', is_numeric($n_val) ? $n_val : strtotime($n_val));
} }
} else { } else {
$prefix = $this->extractPrefix($data[$column], $where_prefixes); $prefix = $this->extractPrefix($data[$column], $where_prefixes);
$n_val = str_replace($prefix, '', $data[$column]); $n_val = str_replace($prefix, '', $data[$column]);
$data[$column] = $prefix . date('Y-m-d H:i:s', strtotime($n_val)); $data[$column] = $prefix . date('Y-m-d H:i:s', is_numeric($n_val) ? $n_val : strtotime($n_val));
} }
break; break;
} }
@ -851,7 +851,7 @@ class DBmodel
return $this->allowNextError(); return $this->allowNextError();
} }
public function import($objModel) public function import(DBmodel $objModel)
{ {
if (is_null($objModel)) return false; if (is_null($objModel)) return false;
if ( if (

233
src/Maintenance.php Normal file
View File

@ -0,0 +1,233 @@
<?php
/*
Copyright (c) TPsoft.org 2000-2025
Author: Ing. Igor Mino <mino@tpsoft.org>
License: GNU GPL-3.0 or later
Description: Tool for automatic maintenance of database in your project.
Version: 1.0.0
Milestones:
2025-05-29 07:59 Igor - Forked from Beteha to standalone public library
*/
namespace TPsoft\DBmodel;
class Maintenance
{
public const ABNORMAL = 0;
public const OK = 1;
public const FIXED = 2;
public const FIX_FAILED = 3;
public const UPDATED = 4;
public const UPDATE_FAIL = 5;
public string $lastMessage = '';
protected DBmodel $dbh;
public function __construct(?DBmodel $dbh)
{
if (is_null($dbh)) {
if (!is_null(DBmodel::$instance)) {
$this->dbh = DBmodel::$instance;
} else {
throw new \Exception('DB handler is null');
}
} else {
$this->dbh = $dbh;
}
}
public function testDB($test, $test_out)
{
if (is_array($test_out)) {
$test_out = array_change_key_case($test_out, CASE_LOWER);
$row = $this->dbh->getRow($test);
if (!is_array($row)) return false;
$row = array_change_key_case($row, CASE_LOWER);
foreach ($test_out as $col => $val) {
if (strlen($row[$col]) != strlen($val)) {
$max_len = min(strlen($row[$col]), strlen($val));
if (substr(strtolower($row[$col]), 0, $max_len) != substr(strtolower($val), 0, $max_len)) {
return false;
}
} else {
if (strtolower($row[$col]) != strtolower($val)) {
return false;
}
}
}
return true;
} else {
return $this->dbh->getOne($test) == $test_out;
}
}
public function checkDB($test, $test_out, $fix)
{
if ($this->testDB($test, $test_out)) {
return Maintenance::OK;
} else {
$this->dbh->query($fix);
if ($this->testDB($test, $test_out)) {
return Maintenance::FIXED;
} else {
return Maintenance::FIX_FAILED;
}
}
return 0;
}
public function existsTable($table_name)
{
return $this->testDB(
'SHOW TABLES LIKE "' . $table_name . '"',
$table_name
);
}
public function checkDBTable($table_name, $definition, $after_definition = '')
{
if (strlen($table_name) <= 0) {
$this->lastMessage = 'Table ' . strtoupper($table_name) . ' - invalid index';
return Maintenance::ABNORMAL;
}
$status = $this->checkDB(
// TEST query
'SHOW TABLES LIKE "' . $table_name . '"',
// TEST output
$table_name,
// FIX query
'CREATE TABLE `' . $table_name . '` (' . $definition . ') ' . $after_definition
);
$this->lastMessage = 'Table ' . strtoupper($table_name) . ' (' . $table_name . ')';
return $status;
}
public function existsColumn($table_name, $column)
{
return $this->testDB(
'DESCRIBE `' . $table_name . '` `' . $column . '`',
$column
);
}
public function checkDBAdd($table_name, $column, $definition)
{
$status = $this->checkDB(
// TEST query
'DESCRIBE `' . $table_name . '` `' . $column . '`',
// TEST output
$column,
// FIX query
'ALTER TABLE `' . $table_name . '` ADD `' . $column . '` ' . $definition
);
$this->lastMessage = "Added column $column to table $table_name";
return $status;
}
public function checkDBRename($table_name, $column_old, $column_new, $definition, $test = null)
{
if (!$this->existsColumn($table_name, $column_old)) {
return Maintenance::ABNORMAL;
}
$status = $this->checkDB(
// TEST query
'DESCRIBE `' . $table_name . '` `' . $column_old . '`',
// TEST output
$test,
// FIX query
'ALTER TABLE `' . $table_name . '` CHANGE `' . $column_old . '` `' . $column_new . '` ' . $definition
);
$this->lastMessage = "Renamed column $column_old to $column_new for $table_name";
return $status;
}
public function checkDBRetype($table_name, $column, $new_type, $definition)
{
if (!$this->existsColumn($table_name, $column)) {
return Maintenance::ABNORMAL;
}
$status = $this->checkDB(
// TEST query
'DESCRIBE `' . $table_name . '` `' . $column . '`',
// TEST output
array('type' => strtolower($new_type)),
// FIX query
'ALTER TABLE `' . $table_name . '` CHANGE `' . $column . '` `' . $column . '` ' . $definition
);
$this->lastMessage = "Changed type of column $column for table " . strtoupper($table_name) . " to tyoe " . strtoupper($new_type);
return $status;
}
public function checkDBRemove($table_name, $column)
{
$status = $this->checkDB(
// TEST query
'DESCRIBE `' . $table_name . '` `' . $column . '`',
// TEST output
NULL,
// FIX query
'ALTER TABLE `' . $table_name . '` DROP COLUMN `' . $column . '`'
);
$this->lastMessage = "Removed column $column for " . strtoupper($table_name);
return $status;
}
public function addKey($table_name, $key)
{
$test = 'SHOW INDEX FROM `' . $table_name . '` WHERE column_name = "' . $key . '"';
$test_out = array('table' => $table_name);
if ($this->testDB($test, $test_out)) {
$status = Maintenance::OK;
} else {
$fix = 'CREATE INDEX `' . $key . '` ON `' . $table_name . '` (`' . $key . '`)';
$this->dbh->query($fix);
if ($this->testDB($test, $test_out)) {
$status = Maintenance::FIXED;
} else {
$status = Maintenance::FIX_FAILED;
}
}
$this->lastMessage = 'Added KEY ' . $key . ' for ' . strtoupper($table_name);
return $status;
}
public function dropKey($table_name, $key)
{
$test = 'SHOW INDEX FROM `' . $table_name . '` WHERE column_name = "' . $key . '"';
$test_out = null;
if ($this->testDB($test, $test_out)) {
$status = Maintenance::OK;
} else {
$index_info = $this->dbh->getRow('SHOW INDEX FROM `' . $table_name . '` WHERE column_name = "' . $key . '"');
$index_info = array_change_key_case($index_info, CASE_LOWER);
$index_name = $index_info['key_name'];
$fix = 'ALTER TABLE `' . $table_name . '` DROP INDEX `' . $index_name . '`';
$this->dbh->query($fix);
if ($this->testDB($test, $test_out)) {
$status = Maintenance::FIXED;
} else {
$status = Maintenance::FIX_FAILED;
}
}
$this->lastMessage = 'Deleted KEY ' . $key . ' for ' . strtoupper($table_name);
return $status;
}
public function updateDB($test, $test_out, $update)
{
if (!$this->testDB($test, $test_out)) {
return Maintenance::OK;
} else {
$suc = $this->dbh->query($update);
if ($suc !== false) {
return Maintenance::UPDATED;
} else {
return Maintenance::UPDATE_FAIL;
}
}
return 0;
}
}

View File

@ -1,7 +1,7 @@
<?php <?php
require_once __DIR__.'/../src/DBmodel.php'; require_once __DIR__.'/../src/DBmodel.php';
require_once __DIR__.'/../src/creator.php'; require_once __DIR__.'/../src/Creator.php';
$db = new \TPsoft\DBmodel\DBmodel('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'test', 'test'); $db = new \TPsoft\DBmodel\DBmodel('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'test', 'test');

49
test/test4.php Normal file
View File

@ -0,0 +1,49 @@
<?php
require_once __DIR__.'/../src/DBmodel.php';
require_once __DIR__.'/../src/Maintenance.php';
$db = new \TPsoft\DBmodel\DBmodel('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'test', 'test');
$maintenance = new \TPsoft\DBmodel\Maintenance($db);
// Check if existing table, if not create it by definition
$suc = $maintenance->checkDBTable('users', '
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
');
echo "\nCheckDBTable: "; var_dump($suc);
// Check if existing column, if not add it by definition
$suc = $maintenance->checkDBAdd('users', 'password', 'varchar(255) DEFAULT NULL');
echo "\nCheckDBAdd: "; var_dump($suc);
// Check if existing column
$suc = $maintenance->existsColumn('users', 'email');
echo "\nExistsColumn: "; var_dump($suc);
// Check if existing column in new name, if not rename it
$suc = $maintenance->checkDBRename('users', 'email', 'email_new', 'varchar(255) DEFAULT NULL');
echo "\nCheckDBRename: "; var_dump($suc);
// Chceck type of column, if not matched change it
$suc = $maintenance->checkDBRetype('users', 'email_new', 'varchar(128)', 'varchar(128) DEFAULT NULL AFTER `password`');
echo "\nCheckDBRetype: "; var_dump($suc);
// Remove column from table
$suc = $maintenance->checkDBRemove('users', 'email_new');
echo "\nCheckDBRemove: "; var_dump($suc);
// Add index for column
$suc = $maintenance->addKey('users', 'name');
echo "\nAddKey: "; var_dump($suc);
// Drop index by column name
$suc = $maintenance->dropKey('users', 'name');
echo "\nDropKey: "; var_dump($suc);
echo "\nDone\n\n";
?>