Add config for db-schema:1.0 + Updated src + Update notice
This commit is contained in:
parent
cca6d23c46
commit
61b74d5bcf
|
@ -35,7 +35,7 @@ The aim of this package is to make your life easier working with database creati
|
|||
Features:
|
||||
- Manage multiple databases (according to the database-driver)
|
||||
- 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
|
||||
----
|
||||
|
@ -66,25 +66,42 @@ use \dbschema\core\dBuilder;
|
|||
|
||||
```php
|
||||
// loads the default schema
|
||||
dBuilder::parse();
|
||||
$dbuilder = dBuilder::load();
|
||||
|
||||
// loads the custom schema
|
||||
dBuilder::parse('custom');
|
||||
$dbuilder = dBuilder::load('custom');
|
||||
```
|
||||
|
||||
> ### 2) Push the loaded schema to the database
|
||||
|
||||
```php
|
||||
// push to the default database
|
||||
dBuilder::push();
|
||||
$dbuilder->push();
|
||||
|
||||
// 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.
|
||||
|
||||
|
||||
> ### 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
|
||||
----
|
||||
|
||||
|
@ -136,31 +153,4 @@ Note: In the first line, it will use the database-driver default configuration.
|
|||
|
||||
## (2) Field types
|
||||
|
||||
### Default 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`|
|
||||
You must use the default MySQL field types.
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -9,20 +9,33 @@
|
|||
|
||||
|
||||
// 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 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
|
||||
*
|
||||
* @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
|
||||
=========================================================*/
|
||||
|
@ -30,22 +43,28 @@
|
|||
|
||||
/* (1) Check if file exists */
|
||||
if( !file_exists($conf) )
|
||||
return false;
|
||||
return null;
|
||||
|
||||
/* (2) Try to read file */
|
||||
self::$raw = file_get_contents($conf);
|
||||
$RAW = file_get_contents($conf);
|
||||
|
||||
if( self::$raw === false )
|
||||
return false;
|
||||
if( $RAW === false )
|
||||
return null;
|
||||
|
||||
/* (3) Try to parse JSON */
|
||||
self::$raw = json_decode(self::$raw, true);
|
||||
$RAW = json_decode($RAW, true);
|
||||
|
||||
if( is_null(self::$raw) )
|
||||
return false;
|
||||
if( is_null($RAW) )
|
||||
return null;
|
||||
|
||||
/* (4) Add 'constrain' flag according to '#' before column name */
|
||||
foreach(self::$raw as &$table){
|
||||
/* (4) If label does not exist -> abort */
|
||||
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){
|
||||
if( $column_name[0] == '#' ){
|
||||
|
@ -60,7 +79,7 @@
|
|||
|
||||
|
||||
|
||||
return true;
|
||||
return new self($RAW[$label]);
|
||||
|
||||
}
|
||||
|
||||
|
@ -69,20 +88,22 @@
|
|||
|
||||
/* [2] Populates a database
|
||||
*
|
||||
* @label<String> DatabaseDriver label
|
||||
* @dblabel<String> DatabaseDriver label
|
||||
*
|
||||
* @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 = '';
|
||||
$foreign_keys = []; // global foreign keys
|
||||
|
||||
|
||||
/* [1] For each table create an entry
|
||||
=========================================================*/
|
||||
foreach(self::$raw as $table_name=>$table){
|
||||
foreach($this->raw as $table_name=>$table){
|
||||
|
||||
/* (1) Initialise variables
|
||||
---------------------------------------------------------*/
|
||||
|
@ -114,12 +135,8 @@
|
|||
if( !isset($column['type']) || !is_string($column['type']) )
|
||||
continue;
|
||||
|
||||
/* (2) If incorrect type, ignore column */
|
||||
if( ($SQLType = self::checkType($column['type'])) == false )
|
||||
continue;
|
||||
|
||||
/* (3) Add corresponding SQL type to the query */
|
||||
$QUERY .= "$SQLType";
|
||||
/* (2) Add corresponding SQL type to the query */
|
||||
$QUERY .= $column['type'];
|
||||
|
||||
|
||||
/* (2.2) Manage inline constraints (not null, auto_increment)
|
||||
|
@ -147,12 +164,12 @@
|
|||
$fk = $column['ref'];
|
||||
|
||||
// {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 //
|
||||
$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] ];
|
||||
|
||||
}
|
||||
|
@ -233,76 +250,118 @@
|
|||
|
||||
|
||||
|
||||
|
||||
/* [3] Checks if a given type is correct
|
||||
/* [3] Fills a config file from a database
|
||||
*
|
||||
* @type<Mixed> The human-readable type
|
||||
* @label<String> The database label
|
||||
* @dblabel<String> DatabaseDriver label
|
||||
*
|
||||
* @return SQLType<String> The corresponding SQL type
|
||||
* FALSE on error
|
||||
* @return instance<dBuilder> dBuilder instance -> NULL on error
|
||||
*
|
||||
=========================================================*/
|
||||
private static function checkType($type){
|
||||
/* [1] Check complex types
|
||||
public static function pull($label=null, $ddlabel=null){
|
||||
/* [1] Get basic information (tables)
|
||||
=========================================================*/
|
||||
/* (1) Varchar
|
||||
---------------------------------------------------------*/
|
||||
if( preg_match("@^varchar\((\d+)\)$@", $type, $match) )
|
||||
return "VARCHAR(${match[1]})";
|
||||
/* (1) Initialize raw data */
|
||||
$RAW = [];
|
||||
|
||||
/* (2) Double
|
||||
---------------------------------------------------------*/
|
||||
if( preg_match("@^(?:double|float|decimal|real)\((\d+), ?(\d+)\)$@", $type, $match) )
|
||||
return "DOUBLE(${match[1]},${match[2]})";
|
||||
/* (2) Get database name */
|
||||
$db_name = DatabaseDriver::get()->getConfig()['dbname'];
|
||||
|
||||
/* (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
|
||||
---------------------------------------------------------*/
|
||||
if( $type == 'bool' || $type == 'boolean' || $type == 'bit' )
|
||||
return 'BIT';
|
||||
foreach($RAW as $table_name=>&$table){
|
||||
|
||||
/* (2) Character
|
||||
---------------------------------------------------------*/
|
||||
if( $type == 'char' || $type == 'byte' )
|
||||
return 'CHAR';
|
||||
/* (1) Get column list */
|
||||
$colList = DatabaseDriver::getPDO($ddlabel)->query("SHOW COLUMNS FROM $table_name");
|
||||
$colList = $colList->fetchAll();
|
||||
|
||||
/* (3) Integer
|
||||
---------------------------------------------------------*/
|
||||
if( $type == 'int' || $type == 'integer' || $type == 'number' )
|
||||
return 'INT';
|
||||
/* (2) Add each column to $table array */
|
||||
foreach($colList as $column){
|
||||
|
||||
/* (4) Float
|
||||
---------------------------------------------------------*/
|
||||
if( $type == 'float' || $type == 'double' || $type == 'decimal' || $type == 'real' )
|
||||
return 'DOUBLE';
|
||||
/* (2.1) Foreign key management */
|
||||
$col_name = ($column['Key'] == 'MUL') ? '#'.$column['Field'] : $column['Field'];
|
||||
|
||||
/* (5) Text
|
||||
---------------------------------------------------------*/
|
||||
if( $type == 'text' )
|
||||
return 'TEXT';
|
||||
/* (2.2) Fill basic data*/
|
||||
$table[$col_name] = [
|
||||
'type' => $column['Type']
|
||||
];
|
||||
|
||||
/* (6) Date & time
|
||||
---------------------------------------------------------*/
|
||||
if( $type == 'date' )
|
||||
return 'DATE';
|
||||
// {2.2.1} PRIMARY KEY //
|
||||
if( $column['Key'] == 'PRI' )
|
||||
$table[$col_name]['primary'] = true;
|
||||
|
||||
if( $type == 'time' )
|
||||
return 'TIME';
|
||||
// {2.2.2} NOT NULL //
|
||||
if( $column['Null'] == 'NO' )
|
||||
$table[$col_name]['null'] = false;
|
||||
|
||||
if( $type == 'datetime' )
|
||||
return 'DATETIME';
|
||||
// {2.2.3} AUTO_INCREMENT //
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue