Skip to content

Commit

Permalink
Merge pull request #4 from dealnews/next
Browse files Browse the repository at this point in the history
Add basic bin script for generating value objects and mappers from tables
  • Loading branch information
brianlmoon authored Oct 21, 2024
2 parents 1e6c520 + c1be261 commit 6ad1317
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 5 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# DealNews Datbase Library


## Factory

The factory creates PDO objects using \DealNews\GetConfigto
Expand Down
252 changes: 252 additions & 0 deletions bin/create_objects.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
#!/usr/bin/env php
<?php

if(file_exists(__DIR__ . '/../vendor/autoload.php')) {
require __DIR__ . '/../vendor/autoload.php';
} elseif (file_exists(__DIR__ . '/../../../autoload.php')) {
require __DIR__ . '/../../../autoload.php';
}

$opts = getopt('', [
'db:',
'schema:',
'table:',
'dir:',
'ini-file:',
'namespace:',
'base-class:',
]);

if(!empty($opts['ini-file'])) {
putenv('DN_INI_FILE=' . $opts['ini-file']);
}

$db = \DealNews\DB\CRUD::factory($opts['db']);

$driver = $db->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);

$sql = "select
table_catalog as table_catalog,
table_schema as table_schema,
table_name as table_name,
column_name as column_name,
ordinal_position as ordinal_position,
column_default as column_default,
is_nullable as is_nullable,
data_type as data_type,
character_maximum_length as character_maximum_length,
character_octet_length as character_octet_length,
numeric_precision as numeric_precision,
numeric_scale as numeric_scale,
datetime_precision as datetime_precision,
character_set_name as character_set_name,
collation_name as collation_name
from
information_schema.columns
where
table_schema='{$opts["schema"]}' and
table_name='{$opts["table"]}'";

$schema = $db->runFetch($sql);

$sql = "select
constraint_name as constraint_name,
column_name as column_name
from
information_schema.key_column_usage
where
table_schema='{$opts["schema"]}' and
table_name='{$opts["table"]}'
order by
constraint_name,
ordinal_position";

$keys = $db->runFetch($sql);

$primary_key = '';

foreach($keys as $key) {
if ($driver === 'mysql' && $key['constraint_name'] == 'PRIMARY') {
$primary_key = $key['column_name'];
break;
} elseif ($driver === 'pgsql' && preg_match('/_pkey$/', $key['constraint_name'])) {
$primary_key = $key['column_name'];
break;
}
}

$properties = [];

foreach($schema as $column) {

switch ($column['data_type']) {

case 'int':
case 'integer':
case 'smallint':
case 'bigint':
case 'tinyint':
case 'year':
$type = 'int';
$def_default = 0;
break;

case 'boolean':
$type = 'bool';
$def_default = true;
break;

case 'float':
case 'double precision':
case 'real':
case 'decimal':
case 'double':
$type = 'float';
$def_default = 0.00;
break;

default:
$type = 'string';
$def_default = '';
}

if (strtoupper($column['is_nullable']) === 'YES') {
$type = "?$type";
$default = null;
} else {
$default = $def_default;
}


if (!empty($column['column_default'])) {
switch ($driver) {
case 'pgsql':
if (preg_match('/^\'(.*?)\'::/', $column['column_default'], $match)) {
$default = $match[1];
}
break;
case 'mysql':
if (strpos($column['column_default'], 'CURRENT_TIMESTAMP') !== 0) {
$default = $column['column_default'];
}
}
}

$properties[$column['column_name']] = [
'type' => $type,
'default' => $default,
];
}

$object_name = rtrim(str_replace(' ', '', ucwords(str_replace('_', ' ', $opts['table']))), 's');



create_value_object($properties, $opts['namespace'], $object_name, $opts['base-class'], $opts['schema'], $opts['table'], $opts['dir']);
create_mapper($properties, $opts['namespace'], $object_name, $opts['base-class'], $opts['schema'], $opts['table'], $opts['dir'], $primary_key);

function create_value_object($properties, $namespace, $object_name, $base_class, $schema, $table, $dir) {

if (!empty($base_class)) {
$base_class = " extends $base_class";
}

$file = "<?php\n\n";

$file .= "namespace $namespace\\Data;\n\n";

$file .= "/**\n";
$file .= " * Value object for $schema.$table\n";
$file .= " *\n";
$file .= " * @package $namespace\n";
$file .= " */\n";

$file .= "class $object_name{$base_class} {\n\n";

$has_datetime = false;

foreach ($properties as $name => $settings) {

if ($settings['default'] === null) {
$default = 'null';
} elseif ($settings['type'] === 'string' || $settings['type'] === '?string') {
$default = "'{$settings["default"]}'";
} else {
$default = $settings["default"];
}

$file .= " /**\n";
$file .= " * @var {$settings['type']}\n";
$file .= " */\n";
if (strpos($settings['type'], 'DateTime') !== false) {
$has_datetime = true;
$file .= " public {$settings['type']} \$$name;\n\n";
} else {
$file .= " public {$settings['type']} \$$name = $default;\n\n";
}
}

if ($has_datetime) {

$file .= " /**\n";
$file .= " * Initialize properties that are objects\n";
$file .= " */\n";
$file .= " public function __construct() {\n";
foreach ($properties as $name => $settings) {
if (strpos($settings['type'], 'DateTime') !== false) {
$file .= " \$this->$name = new \\DateTime();\n";
}
}
$file .= " }\n";

}

$file .= "}\n";

if (!file_exists("$dir/Data")) {
mkdir("$dir/Data", recursive: true);
}

file_put_contents("$dir/Data/$object_name.php", $file);
}

function create_mapper($properties, $namespace, $object_name, $base_class, $schema, $table, $dir, $primary_key) {

$file = "<?php\n";
$file .= "\n";
$file .= "namespace $namespace\\Mapper;\n";
$file .= "\n";
$file .= "class $object_name extends \DealNews\DB\AbstractMapper {\n";
$file .= "\n";
$file .= " /**\n";
$file .= " * Table name\n";
$file .= " */\n";
$file .= " public const TABLE = '$table';\n";
$file .= "\n";
$file .= " /**\n";
$file .= " * Table primary key column name\n";
$file .= " */\n";
$file .= " public const PRIMARY_KEY = '$primary_key';\n";
$file .= "\n";
$file .= " /**\n";
$file .= " * Name of the class the mapper is mapping\n";
$file .= " */\n";
$file .= " public const MAPPED_CLASS = \\$namespace\\Data\\$object_name::class;\n";
$file .= "\n";
$file .= " /**\n";
$file .= " * Defines the properties that are mapped and any\n";
$file .= " * additional information needed to map them.\n";
$file .= " */\n";
$file .= " protected const MAPPING = [\n";
foreach (array_keys($properties) as $name) {
$file .= " '$name' => [],\n";
}
$file .= " ];\n";
$file .= "}\n";

if (!file_exists("$dir/Mapper")) {
mkdir("$dir/Mapper", recursive: true);
}

file_put_contents("$dir/Mapper/$object_name.php", $file);
}
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"discard-changes": true,
"sort-packages": true
},
"bin": [
"bin/create_objects.php"
],
"require": {
"php": "^8.0",
"dealnews/data-mapper": "^3.1.1",
Expand Down Expand Up @@ -43,4 +46,4 @@
"php-cs-fixer fix --config .php-cs-fixer.dist.php src tests"
]
}
}
}
2 changes: 1 addition & 1 deletion src/AbstractMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ abstract class AbstractMapper extends \DealNews\DataMapper\AbstractMapper {
*
* @param \DealNews\DB\CRUD|null $crud Optional CRUD object
*/
public function __construct(CRUD $crud = null) {
public function __construct(?CRUD $crud = null) {
if ($crud !== null) {
$this->crud = $crud;
} elseif (!empty($this::DATABASE_NAME)) {
Expand Down
6 changes: 3 additions & 3 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Factory {
* @throws \PDOException
* @throws \LogicException
*/
public static function init(string $db, array $options = null, string $type = null): PDO {
public static function init(string $db, ?array $options = null, ?string $type = null): PDO {
static $objs = [];

if (!empty($type)) {
Expand Down Expand Up @@ -91,7 +91,7 @@ public static function build(array $config): PDO {
* @throws \LogicException
* @throws \UnexpectedValueException
*/
public static function loadConfig(array $config, array $options = null, string $type = null): array {
public static function loadConfig(array $config, ?array $options = null, ?string $type = null): array {
if (empty($config['server']) && empty($config['dsn'])) {
throw new \LogicException('Either `server` or `dsn` is required', 3);
} elseif (!empty($config['server'])) {
Expand Down Expand Up @@ -157,7 +157,7 @@ public static function loadConfig(array $config, array $options = null, string $
* @return array
* @throws \LogicException
*/
public static function getConfig(string $db, GetConfig $cfg = null): array {
public static function getConfig(string $db, ?GetConfig $cfg = null): array {
if (empty($cfg)) {
$cfg = new GetConfig();
}
Expand Down

0 comments on commit 6ad1317

Please sign in to comment.