Skip to content

Commit 6ad1317

Browse files
authored
Merge pull request #4 from dealnews/next
Add basic bin script for generating value objects and mappers from tables
2 parents 1e6c520 + c1be261 commit 6ad1317

5 files changed

Lines changed: 261 additions & 5 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# DealNews Datbase Library
22

3+
34
## Factory
45

56
The factory creates PDO objects using \DealNews\GetConfigto

bin/create_objects.php

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
if(file_exists(__DIR__ . '/../vendor/autoload.php')) {
5+
require __DIR__ . '/../vendor/autoload.php';
6+
} elseif (file_exists(__DIR__ . '/../../../autoload.php')) {
7+
require __DIR__ . '/../../../autoload.php';
8+
}
9+
10+
$opts = getopt('', [
11+
'db:',
12+
'schema:',
13+
'table:',
14+
'dir:',
15+
'ini-file:',
16+
'namespace:',
17+
'base-class:',
18+
]);
19+
20+
if(!empty($opts['ini-file'])) {
21+
putenv('DN_INI_FILE=' . $opts['ini-file']);
22+
}
23+
24+
$db = \DealNews\DB\CRUD::factory($opts['db']);
25+
26+
$driver = $db->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
27+
28+
$sql = "select
29+
table_catalog as table_catalog,
30+
table_schema as table_schema,
31+
table_name as table_name,
32+
column_name as column_name,
33+
ordinal_position as ordinal_position,
34+
column_default as column_default,
35+
is_nullable as is_nullable,
36+
data_type as data_type,
37+
character_maximum_length as character_maximum_length,
38+
character_octet_length as character_octet_length,
39+
numeric_precision as numeric_precision,
40+
numeric_scale as numeric_scale,
41+
datetime_precision as datetime_precision,
42+
character_set_name as character_set_name,
43+
collation_name as collation_name
44+
from
45+
information_schema.columns
46+
where
47+
table_schema='{$opts["schema"]}' and
48+
table_name='{$opts["table"]}'";
49+
50+
$schema = $db->runFetch($sql);
51+
52+
$sql = "select
53+
constraint_name as constraint_name,
54+
column_name as column_name
55+
from
56+
information_schema.key_column_usage
57+
where
58+
table_schema='{$opts["schema"]}' and
59+
table_name='{$opts["table"]}'
60+
order by
61+
constraint_name,
62+
ordinal_position";
63+
64+
$keys = $db->runFetch($sql);
65+
66+
$primary_key = '';
67+
68+
foreach($keys as $key) {
69+
if ($driver === 'mysql' && $key['constraint_name'] == 'PRIMARY') {
70+
$primary_key = $key['column_name'];
71+
break;
72+
} elseif ($driver === 'pgsql' && preg_match('/_pkey$/', $key['constraint_name'])) {
73+
$primary_key = $key['column_name'];
74+
break;
75+
}
76+
}
77+
78+
$properties = [];
79+
80+
foreach($schema as $column) {
81+
82+
switch ($column['data_type']) {
83+
84+
case 'int':
85+
case 'integer':
86+
case 'smallint':
87+
case 'bigint':
88+
case 'tinyint':
89+
case 'year':
90+
$type = 'int';
91+
$def_default = 0;
92+
break;
93+
94+
case 'boolean':
95+
$type = 'bool';
96+
$def_default = true;
97+
break;
98+
99+
case 'float':
100+
case 'double precision':
101+
case 'real':
102+
case 'decimal':
103+
case 'double':
104+
$type = 'float';
105+
$def_default = 0.00;
106+
break;
107+
108+
default:
109+
$type = 'string';
110+
$def_default = '';
111+
}
112+
113+
if (strtoupper($column['is_nullable']) === 'YES') {
114+
$type = "?$type";
115+
$default = null;
116+
} else {
117+
$default = $def_default;
118+
}
119+
120+
121+
if (!empty($column['column_default'])) {
122+
switch ($driver) {
123+
case 'pgsql':
124+
if (preg_match('/^\'(.*?)\'::/', $column['column_default'], $match)) {
125+
$default = $match[1];
126+
}
127+
break;
128+
case 'mysql':
129+
if (strpos($column['column_default'], 'CURRENT_TIMESTAMP') !== 0) {
130+
$default = $column['column_default'];
131+
}
132+
}
133+
}
134+
135+
$properties[$column['column_name']] = [
136+
'type' => $type,
137+
'default' => $default,
138+
];
139+
}
140+
141+
$object_name = rtrim(str_replace(' ', '', ucwords(str_replace('_', ' ', $opts['table']))), 's');
142+
143+
144+
145+
create_value_object($properties, $opts['namespace'], $object_name, $opts['base-class'], $opts['schema'], $opts['table'], $opts['dir']);
146+
create_mapper($properties, $opts['namespace'], $object_name, $opts['base-class'], $opts['schema'], $opts['table'], $opts['dir'], $primary_key);
147+
148+
function create_value_object($properties, $namespace, $object_name, $base_class, $schema, $table, $dir) {
149+
150+
if (!empty($base_class)) {
151+
$base_class = " extends $base_class";
152+
}
153+
154+
$file = "<?php\n\n";
155+
156+
$file .= "namespace $namespace\\Data;\n\n";
157+
158+
$file .= "/**\n";
159+
$file .= " * Value object for $schema.$table\n";
160+
$file .= " *\n";
161+
$file .= " * @package $namespace\n";
162+
$file .= " */\n";
163+
164+
$file .= "class $object_name{$base_class} {\n\n";
165+
166+
$has_datetime = false;
167+
168+
foreach ($properties as $name => $settings) {
169+
170+
if ($settings['default'] === null) {
171+
$default = 'null';
172+
} elseif ($settings['type'] === 'string' || $settings['type'] === '?string') {
173+
$default = "'{$settings["default"]}'";
174+
} else {
175+
$default = $settings["default"];
176+
}
177+
178+
$file .= " /**\n";
179+
$file .= " * @var {$settings['type']}\n";
180+
$file .= " */\n";
181+
if (strpos($settings['type'], 'DateTime') !== false) {
182+
$has_datetime = true;
183+
$file .= " public {$settings['type']} \$$name;\n\n";
184+
} else {
185+
$file .= " public {$settings['type']} \$$name = $default;\n\n";
186+
}
187+
}
188+
189+
if ($has_datetime) {
190+
191+
$file .= " /**\n";
192+
$file .= " * Initialize properties that are objects\n";
193+
$file .= " */\n";
194+
$file .= " public function __construct() {\n";
195+
foreach ($properties as $name => $settings) {
196+
if (strpos($settings['type'], 'DateTime') !== false) {
197+
$file .= " \$this->$name = new \\DateTime();\n";
198+
}
199+
}
200+
$file .= " }\n";
201+
202+
}
203+
204+
$file .= "}\n";
205+
206+
if (!file_exists("$dir/Data")) {
207+
mkdir("$dir/Data", recursive: true);
208+
}
209+
210+
file_put_contents("$dir/Data/$object_name.php", $file);
211+
}
212+
213+
function create_mapper($properties, $namespace, $object_name, $base_class, $schema, $table, $dir, $primary_key) {
214+
215+
$file = "<?php\n";
216+
$file .= "\n";
217+
$file .= "namespace $namespace\\Mapper;\n";
218+
$file .= "\n";
219+
$file .= "class $object_name extends \DealNews\DB\AbstractMapper {\n";
220+
$file .= "\n";
221+
$file .= " /**\n";
222+
$file .= " * Table name\n";
223+
$file .= " */\n";
224+
$file .= " public const TABLE = '$table';\n";
225+
$file .= "\n";
226+
$file .= " /**\n";
227+
$file .= " * Table primary key column name\n";
228+
$file .= " */\n";
229+
$file .= " public const PRIMARY_KEY = '$primary_key';\n";
230+
$file .= "\n";
231+
$file .= " /**\n";
232+
$file .= " * Name of the class the mapper is mapping\n";
233+
$file .= " */\n";
234+
$file .= " public const MAPPED_CLASS = \\$namespace\\Data\\$object_name::class;\n";
235+
$file .= "\n";
236+
$file .= " /**\n";
237+
$file .= " * Defines the properties that are mapped and any\n";
238+
$file .= " * additional information needed to map them.\n";
239+
$file .= " */\n";
240+
$file .= " protected const MAPPING = [\n";
241+
foreach (array_keys($properties) as $name) {
242+
$file .= " '$name' => [],\n";
243+
}
244+
$file .= " ];\n";
245+
$file .= "}\n";
246+
247+
if (!file_exists("$dir/Mapper")) {
248+
mkdir("$dir/Mapper", recursive: true);
249+
}
250+
251+
file_put_contents("$dir/Mapper/$object_name.php", $file);
252+
}

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
"discard-changes": true,
99
"sort-packages": true
1010
},
11+
"bin": [
12+
"bin/create_objects.php"
13+
],
1114
"require": {
1215
"php": "^8.0",
1316
"dealnews/data-mapper": "^3.1.1",
@@ -43,4 +46,4 @@
4346
"php-cs-fixer fix --config .php-cs-fixer.dist.php src tests"
4447
]
4548
}
46-
}
49+
}

src/AbstractMapper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ abstract class AbstractMapper extends \DealNews\DataMapper\AbstractMapper {
7474
*
7575
* @param \DealNews\DB\CRUD|null $crud Optional CRUD object
7676
*/
77-
public function __construct(CRUD $crud = null) {
77+
public function __construct(?CRUD $crud = null) {
7878
if ($crud !== null) {
7979
$this->crud = $crud;
8080
} elseif (!empty($this::DATABASE_NAME)) {

src/Factory.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class Factory {
3636
* @throws \PDOException
3737
* @throws \LogicException
3838
*/
39-
public static function init(string $db, array $options = null, string $type = null): PDO {
39+
public static function init(string $db, ?array $options = null, ?string $type = null): PDO {
4040
static $objs = [];
4141

4242
if (!empty($type)) {
@@ -91,7 +91,7 @@ public static function build(array $config): PDO {
9191
* @throws \LogicException
9292
* @throws \UnexpectedValueException
9393
*/
94-
public static function loadConfig(array $config, array $options = null, string $type = null): array {
94+
public static function loadConfig(array $config, ?array $options = null, ?string $type = null): array {
9595
if (empty($config['server']) && empty($config['dsn'])) {
9696
throw new \LogicException('Either `server` or `dsn` is required', 3);
9797
} elseif (!empty($config['server'])) {
@@ -157,7 +157,7 @@ public static function loadConfig(array $config, array $options = null, string $
157157
* @return array
158158
* @throws \LogicException
159159
*/
160-
public static function getConfig(string $db, GetConfig $cfg = null): array {
160+
public static function getConfig(string $db, ?GetConfig $cfg = null): array {
161161
if (empty($cfg)) {
162162
$cfg = new GetConfig();
163163
}

0 commit comments

Comments
 (0)