Skip to content

Commit f43bd43

Browse files
committed
Merge pull request #8 from igormukhingmailcom/next-version
Next version
2 parents cad35ed + 0c3da8c commit f43bd43

File tree

10 files changed

+238
-123
lines changed

10 files changed

+238
-123
lines changed

README.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,26 @@ Load database schema from an XML file.
55

66
## Usage
77

8-
You can load schema to database by PDO url:
8+
You can load schema to database by:
99

10-
```bash
11-
./bin/dbtk-schema-loader schema:load example/schema.xml mysql://username:password@localhost/database_name
10+
### Database url
11+
12+
A full URL containing username, password, hostname and dbname:
13+
14+
```
15+
./bin/dbtk-schema-loader schema:load example/schema.xml mysql://username:password@localhost/dbname
1216
```
1317

14-
or by database name:
18+
### Just a dbname
19+
20+
In this case [linkorb/database-manager](https://github.com/linkorb/database-manager) is used for loading database connection details (server, username, password, etc) from .conf files (read project readme for more details).
21+
22+
In a nutshell - you must have a `dbname.conf` file at `/share/config/database/` as described at [database-manager's documentation](https://github.com/linkorb/database-manager#database-configuration-files).
1523

1624
```bash
17-
./bin/dbtk-schema-loader schema:load example/schema.xml database_name
25+
./bin/dbtk-schema-loader schema:load example/schema.xml dbname
1826
```
1927

20-
In this case you must have a `database_name.conf` file at `/share/config/database/` as described at [database-manager's documentation](https://github.com/linkorb/database-manager#database-configuration-files).
21-
2228
## License
2329
Please refer to the included LICENSE file
2430

TODO.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- Validate values:
2+
- boolean can be only true and false
3+
-

bin/dbtk-schema-loader

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ $loader = require $filename;
1818

1919
use Symfony\Component\Console\Application;
2020

21-
$application = new Application('DBTK Schema Loader', '1.1.1');
21+
$application = new Application('DBTK Schema Loader', '2.0.0');
2222
$application->setCatchExceptions(true);
2323
$application->add(new \DbTk\SchemaLoader\Command\SchemaLoadCommand());
2424
$application->run();

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
"bin": ["bin/dbtk-schema-loader"],
99
"minimum-stability": "stable",
1010
"require": {
11+
"symfony/console": "~2.4",
1112
"doctrine/dbal": "~2.5",
12-
"symfony/console": "~2.7"
13+
"linkorb/database-manager": "~2.0"
1314
},
1415
"require-dev": {
1516
"phpunit/phpunit": "~4.6"

example/schema.xml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<?xml version="1.0"?>
22
<schema>
3-
<table name="user" primaryKey="id">
4-
<column name="id" type="INT" autoincrement="true"/>
5-
<column name="name" type="VARCHAR(32)"/>
6-
<column name="display_name" type="VARCHAR(64)"/>
7-
<column name="about" type="LONGTEXT"/>
8-
<column name="created_at" type="INT"/>
9-
<column name="deleted_at" type="INT"/>
3+
<table name="user">
4+
<column name="id" type="integer" unsigned="true" autoincrement="true" />
5+
<column name="username" type="string" length="32" />
6+
<column name="firstname" type="string" length="32" />
7+
<column name="lastname" type="string" length="32" />
8+
<column name="password" type="string" length="32" />
9+
<column name="email" type="string" length="32" />
10+
11+
<index name="primary" primary="true" columns="id" />
1012
</table>
1113
</schema>

src/Command/SchemaLoadCommand.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
namespace DbTk\SchemaLoader\Command;
44

5+
use DbTk\SchemaLoader\Loader\LoaderFactory;
6+
use LinkORB\Component\DatabaseManager\DatabaseManager;
7+
58
use Symfony\Component\Console\Command\Command;
69
use Symfony\Component\Console\Input\InputArgument;
710
use Symfony\Component\Console\Input\InputOption;
811
use Symfony\Component\Console\Input\InputInterface;
912
use Symfony\Component\Console\Output\OutputInterface;
1013

11-
use DbTk\SchemaLoader\Loader\LoaderFactory;
12-
1314
use Doctrine\DBAL\Configuration;
1415
use Doctrine\DBAL\DriverManager;
1516
use Doctrine\DBAL\Schema\Schema;
@@ -37,7 +38,7 @@ protected function configure()
3738
->addArgument(
3839
'url',
3940
InputArgument::REQUIRED,
40-
'Database connection details'
41+
'Database connection details. You can use PDO url or just database name.'
4142
)
4243
->addOption(
4344
'apply',
@@ -61,8 +62,10 @@ public function execute(InputInterface $input, OutputInterface $output)
6162
$toSchema = $loader->loadSchema($filename);
6263

6364
$config = new Configuration();
65+
$dbmanager = new DatabaseManager();
66+
6467
$connectionParams = array(
65-
'url' => $url
68+
'url' => $dbmanager->getUrlByDatabaseName($url)
6669
);
6770
$connection = DriverManager::getConnection($connectionParams, $config);
6871

src/Loader/BaseLoader.php

Lines changed: 132 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,78 +7,163 @@
77

88
use Doctrine\DBAL\Types\Type;
99
use Doctrine\DBAL\Schema\Column;
10+
use Doctrine\DBAL\Schema\Index;
11+
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
1012

1113
/**
1214
* @author Igor Mukhin <igor.mukhin@gmail.com>
1315
*/
1416
abstract class BaseLoader
1517
{
16-
protected $typesMap = array(
17-
'bigint'=>'bigint',
18-
'tinyint'=>'boolean',
19-
'integer'=>'integer',
20-
'int'=>'integer',
21-
'longtext'=>'text',
22-
'text'=>'text',
23-
'varchar'=>'string',
24-
'char'=>'string',
25-
'decimal'=>'decimal',
26-
'float'=>'float',
27-
'datetime'=>'datetime',
28-
'date'=>'date'
18+
protected $extraOptions = array(
19+
'default'=>'typeaware',
20+
'notnull'=>'boolean',
21+
'length'=>'integer',
22+
'precision'=>'integer',
23+
'scale'=>'integer',
24+
'fixed'=>'boolean',
25+
'unsigned'=>'boolean',
26+
'autoincrement'=>'boolean',
27+
'comment'=>'string'
2928
);
3029

3130
/**
32-
* @param string $type
33-
* @return boolean
31+
* @param array $columns
32+
* @return Column[]
3433
*/
35-
public function isTypeSupported($type)
34+
public function loadColumns($columns)
3635
{
37-
return isset($this->typesMap[$type]);
36+
return array_map(function($columnNode){
37+
return $this->loadColumn($columnNode);
38+
}, $columns);
3839
}
3940

4041
/**
41-
* Apply extra options for special types.
42-
*
43-
* @param Column $column
44-
* @param string $extra
42+
* @param array $column
43+
* @return Column
4544
*/
46-
public function applyType(Column $column, $type, $extra)
45+
public function loadColumn($columnNode)
4746
{
48-
switch(strtolower($type)) {
49-
case "varchar":
50-
case "char":
51-
$column
52-
->setLength($extra)
53-
;
54-
break;
55-
56-
case "decimal":
57-
case "float":
58-
list($precision, $scale) = explode(',', $extra);
59-
$column
60-
->setPrecision($precision)
61-
->setScale($scale)
62-
;
63-
break;
47+
$type = trim($columnNode['type']);
48+
49+
if (!Type::hasType($type)) {
50+
throw new UnsupportedFieldTypeException($type);
6451
}
52+
53+
$options = $this->getColumnExtraOptions($type, $columnNode);
54+
return new Column((string)$columnNode['name'], Type::getType($type), $options);
6555
}
6656

6757
/**
6858
* @param string $type
69-
* @return Type
70-
*
71-
* @throws UnsupportedFieldTypeException
59+
* @param string $columnNode
60+
* @return array
7261
*/
73-
public function getType($type)
62+
public function getColumnExtraOptions($type, $columnNode)
7463
{
75-
$type = strtolower($type);
76-
if (!$this->isTypeSupported($type)) {
77-
throw new UnsupportedFieldTypeException($type);
64+
$options = array();
65+
foreach ($this->extraOptions as $optionName=>$optionType) {
66+
if (isset($columnNode[$optionName])) {
67+
$options[$optionName] = $this->convertColumnOptionValue((string)$columnNode[$optionName], $type, $optionType);
68+
}
69+
}
70+
return $options;
71+
}
72+
73+
/**
74+
* @param string $optionValue
75+
* @param string $type
76+
* @param string $optionType
77+
* @return mixed
78+
*/
79+
protected function convertColumnOptionValue($optionValue, $type, $optionType)
80+
{
81+
switch ($optionType) {
82+
case 'boolean':
83+
return $optionValue === 'true' ? true : false;
84+
85+
case 'integer':
86+
return (int)$optionValue;
87+
88+
case 'string':
89+
return (string)$optionValue;
90+
91+
case 'typeaware':
92+
if ('null' == $optionValue) {
93+
return NULL;
94+
}
95+
96+
switch ($type) {
97+
case 'tinyint':
98+
case 'integer':
99+
case 'bigint':
100+
case 'float':
101+
case 'decimal':
102+
return (int)$optionValue;
103+
104+
case 'boolean':
105+
return $optionValue === 'true' ? true : false;
106+
107+
default:
108+
return (string)$optionValue;
109+
}
110+
111+
default:
112+
throw new RuntimeException(sprintf("Uknown optionType '%s'", $optionType));
113+
}
114+
}
115+
116+
/**
117+
* @param array $indexes
118+
* @return Index[]
119+
*/
120+
public function loadIndexes($indexes)
121+
{
122+
return array_map(function($indexNode){
123+
return $this->loadIndex($indexNode);
124+
}, $indexes);
125+
}
126+
127+
/**
128+
* @param array $indexNode
129+
* @return Index
130+
*/
131+
public function loadIndex($indexNode)
132+
{
133+
$indexName = (string) $indexNode['name'];
134+
$columns = explode(',', (string) $indexNode['columns']);
135+
$isUnique = isset($indexNode['unique']) && 'true' === (string) $indexNode['unique'] ? true : false;
136+
$isPrimary = isset($indexNode['primary']) && 'true' === (string) $indexNode['primary'] ? true : false;
137+
138+
return new Index($indexName, $columns, $isUnique, $isPrimary);
139+
}
140+
141+
/**
142+
* @param array $constraints
143+
* @return ForeignKeyConstraint[]
144+
*/
145+
public function loadContstraints($constraints)
146+
{
147+
return array_map(function($constraintNode){
148+
return $this->loadContstraint($constraintNode);
149+
}, $constraints);
150+
}
151+
152+
/**
153+
* @param array $constraintNode
154+
* @return ForeignKeyConstraint
155+
*/
156+
public function loadContstraint($constraintNode)
157+
{
158+
$name = null;
159+
if (isset($constraintNode['name'])) {
160+
$name = (string) $constraintNode['name'];
78161
}
79162

80-
$actialType = $this->typesMap[$type];
163+
$columns = explode(',', (string) $constraintNode['columns']);
164+
$foreignTable = (string) $constraintNode['foreign-table'];
165+
$foreignColumns = explode(',', (string) $constraintNode['foreign-columns']);
81166

82-
return Type::getType($actialType);
167+
return new ForeignKeyConstraint($columns, $foreignTable, $foreignColumns, $name);
83168
}
84169
}

src/Loader/XmlLoader.php

Lines changed: 7 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Doctrine\DBAL\Schema\Schema;
88
use Doctrine\DBAL\Schema\Table;
99
use Doctrine\DBAL\Schema\Column;
10+
use Doctrine\DBAL\Schema\Index;
1011

1112
/**
1213
* @author Joost Faassen <j.faassen@linkorb.com>
@@ -46,51 +47,15 @@ public function loadSchema($filename)
4647
*/
4748
public function loadTables($tables)
4849
{
49-
return array_map(function ($tableNode) {
50-
$columns = $this->loadColumns($tableNode->xpath('./column'));
51-
$table = new Table((string)$tableNode['name'], $columns);
50+
return array_map(function($tableNode){
51+
$columns = $this->loadColumns($tableNode->xpath('.//column'));
52+
$indexes = $this->loadIndexes($tableNode->xpath('.//index'));
53+
$constraints = $this->loadContstraints($tableNode->xpath('.//constraint'));
54+
55+
$table = new Table((string)$tableNode['name'], $columns, $indexes, $constraints);
5256

53-
if ((string) $tableNode['primaryKey']) {
54-
$table->setPrimaryKey(
55-
[(string) $tableNode['primaryKey']]
56-
);
57-
}
5857
return $table;
5958
}, $tables);
6059
}
6160

62-
/**
63-
* @param array $columns
64-
* @return Column[]
65-
*/
66-
public function loadColumns($columns)
67-
{
68-
return array_map(function ($columnNode) {
69-
// var_dump($columnNode);
70-
return $this->loadColumn($columnNode);
71-
}, $columns);
72-
}
73-
74-
/**
75-
* @param array $column
76-
* @return Column
77-
*/
78-
public function loadColumn($columnNode)
79-
{
80-
// @todo Refactor that line - its ugly little bit
81-
$extra = '';
82-
$type = trim($columnNode['type'], ' )');
83-
if (false !== strpos($type, '(')) {
84-
list($type, $extra) = explode('(', $type);
85-
}
86-
87-
$column = new Column($columnNode['name'], $this->getType($type));
88-
$this->applyType($column, $type, $extra);
89-
90-
if (strtolower($columnNode['autoincrement']) == 'true') {
91-
$column->setAutoincrement(true);
92-
}
93-
94-
return $column;
95-
}
9661
}

0 commit comments

Comments
 (0)