Add config for db-schema:1.0 + Updated src + Update notice

This commit is contained in:
xdrm-brackets 2017-09-09 15:55:32 +02:00
parent cca6d23c46
commit 61b74d5bcf
3 changed files with 175 additions and 108 deletions

View File

@ -35,7 +35,7 @@ The aim of this package is to make your life easier working with database creati
Features: Features:
- Manage multiple databases (according to the database-driver) - Manage multiple databases (according to the database-driver)
- Push a json configuration to a database (mysql) - Push a json configuration to a database (mysql)
- (todo) Pull a database schema to a json file - Pull a database schema to a json file
2 - Usage 2 - Usage
---- ----
@ -66,25 +66,42 @@ use \dbschema\core\dBuilder;
```php ```php
// loads the default schema // loads the default schema
dBuilder::parse(); $dbuilder = dBuilder::load();
// loads the custom schema // loads the custom schema
dBuilder::parse('custom'); $dbuilder = dBuilder::load('custom');
``` ```
> ### 2) Push the loaded schema to the database > ### 2) Push the loaded schema to the database
```php ```php
// push to the default database // push to the default database
dBuilder::push(); $dbuilder->push();
// push a custom database // push a custom database
dBuilder::push('custom-db'); $dbuilder->push('custom-db');
``` ```
Note: In the first line, it will use the database-driver default configuration. In the second line, it will use the database-driver `custom` configuration. Note: In the first line, it will use the database-driver default configuration. In the second line, it will use the database-driver `custom` configuration.
> ### 3) Pull a configuration from a database
```php
// pulls into the default schema (default database-driver config)
$dbuilder = dBuilder::pull();
// pulls into the custom schema
$dbuilder = dBuilder::pull('custom');
// pulls into the default schema ('custom-db' database-driver config)
$dbuilder = dBuilder::pull(null, 'custom-db');
// pulls into the custom schema ('custom-db' database-driver config)
$dbuilder = dBuilder::pull('custom', 'custom-db);
```
3 - configuration 3 - configuration
---- ----
@ -136,31 +153,4 @@ Note: In the first line, it will use the database-driver default configuration.
## (2) Field types ## (2) Field types
### Default types You must use the default MySQL field types.
|Type|Example|Description|
|---|---|---|
|`bool`|`true`|Boolean (true or false)|
|`boolean`|`true`|Boolean (true or false)|
|`bit`|`true`|Boolean (true or false)|
|`char`|`a`|Any character|
|`int`|`10`|Positive integer number between `0` and `2147483647`|
|`integer`|`10`|Positive integer number between `0` and `2147483647`|
|`number`|`10`|Positive integer number between `0` and `2147483647`|
|`float`|`-10.2`, `23.4`|Any real number|
|`double`|`-10.2`, `23.4`|Any real number|
|`decimal`|`-10.2`, `23.4`|Any real number|
|`real`|`-10.2`, `23.4`|Any real number|
|`time`|`12:23,32`|Time|
|`date`|`12-08-2017`|Date|
|`datetime`|`12-08-2017 12:23,32`|Datetime|
|`timestamp`|`1504954629`|Timestamp|
### Complex types
|Type|Description|
|---|---|
|`varchar(a)`|Text of a maximum length of `a`|
|`double(a,b)`|Decimal number with a size of `a` and a decimal size of `b`|
|`float(a,b)`|Decimal number with a size of `a` and a decimal size of `b`|
|`decimal(a,b)`|Decimal number with a size of `a` and a decimal size of `b`|
|`real(a,b)`|Decimal number with a size of `a` and a decimal size of `b`|

View File

@ -0,0 +1,18 @@
{
"default": {
"user": {
"id_user": { "type": "int", "primary": true, "auto_increment": true },
"username": { "type": "varchar(30)", "unique": true, "null": false },
"description": { "type": "text", "default": null }
},
"article": {
"id_article": { "type": "int", "primary": true, "auto_increment": true },
"#id_writer": { "type": "int", "ref": [ "user", "id_user" ] },
"title": { "type": "varchar(50)", "null": false },
"body": { "type": "text", "null": false }
}
}
}

View File

@ -9,20 +9,33 @@
// Constantes // Constantes
private static function config_path(){ return __ROOT__.'/config/dbschema.json'; } private static function config_path(){ return __ROOT__.'/config/db-schema.json'; }
// Private attributes // Private attributes
private static $raw = null; private $raw = null;
/* [0] Constructor
*
* @raw<array> Creates an instance from a json
*
=========================================================*/
private function __construct($raw=[]){
$this->raw = $raw;
}
/* [1] Builds a local schema from the json file /* [1] Builds a local schema from the json file
* *
* @return status False on error * @label<String> The database label
*
* @return instance<dBuilder> dBuilder instance -> NULL on error
*
=========================================================*/ =========================================================*/
public static function parse(){ public static function load($label=null){
/* [1] Extract config file /* [1] Extract config file
=========================================================*/ =========================================================*/
@ -30,22 +43,28 @@
/* (1) Check if file exists */ /* (1) Check if file exists */
if( !file_exists($conf) ) if( !file_exists($conf) )
return false; return null;
/* (2) Try to read file */ /* (2) Try to read file */
self::$raw = file_get_contents($conf); $RAW = file_get_contents($conf);
if( self::$raw === false ) if( $RAW === false )
return false; return null;
/* (3) Try to parse JSON */ /* (3) Try to parse JSON */
self::$raw = json_decode(self::$raw, true); $RAW = json_decode($RAW, true);
if( is_null(self::$raw) ) if( is_null($RAW) )
return false; return null;
/* (4) Add 'constrain' flag according to '#' before column name */ /* (4) If label does not exist -> abort */
foreach(self::$raw as &$table){ is_null($label) && ($label = 'default');
if( !isset($RAW[$label]) )
return null;
/* (5) Add 'constraint' flag according to '#' before column name */
foreach($RAW[$label] as &$table){
foreach($table as $column_name=>$column){ foreach($table as $column_name=>$column){
if( $column_name[0] == '#' ){ if( $column_name[0] == '#' ){
@ -60,7 +79,7 @@
return true; return new self($RAW[$label]);
} }
@ -69,20 +88,22 @@
/* [2] Populates a database /* [2] Populates a database
* *
* @label<String> DatabaseDriver label * @dblabel<String> DatabaseDriver label
* *
* @return status False on error * @return status False on error
=========================================================*/ =========================================================*/
public static function push($label=null){ public function push($ddlabel=null){
$PDO = DatabaseDriver::getPDO($label); /* [0] Initialize useful variables
=========================================================*/
$PDO = DatabaseDriver::getPDO($ddlabel);
$QUERY = ''; $QUERY = '';
$foreign_keys = []; // global foreign keys $foreign_keys = []; // global foreign keys
/* [1] For each table create an entry /* [1] For each table create an entry
=========================================================*/ =========================================================*/
foreach(self::$raw as $table_name=>$table){ foreach($this->raw as $table_name=>$table){
/* (1) Initialise variables /* (1) Initialise variables
---------------------------------------------------------*/ ---------------------------------------------------------*/
@ -114,12 +135,8 @@
if( !isset($column['type']) || !is_string($column['type']) ) if( !isset($column['type']) || !is_string($column['type']) )
continue; continue;
/* (2) If incorrect type, ignore column */ /* (2) Add corresponding SQL type to the query */
if( ($SQLType = self::checkType($column['type'])) == false ) $QUERY .= $column['type'];
continue;
/* (3) Add corresponding SQL type to the query */
$QUERY .= "$SQLType";
/* (2.2) Manage inline constraints (not null, auto_increment) /* (2.2) Manage inline constraints (not null, auto_increment)
@ -147,12 +164,12 @@
$fk = $column['ref']; $fk = $column['ref'];
// {1} Check if the 'ref' field exists in JSON structure // // {1} Check if the 'ref' field exists in JSON structure //
if( isset(self::$raw[$fk[0]]) && isset(self::$raw[$fk[0]][$fk[1]]) ){ if( isset($this->raw[$fk[0]]) && isset($this->raw[$fk[0]][$fk[1]]) ){
// {2} Check if the type matches // // {2} Check if the type matches //
$refSQLType = self::checkType(self::$raw[$fk[0]][$fk[1]]['type']); $refType = $this->raw[$fk[0]][$fk[1]]['type'];
if( $refSQLType == $SQLType ) if( $refType == $column['type'] )
$foreign_keys[] = [ $table_name, $column_name, $fk[0], $fk[1] ]; $foreign_keys[] = [ $table_name, $column_name, $fk[0], $fk[1] ];
} }
@ -233,76 +250,118 @@
/* [3] Fills a config file from a database
/* [3] Checks if a given type is correct
* *
* @type<Mixed> The human-readable type * @label<String> The database label
* @dblabel<String> DatabaseDriver label
* *
* @return SQLType<String> The corresponding SQL type * @return instance<dBuilder> dBuilder instance -> NULL on error
* FALSE on error
* *
=========================================================*/ =========================================================*/
private static function checkType($type){ public static function pull($label=null, $ddlabel=null){
/* [1] Check complex types /* [1] Get basic information (tables)
=========================================================*/ =========================================================*/
/* (1) Varchar /* (1) Initialize raw data */
---------------------------------------------------------*/ $RAW = [];
if( preg_match("@^varchar\((\d+)\)$@", $type, $match) )
return "VARCHAR(${match[1]})";
/* (2) Double /* (2) Get database name */
---------------------------------------------------------*/ $db_name = DatabaseDriver::get()->getConfig()['dbname'];
if( preg_match("@^(?:double|float|decimal|real)\((\d+), ?(\d+)\)$@", $type, $match) )
return "DOUBLE(${match[1]},${match[2]})"; /* (3) Get the table list */
$tabList = DatabaseDriver::getPDO($ddlabel)->query("SHOW TABLES");
$tabList = $tabList->fetchAll();
/* (4) Populate raw data */
foreach($tabList as $field){
$table_name = $field['Tables_in_'.$db_name];
$RAW[$table_name] = [];
}
/* [2] Check simple types /* [2] Get each tables' columns
=========================================================*/ =========================================================*/
/* (1) Boolean foreach($RAW as $table_name=>&$table){
---------------------------------------------------------*/
if( $type == 'bool' || $type == 'boolean' || $type == 'bit' )
return 'BIT';
/* (2) Character /* (1) Get column list */
---------------------------------------------------------*/ $colList = DatabaseDriver::getPDO($ddlabel)->query("SHOW COLUMNS FROM $table_name");
if( $type == 'char' || $type == 'byte' ) $colList = $colList->fetchAll();
return 'CHAR';
/* (3) Integer /* (2) Add each column to $table array */
---------------------------------------------------------*/ foreach($colList as $column){
if( $type == 'int' || $type == 'integer' || $type == 'number' )
return 'INT';
/* (4) Float /* (2.1) Foreign key management */
---------------------------------------------------------*/ $col_name = ($column['Key'] == 'MUL') ? '#'.$column['Field'] : $column['Field'];
if( $type == 'float' || $type == 'double' || $type == 'decimal' || $type == 'real' )
return 'DOUBLE';
/* (5) Text /* (2.2) Fill basic data*/
---------------------------------------------------------*/ $table[$col_name] = [
if( $type == 'text' ) 'type' => $column['Type']
return 'TEXT'; ];
/* (6) Date & time // {2.2.1} PRIMARY KEY //
---------------------------------------------------------*/ if( $column['Key'] == 'PRI' )
if( $type == 'date' ) $table[$col_name]['primary'] = true;
return 'DATE';
if( $type == 'time' ) // {2.2.2} NOT NULL //
return 'TIME'; if( $column['Null'] == 'NO' )
$table[$col_name]['null'] = false;
if( $type == 'datetime' ) // {2.2.3} AUTO_INCREMENT //
return 'DATETIME'; if( $column['Extra'] == 'auto_increment' )
$table[$col_name]['auto_increment'] = true;
if( $type == 'timestamp' ) }
return 'TIMESTAMP';
/* (3) Get each tables' constraints */
// {3.1} Get table constraints //
$conList = DatabaseDriver::getPDO($ddlabel)->query("SHOW CREATE TABLE $table_name");
$conList = $conList->fetch()['Create Table'];
$conLines = explode("\n", $conList);
return false; // {3.2} For each column -> get constraints //
foreach($conLines as $i=>$line)
if( preg_match('/CONSTRAINT `.+` FOREIGN KEY \(`(.+)`\) REFERENCES `(.+)` \(`(.+)`\)+/i', $line, $m) )
if( isset($table['#'.$m[1]]) )
$table['#'.$m[1]]['ref'] = [ $m[2], $m[3] ];
} }
/* [3] Extract config file
=========================================================*/
$conf = self::config_path();
/* (1) Check if file exists */
if( !file_exists($conf) )
return 1;
/* (2) Try to read file */
$OUTDATED = file_get_contents($conf);
if( $OUTDATED === false )
return 2;
/* (3) Try to parse JSON */
$OUTDATED = json_decode($OUTDATED, true);
if( is_null($OUTDATED) )
return 3;
/* [4] Adds pulled schema + writes
=========================================================*/
/* (1) Set $label to default if missing */
is_null($label) && ($label = 'default');
/* (2) Adds pulled conf to conf */
var_dump($RAW);
$OUTDATED[$label] = $RAW;
/* (3) Writes down new config */
file_put_contents(self::config_path(), json_encode($OUTDATED, JSON_PRETTY_PRINT) );
return self::load($label);
}
} }