rosavox/lib/alveolata/storage/implementation-sqltable/functions.php
2025-05-23 07:33:29 +00:00

418 lines
7.9 KiB
PHP

<?php
namespace alveolata\storage;
// require_once(DIR_ALVEOLATA . '/definitions.php');
require_once(DIR_ALVEOLATA . '/string/functions.php');
require_once(DIR_ALVEOLATA . '/list/functions.php');
require_once(DIR_ALVEOLATA . '/sql/functions.php');
require_once(DIR_ALVEOLATA . '/report/functions.php');
require_once(DIR_ALVEOLATA . '/observer/wrapper-class.php');
require_once(DIR_ALVEOLATA . '/database/abstract/interface.php');
/**
* @author Christian Fraß <frass@greenscale.de>
*/
class struct_sqltable
{
/**
* @var \alveolata\database\interface_database
* @author Christian Fraß <frass@greenscale.de>
*/
public $database;
/**
* @var string
* @author Christian Fraß <frass@greenscale.de>
*/
public $name;
/**
* @var list<record<name:string,type:string,null_allowed:boolean,default:any>>
* @author Christian Fraß <frass@greenscale.de>
*/
public $fields;
/**
* @var \alveolata\observer\class_observer
*/
public $observer_teardown;
/**
* @var \alveolata\observer\class_observer
*/
public $observer_delete;
/**
* @author Christian Fraß <frass@greenscale.de>
*/
public function __construct(
\alveolata\database\interface_database $database,
string $name,
array $fields
)
{
$this->database = $database;
$this->name = $name;
$this->fields = $fields;
$this->observer_teardown = \alveolata\observer\class_observer::make();
$this->observer_delete = \alveolata\observer\class_observer::make();
}
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function sqltable_make(
\alveolata\database\interface_database $database,
string $name,
array $fields = UNSET_ARRAY
) : struct_sqltable
{
return (
new struct_sqltable(
$database,
$name,
$fields
)
);
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function sqltable_hook_delete(
struct_sqltable $subject,
\Closure $procedure
) : string
{
return $subject->observer_delete->register($procedure);
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function sqltable_teardown(
struct_sqltable $subject
) : void
{
$subject->observer_teardown->notify(null);
$template = \alveolata\string\coin(
'DROP TABLE IF EXISTS `{{tablename}}`',
[
'tablename' => $subject->name,
]
);
$subject->database->query(
$template
);
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function sqltable_setup(
struct_sqltable $subject
) : void
{
if ($subject->fields === UNSET_ARRAY) {
$report = \alveolata\report\make(
'SQL table setup not possible since fields have not been specified',
[
'tablename' => $subject->name,
]
);
throw (\alveolata\report\as_exception($report));
}
else {
$arguments = [];
$fielddescriptions = [];
// id
{
$fielddescription = \alveolata\string\coin(
'`id` {{definition}}',
[
'definition' => $subject->database->boilerplate_field_definition_for_integer_primary_key_with_auto_increment(),
]
);
array_push($fielddescriptions, $fielddescription);
}
// regular fields
{
}
foreach ($subject->fields as $field) {
$key = \alveolata\string\coin(
'defaultvalue_{{columnname}}',
[
'columnname' => $field['name'],
]
);
$fielddescription = \alveolata\string\coin(
'`{{columnname}}` {{type}} {{nullability}} DEFAULT {{defaultvalue}}',
[
'columnname' => $field['name'],
'type' => $field['type'],
'nullability' => ($field['null_allowed'] ? 'NULL' : 'NOT NULL'),
// 'defaultvalue' => $field['default'],
'defaultvalue' => sprintf(':%s', $key),
]
);
array_push($fielddescriptions, $fielddescription);
$value = ($field['default'] ?? 'NULL');
$arguments[$key] = $value;
}
$template = \alveolata\string\coin(
'CREATE TABLE IF NOT EXISTS `{{tablename}}`({{fields}});',
[
'tablename' => $subject->name,
'fields' => implode(', ', $fielddescriptions),
]
);
$subject->database->query(
$template,
$arguments
);
}
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function sqltable_create(
struct_sqltable $subject,
/*map<string,any> */$value
)/* : int*/
{
$fields = array_keys($value);
$template = \alveolata\string\coin(
'INSERT INTO `{{tablename}}`({{fields}}) VALUES ({{values}})',
[
'tablename' => $subject->name,
'fields' => implode(
',',
\alveolata\list_\map(
$fields,
function ($field) {
return \alveolata\string\coin('`{{field}}`', ['field' => $field]);
}
)
),
'values' => implode(
',',
\alveolata\list_\map(
$fields,
function ($field) {
return \alveolata\string\coin(':{{field}}', ['field' => $field]);
}
)
),
]
);
$arguments = $value;
$result = $subject->database->query(
$template,
$arguments
);
return $result['id'];
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function sqltable_update(
struct_sqltable $subject,
/*int */$key,
/*map<string,any> */$value
) : void
{
$fields = array_keys($value);
if (empty($fields)) {
}
else {
$template = \alveolata\string\coin(
'UPDATE `{{tablename}}` SET {{sets}} WHERE (`id` = :id)',
[
'tablename' => $subject->name,
'sets' => implode(
',',
\alveolata\list_\map(
$fields,
function ($field) {
return \alveolata\string\coin('`{{field}}` = :{{field}}', ['field' => $field]);
}
)
),
]
);
$arguments = array_merge(
[
'id' => $key,
],
$value,
);
$result = $subject->database->query(
$template,
$arguments
);
/*
if (! ($result['affected'] === 1)) {
$report = \alveolata\report\make(
'could not update dataset',
[
'tablename' => $subject->name,
'key' => $key
]
);
throw (\alveolata\report\as_exception($report));
}
else {
// do nothing
}
*/
}
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function sqltable_delete(
struct_sqltable $subject,
/*int */$key
) : void
{
$subject->observer_delete->notify($key);
$template = \alveolata\string\coin(
'DELETE FROM `{{tablename}}` WHERE (`id` = :id)',
[
'tablename' => $subject->name,
]
);
$arguments = [
'id' => $key,
];
$result = $subject->database->query(
$template,
$arguments
);
if (! ($result['affected'] === 1)) {
$report = \alveolata\report\make(
'could not delete dataset',
[
'tablename' => $subject->name,
'key' => $key
]
);
throw (\alveolata\report\as_exception($report));
}
else {
// do nothing
}
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function sqltable_read(
struct_sqltable $subject,
/*int */$key
)/* : map<string,any>*/
{
$template = \alveolata\string\coin(
'SELECT * FROM `{{tablename}}` WHERE (`id` = :id)',
[
'tablename' => $subject->name,
]
);
$result = $subject->database->query(
$template,
[
'id' => $key
]
);
if (count($result['rows']) !== 1) {
$report = \alveolata\report\make(
'none or ambiguous result on read',
[
'tablename' => $subject->name,
'id' => $key,
]
);
throw (\alveolata\report\as_exception($report));
}
else {
$row = $result['rows'][0];
unset($row['id']);
return $row;
}
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
function sqltable_search(
struct_sqltable $subject,
array $parameters = []
) : array
{
$condition = (
(count($parameters) === 0)
// ? 'TRUE'
? '(0 = 0)'
: \alveolata\string\coin(
'({{clauses}})',
[
'clauses' => \alveolata\sql\conjunction(
\alveolata\list_\map(
array_keys($parameters),
function ($key) {
return \alveolata\string\coin(
'`{{key}}` = :{{key}}',
[
'key' => $key,
]
);
}
)
),
]
)
);
$template = \alveolata\string\coin(
'SELECT `id` FROM `{{tablename}}` WHERE {{condition}}',
[
'tablename' => $subject->name,
'condition' => $condition,
]
);
$arguments = $parameters;
$result = $subject->database->query(
$template,
$arguments
);
return \alveolata\list_\map(
$result['rows'],
function ($row) {
return $row['id'];
}
);
}
?>