/** Datei: library/ARUNOX/Database/BaseAttribute.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Database; use JsonSerializable; use ARUNOX\Plugins\REST; abstract class BaseAttribute implements JsonSerializable { /* ### VARIABLES ################################################ # */ protected static ?string $TEXTDOMAIN = NULL; protected static ?string $DEFAULT_LABEL = NULL; protected static ?string $DEFAULT_ICON = NULL; protected static mixed $DEFAULT_COLOR = REST\Generator\Config\Color::Default; protected ?BaseEntity $parent = NULL; protected mixed $value = NULL; /* ### PUBLIC FUNCTIONS ######################################### # */ public function defaultColor() { return static::color(); } public function defaultLabel() { return static::label(); } public function defaultIcon() { return static::icon(); } public function is( mixed $value ) { return $this->value === $value; } public function getValue() : mixed { return $this->value; } public function setValue( mixed $value) : void { $this->value = $value; } public function getParent() : ?BaseEntity { return $this->parent; } public function setParent( BaseEntity $parent) : void { $this->parent = $parent; } public function jsonSerialize() : mixed { return $this->value; } /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function color() { return static::$DEFAULT_COLOR; } public static function label() { return static::$DEFAULT_LABEL; } public static function icon() { return static::$DEFAULT_ICON; } public static function getConstant( mixed $value ) { return array_search( $value, static::getConstants() ); } public static function getConstants() { $class = get_called_class(); $object = new \ReflectionClass( $class ); return $object->getConstants(); } public static function getByConstant( ?string $name = NULL, mixed $default = NULL ) { if( $name === NULL ) return $default; $name = preg_replace( "/[\s_-]+/", "", $name ); $constants = static::getConstants(); $constants = array_change_key_case( $constants, CASE_LOWER ); return $constants[ strtolower( $name ) ] ?? $default; } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( mixed $value, ?BaseEntity $parent = NULL ) { $this->value = $value; $this->parent = $parent; } public function __toString() : string { return $this->value; } public function __get( string $name ) { if( method_exists( $this, $name ) ) { return $this->{$name}(); } } } /** Datei: library/ARUNOX/Database/Attribute/Options.php **/ getName() ] = $attribute->get(); } unset( $option["attributes"] ); } $hookName = Hooks::parseName( get_called_class() ); $options = array_replace( $options, static::$OPTIONS ); $options = Hooks::filterWithNames( "database.attribute.options::getAll", $hookName, $options ); foreach( $options as $value => &$option ) { $option = array_merge( $option, [ "value" => $value ] ); $option = Hooks::filterWithNames( "database.attribute.options::get", $hookName, $option ); } // if( static::class == \ARUNOX\Plugins\Websites\Connections\Status::class ) { // show($options, FALSE); // } return $options; } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( mixed $value, $parent = NULL ) { if( is_bool( $value ) ) $value = (int)$value; if( is_a( $value, self::class ) ) $value = $value->getValue(); parent::__construct( $value, $parent ); $options = static::getOptions(); if( !$options ) return; // if( !is_numeric($value) ) show( $value, get_class( $value ), self::class, FALSE ); if( !isset( $options[ $value ] ) ) return; $this->value = $value; $this->icon = $options[ $value ]["icon"]; $this->color = $options[ $value ]["color"]; $this->label = $options[ $value ]["label"]; foreach( $options[ $this->value ] as $key => $value ) { if( property_exists( $this, $key ) ) { $this->{$key} = $options[ $this->value ][ $key ]; } else { $this->dynamic[ $key ] = $options[ $this->value ][ $key ]; } } } #[PHP\Attributes\Magic\Call( "is([A-Z][a-zA-Z0-9]+)" )] public function handleOptionsIsCall( array $matches, ...$args ) { if( NULL !== $value = static::getByConstant( $matches[1] ) ) { return $this->is( $value ); } throw new PHP\Attributes\Magic\Call\Exceptions\PatternFailed(); } // #[PHP\Attributes\Magic\Call( "get([A-Z][a-zA-Z0-9]+)" )] // public static function handleOptionsGetCall( array $matches, ...$args ) { // return $matches[0]; // } // #[PHP\Attributes\Magic\Call( "get([A-Z][a-zA-Z0-9]+)" )] // public function handleOptionsGetCall( array $matches, ...$args ) { // $key = strtolower( $matches[1] ); // // if( property_exists( $this, $key ) ) return $this->{$key}; // if( key_exists( $key, $this->dynamic ) ) return $this->dynamic[ $key ]; // // throw new PHP\Attributes\Magic\Call\Exceptions\PatternFailed(); // } } /** Datei: library/ARUNOX/Database/Attribute/DateTime.php **/ format( $format ); } public function time( $format = FORMAT_TIME ) { return $this->format( $format ); } public function datetime( $format = FORMAT_DATETIME ) { return $this->format( $format ); } public function format( $format ) { return $this->value ? dateformat( $format, strtotime( $this->value ), FORMAT_TIMEZONE, FORMAT_LOCALE ) : NULL; } public function timestamp() { return $this->value ? strtotime( $this->value ) : NULL; } public function setTimestamp( $timestamp ) { return $this->value = dateformat( "Y-m-d H:i:s", $timestamp, FORMAT_TIMEZONE, FORMAT_LOCALE ); } public function addTimestamp( $timestamp ) { return $this->value = dateformat( "Y-m-d H:i:s", $this->timestamp() + $timestamp, FORMAT_TIMEZONE, FORMAT_LOCALE ); } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( mixed $value, ?BaseEntity $parent = NULL ) { if( $value === TRUE ) $value = time(); if( is_string( $value ) ) $value = strtotime( $value ); if( is_numeric( $value ) ) $value = dateformat( "Y-m-d H:i:s", $value ); parent::__construct( $value, $parent ); } public function __toString() : string { return $this->datetime(); } } /** Datei: library/ARUNOX/Database/Attribute/File.php **/ __toString(); } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( mixed $value, ?Database\BaseEntity $parent = NULL ) { if( API\Filesystem::isFile( STORAGE_DIR . $value ) ) { $value = API\Filesystem::getFile( STORAGE_DIR . $value ); } parent::__construct( $value, $parent ); } public function __toString() : string { return (string)$this->url; } public function __get( string $name ) { if( $this->value ) { return $this->value?->{$name} ?? NULL; } return parent::__get( $name ); } } /** Datei: library/ARUNOX/Database/Attribute/Status.php **/ isset( $item["toggle"] ) && $item["toggle"] ); $options = array_map( fn( $item ) => $item["name"], $options ); ksort( $options ); $this->value++; if( $this->value > array_key_last( $options ) ) { $this->value = array_key_first( $options ); } return $this->value; } } /** Datei: library/ARUNOX/Database/BaseModelWithMeta.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created * @since 1.1.0 Modified get() and delete() to accept string values */ namespace ARUNOX\Database; use ARUNOX\Database; abstract class BaseModelWithMeta extends BaseModel { /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function hasMeta( ?string $meta_key = NULL ) : bool { if( !Database::existsTable( static::getTable( "meta" ) ) ) { return false; } if( $meta_key === null ) { return true; } return QueryBuilder::select(static::getTable( "meta" ), ["meta_key"], [ "meta_key" => $meta_key, ])->fetch() !== false; } public static function get( int|string|array $value, string|array $columns = ["*"] ) : BaseEntity|false { $record = parent::get( $value, $columns ); if( !in_array( "*", (array)$columns ) ) { return $record; } if( !$record || !static::hasMeta() ) { return $record; } $metaRecords = static::getAllMeta($record->{static::$PRIMARY_KEY}); foreach( $metaRecords as $key => $value ) { $record->__set( $key, $value ); } return $record; } public static function getAll( null|array|int|string $where = [], string|array $columns = ['*'] ) : array { $records = parent::getAll( $where, $columns ); if( !$records || !static::hasMeta() ) { return $records; } foreach( $records as $record ) { $metaRecords = static::getAllMeta( $record->{static::$PRIMARY_KEY} ); foreach( $metaRecords as $key => $value ) { $record->__set( $key, $value ); } } return $records; } public static function save( array $data ) : mixed { $columns = static::getColumns(); $columns = array_flip( $columns ); $mainData = array_intersect_key( $data, $columns ); $metaData = array_diff_key( $data, $columns ); $return = parent::save( $mainData ); $primid = parent::getValue( $return, static::$PRIMARY_KEY ); if( !empty( $metaData ) ) { // show( $id, $metaData ); static::saveAllMeta( $primid, $metaData ); } return $return; } public static function delete( int|string|array $value): int { // Hauptdatensatz löschen $deletedRows = parent::delete( $value ); // Meta-Daten löschen, falls die Tabelle existiert if( static::hasMeta() ) { if( is_array( $value ) ) { $value = $value["origin_id"]; } QueryBuilder::delete( static::getTable( "meta" ), [ 'origin_id' => $value, ])->execute(); } return $deletedRows; } /* ### PUBLIC STATIC META FUNCTIONS ############################# # */ public static function getMeta(int $origin, string $meta_key): mixed { $result = QueryBuilder::select( static::getTable( "meta" ), ['meta_value'], [ 'origin_id' => $origin, 'meta_key' => $meta_key, ])->fetch(); return $result['meta_value'] ?? null; } public static function getAllMeta(int $origin): array { $metaRecords = QueryBuilder::select( static::getTable( "meta" ), ['meta_key', 'meta_value'], [ 'origin_id' => $origin, ])->fetchAll(); return array_column($metaRecords, 'meta_value', 'meta_key'); } public static function saveMeta( int $origin, string $meta_key, mixed $meta_value ): void { if( $meta_value instanceof BaseAttribute ) { $meta_value = (string)$meta_value; } QueryBuilder::insert( static::getTable( "meta" ), [ 'origin_id' => $origin, 'meta_key' => $meta_key, 'meta_value' => is_scalar( $meta_value ) ? $meta_value : json_encode( $meta_value ), ])->onDuplicateKeyUpdate([ 'meta_value' => is_scalar( $meta_value ) ? $meta_value : json_encode( $meta_value ), ])->execute(); } public static function saveAllMeta(int $origin, array $meta_key_value): void { foreach( $meta_key_value as $key => $value ) { static::saveMeta( $origin, $key, $value ); } } public static function deleteMeta(int $origin, string $meta_key): void { QueryBuilder::delete( static::getTable( "meta" ), [ 'origin_id' => $origin, 'meta_key' => $meta_key, ])->execute(); } public static function deleteAllMeta(int $origin): void { QueryBuilder::delete( static::getTable( "meta" ), [ 'origin_id' => $origin, ])->execute(); } } /** Datei: library/ARUNOX/Database/QueryBuilder.php **/ type( "select" ) ->table( $table ) ->columns( (array)$columns ) ->where( $where ); } public static function insert( ?string $table = NULL, array $data = [] ): self { return (new self()) ->type( "insert" ) ->table( $table ) ->data( $data ); } public static function update( ?string $table = NULL, array $data = [], array $where = [] ): self { return (new self()) ->type( "update" ) ->table( $table ) ->data( $data ) ->where( $where ); } public static function delete( ?string $table = NULL, array $where = [] ): self { return (new self()) ->type( "delete" ) ->table( $table ) ->where( $where ); } public static function truncate( ?string $table = NULL ) : self { return (new self()) ->type( "truncate" ) ->table( $table ); } /* ### PUBLIC CHAIN FUNCTIONS ################################### # */ public function type( string $type ) : self { $this->type = strtolower( $type ); return $this; } public function table( ?string $table ) : self { if( $table ) { $this->table = $table; } return $this; } public function columns( array|string $columns = [] ) : self { if( $columns ) { $this->columns = explode( ",", implode( ",", (array)$columns ) ); } return $this; } public function data( ?array $data = [] ) : self { if( $data ) { foreach( $data as $name => $value ) { $this->addData( $name, $value ); } } return $this; } public function addData( string $name, mixed $value ) : self { $this->data[ $name ] = $value; $this->bindings[":{$name}"] = $value; return $this; } public function where( ?array $conditions = [] ) : self { if( $conditions ) { $this->where = Filters::buildWhereClause( $conditions, $this->bindings, $this->table, [], $this->database ); if( key_exists( "ORDERBY", $conditions ) ) { $this->addOrderBy( $conditions["ORDERBY"] ); } if( key_exists( "LIMIT", $conditions ) ) { $this->limit( $conditions["LIMIT"] ); } // if( $this->table == "logs" ) // show( $conditions, $this->where, $this->bindings, FALSE ); } return $this; } public function addWhere( array $conditions = [] ) : self { if( $conditions ) { $this->where = Filters::buildWhereClause( $conditions, $this->bindings, $this->table, $this->where, $this->database ); } return $this; } public function addOrderBy( array|string $orderby ) { $orderby = explode( ",", $orderby ); $orderby = array_map( fn( $item ) => trim( $item ), $orderby ); $orderby = array_filter( $orderby ); if( !$orderby ) return $this; $orderby = array_map( fn( $item ) => explode( " ", $item ), $orderby ); $orderby = array_map( fn( $item ) => array_pad( $item, 2, "asc" ), $orderby ); // show( $orderby ); foreach( $orderby as [ $column, $direction ] ) { $this->orderBy[] = implode(" ", [ $column, strtoupper( $direction ) ] ); } // show( $this->orderBy ); return $this; } public function orderBy( ?string $column, ?string $direction = 'ASC' ) : self { if( $column ) { $this->addOrderBy( $column, $direction ); } return $this; } public function limit( ?int $rowCount = NULL, ?int $offset = NULL ) : self { if( $rowCount ) { $this->limit = $rowCount; $this->offset = $offset; } return $this; } public function onDuplicateKeyUpdate( array $updateValues, array $exclude = [] ) : self { $updateParts = []; foreach ($updateValues as $key => $value) { if (in_array($key, $exclude)) continue; $updateParts[] = "{$key} = :update_{$key}"; $this->updateBindings[":update_{$key}"] = $value; } $this->bindings = array_merge($this->bindings, $this->updateBindings); // Binde Update-Werte $this->bindings[':onDuplicateKeyUpdate'] = implode(', ', $updateParts); // Speichere SQL-Teile in `data` return $this; } public function using( string $name ) : self { if( Connections::has( $name ) ) { $this->database = Connections::get( $name ); } return $this; } /* ### PUBLIC EXECUTE FUNCTIONS ################################# # */ public function execute() : mixed { try { $this->database->query( $this->getSql(), $this->parseBindung()->bindings ); if( $this->type === "insert" ) return $this->database->lastInsertId(); return TRUE; } catch( \Exception $e ) { return FALSE; } } public function fetch( string|bool $entityClass = TRUE ) { return $this->database->fetch( $this->getSql(), $this->bindings, $entityClass ); } public function fetchAll( string|bool $entityClass = TRUE ) : array|false|BaseEntity|\stdClass { return $this->database->fetchAll( $this->getSql(), $this->bindings, $entityClass ); } public function fetchColumn( string|bool $entityClass = TRUE ) : mixed { return $this->database->fetchColumn( $this->getSql(), $this->bindings ); } public function fetchColumns( string|bool $entityClass = TRUE ) : array|false|BaseEntity|\stdClass { return $this->database->fetchColumns( $this->getSql(), $this->bindings ); } /* ### PUBLIC GETTER FUNCTIONS ################################## # */ public function getBindings() : array { return $this->bindings; } public function getData( ?string $name = NULL ) : mixed { return $name ? $this->data[ $name ] ?? NULL : $this->data; } public function getSql(): string { $sql = ''; switch ($this->type) { case 'select': $columns = implode(', ', $this->columns ?: ['*']); $sql = "SELECT {$columns} FROM `{$this->table}`"; break; case 'insert': $columns = implode(', ', array_map( function( $item ) { return "`{$item}`"; }, array_keys( $this->data ) ) ); $placeholders = ':' . implode(', :', array_keys($this->data)); $sql = "INSERT INTO `{$this->table}` ({$columns}) VALUES ({$placeholders})"; // Falls onDuplicateKeyUpdate definiert wurde if (!empty($this->bindings[':onDuplicateKeyUpdate'])) { $sql .= " ON DUPLICATE KEY UPDATE {$this->bindings[':onDuplicateKeyUpdate']}"; unset( $this->bindings[':onDuplicateKeyUpdate'] ); } break; case 'update': $set = implode(', ', array_map(fn($key) => "`{$key}` = :{$key}", array_keys($this->data))); $sql = "UPDATE `{$this->table}` SET {$set}"; break; case 'delete': $sql = "DELETE FROM `{$this->table}`"; break; case 'truncate': $sql = "TRUNCATE TABLE `{$this->table}`"; break; } if (!empty($this->where)) { $sql .= ' WHERE ' . implode(' AND ', $this->where); } if ($this->type === 'select' && !empty( $this->orderBy ) ) { $sql .= " ORDER BY " . implode( ",", $this->orderBy ); } if ($this->type === 'select' && $this->limit !== null) { $sql .= $this->offset !== null ? " LIMIT {$this->offset}, {$this->limit}" : " LIMIT {$this->limit}"; } return $sql; } /* ### PRIVATE FUNCTIONS ######################################## # */ private function parseBindung() { foreach( $this->bindings as &$binding ) { if( is_array( $binding ) ) { $binding = json_encode( $binding ); } } return $this; } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct() { $this->database = Connections::current(); } public function __get( $name ) { if( property_exists( $this, $name ) ) return $this->{$name}; return NULL; } } /** Datei: library/ARUNOX/Database/Database.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 File is created */ namespace ARUNOX; use PDO; use PDOException; use ARUNOX\Database\BaseEntity; use ARUNOX\Database\Connections; use ARUNOX\Database\QueryBuilder; class Database { /* ### VARIABLES ################################################ # */ public ?PDO $connection = NULL; private array $config = []; /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function connect( array $config ) : Database { return new self( $config ); } public static function parseSQLArgs( array $args, array &$params = [] ) : string { $query = QueryBuilder::select() ->table( $args['table'] ) ->columns( $args['fields'] ?? "*" ) ->where( $args['where'] ?? NULL ) ->orderBy( $args['orderby'] ?? NULL, $args['order'] ?? NULL ) ->limit( $args['limit'] ?? NULL ); $params = $query->getBindings(); $sql = $query->getSql(); return $sql; } public static function getDatabase() : array { $current = Connections::current(); try { $stmt = $current->query("SHOW DATABASES"); return $stmt->fetchAll( PDO::FETCH_COLUMN ); } catch (PDOException $e) { return false; } } public static function existsDatabase( string $database ) : bool { $current = Connections::current(); try { $stmt = $current->query("SHOW DATABASES LIKE :database", [ "database" => $database ]); return !!$stmt->fetch(); } catch (PDOException $e) { return false; } } /* ### PSEUDO STATIC FUNCTIONS ################################## # */ private function pseudoFetchColumn( string|array $sql, array $params = [] ) : mixed { $stmt = $this->query( $sql, $params ); return $stmt->fetchColumn(); } private function pseudoFetchColumns( string|array $sql, array $params = [] ) : array|false { $stmt = $this->query( $sql, $params ); return $stmt->fetchAll( PDO::FETCH_COLUMN ); } private function pseudoFetchAll( string|array $sql, array $params = [], string|bool $entityClass = FALSE ) : array|false { $stmt = $this->query( $sql, $params ); if( $entityClass === FALSE ) return $stmt->fetchAll( PDO::FETCH_ASSOC ); if( $entityClass === TRUE ) return $stmt->fetchAll( PDO::FETCH_OBJ ); return $stmt->fetchAll( PDO::FETCH_CLASS, $entityClass ); } private function pseudoFetch( string|array $sql, array $params = [], string|bool $entityClass = FALSE ) { $stmt = $this->query( $sql, $params ); if( $entityClass === FALSE ) return $stmt->fetch( PDO::FETCH_ASSOC ); if( $entityClass === TRUE ) return $stmt->fetch( PDO::FETCH_OBJ ); $stmt->setFetchMode(\PDO::FETCH_CLASS, $entityClass ); return $stmt->fetch(); } private function pseudoExistsTable( string $table ) : bool { try { $stmt = $this->query( "SHOW TABLES LIKE :table", [ "table" => $table ] ); return !!$stmt->fetch(); } catch (PDOException $e) { return false; } } private function pseudoGetTables( ?string $where = NULL) : array { try { if( $where ) { $stmt = $this->query( "SHOW TABLES LIKE :where", [ "where" => $where ] ); } else $stmt = $this->query( "SHOW TABLES" ); return $stmt->fetchAll( PDO::FETCH_COLUMN ); } catch (PDOException $e) { return false; } } private function pseudoExistsColumn( string $table, string $column ) : bool { try { $stmt = $this->query( " SELECT * FROM `information_schema`.`columns` WHERE `TABLE_SCHEMA` = :schema AND `TABLE_NAME` = :table AND `COLUMN_NAME` = :column ", [ "table" => $table, "column" => $column, "schema" => $this->getConfig("dbname"), ]); return !!$stmt->fetch(); } catch (PDOException $e) { return false; } } private function pseudoGetColumns( string $table ) : array { try { $stmt = $this->query( " SELECT `COLUMN_NAME` FROM information_schema.columns WHERE `TABLE_SCHEMA` = :schema AND `TABLE_NAME` = :table ", [ "table" => $table, "schema" => $this->getConfig("dbname"), ]); return $stmt->fetchAll( PDO::FETCH_COLUMN ); } catch (PDOException $e) { return false; } } private function pseudoGetSchema( string $table ) : array { $schema = [ 'columns' => [], 'primary' => [], 'indexes' => [], ]; // 🔹 1. Spalten lesen $columns = $this->fetchAll(" SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, EXTRA FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = '{$table}' ORDER BY ORDINAL_POSITION "); foreach ($columns as $col) { $def = strtoupper($col['COLUMN_TYPE']); if ($col['IS_NULLABLE'] === 'NO') { $def .= ' NOT NULL'; } // DEFAULT-Wert if ($col['COLUMN_DEFAULT'] !== null) { if ($this->isSqlLiteral( $col['COLUMN_DEFAULT'] )) { $def .= " DEFAULT {$col['COLUMN_DEFAULT']}"; } else { $def .= " DEFAULT '" . addslashes($col['COLUMN_DEFAULT']) . "'"; } } // ON UPDATE / AUTO_INCREMENT etc. if (!empty($col['EXTRA'])) { $extra = strtoupper(str_replace('DEFAULT_GENERATED', '', $col['EXTRA'])); $def .= ' ' . trim($extra); } $schema['columns'][$col['COLUMN_NAME']] = trim($def); } // 🔹 2. Indexe + Primärschlüssel $indexes = $this->fetchAll(" SELECT INDEX_NAME, COLUMN_NAME, NON_UNIQUE FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = '{$table}' ORDER BY INDEX_NAME, SEQ_IN_INDEX "); foreach ($indexes as $idx) { if ($idx['INDEX_NAME'] === 'PRIMARY') { $schema['primary'][] = $idx['COLUMN_NAME']; } else { $schema['indexes'][$idx['INDEX_NAME']][] = $idx['COLUMN_NAME']; } } // 🔹 3. Engine + Charset $tableInfo = $this->fetchRow(" SELECT ENGINE, TABLE_COLLATION FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = '{$table}' "); if ($tableInfo) { $schema['engine'] = $tableInfo['ENGINE']; if (str_contains($tableInfo['TABLE_COLLATION'], '_')) { $schema['charset'] = explode('_', $tableInfo['TABLE_COLLATION'])[0]; } } return $schema; } private function pseudoInstallSchema( array $schema ) : bool { foreach( $schema as $table => &$definition) { if( $this->existsTable( $table ) ) continue; $definition = $this->createTable( $table, $definition ); } $schema = array_filter( $schema ); return !!$schema; } private function pseudoUpdateSchema( array $schema ) : bool { foreach( $schema as $table => &$definition) { if( !$this->existsTable( $table ) ) { $definition = $this->createTable( $table, $definition ); continue; } $definition = $this->updateTable( $table, $definition ); } $schema = array_filter( $schema ); return !!$schema; } private function pseudoCreateTable( string $table, array $schema ) : bool { if (empty($schema['columns'])) return false; try { $fields = []; foreach ($schema['columns'] as $name => $definition) { $fields[] = "`{$name}` {$definition}"; } if (!empty($schema['primary'])) { $columns = implode('`, `', $schema['primary']); $fields[] = "PRIMARY KEY (`{$columns}`)"; } if (!empty($schema['indexes'])) { foreach ($schema['indexes'] as $index => $cols) { $colList = implode('`, `', (array)$cols); $fields[] = "KEY `{$index}` (`{$colList}`)"; } } $fields = implode(",\n ", $fields ); $engine = $schema["engine"] ?? "InnoDB"; $charset = $schema["charset"] ?? "utf8mb4"; $this->query( " CREATE TABLE IF NOT EXISTS `{$table}` ( {$fields} ) ENGINE={$engine} DEFAULT CHARSET={$charset}; ")->execute(); return TRUE; } catch( \Throwable $e ) { return FALSE; } } private function pseudoUpdateTable( string $table, array $schema ) : bool { if (empty($schema['columns'])) return false; try { $current = $this->pseudoGetSchema($table); if (!$current || empty($current['columns'])) return false; $alters = []; // 🔍 Spalten prüfen foreach ($schema['columns'] as $col => $definition) { if (!isset($current['columns'][$col])) { // ➕ Spalte fehlt $alters[] = "ADD COLUMN `{$col}` {$definition}"; } elseif (trim(strtoupper($current['columns'][$col])) !== trim(strtoupper($definition))) { // 🔄 Spaltendefinition unterscheidet sich $alters[] = "MODIFY COLUMN `{$col}` {$definition}"; } } // 🔍 Primary Key prüfen if (!empty($schema['primary']) && $schema['primary'] !== $current['primary']) { $alters[] = "DROP PRIMARY KEY"; $alters[] = "ADD PRIMARY KEY (`" . implode('`, `', $schema['primary']) . "`)"; } // 🔍 Indexe prüfen (nur hinzufügen – kein remove) foreach ($schema['indexes'] as $key => $cols) { if (!isset($current['indexes'][$key])) { $colList = implode('`, `', (array)$cols); $alters[] = "ADD KEY `{$key}` (`{$colList}`)"; } } if (empty($alters)) return true; $this->query("ALTER TABLE `{$table}`\n" . implode(",\n", $alters) . ";")->execute(); return TRUE; } catch( \Throwable $e ) { return FALSE; } } private function isSqlLiteral( string $default ) : bool { static $keywords = [ 'CURRENT_TIMESTAMP', 'CURRENT_DATE', 'CURRENT_TIME', 'NOW()', 'SYSDATE()', 'NULL' ]; return in_array(strtoupper($default), $keywords, true); } /* ### PUBLIC FUNCTIONS ######################################### # */ public function query( string|array $sql, array $params = [] ) : \PDOStatement { if( is_array( $sql ) ) { $sql = static::parseSQLArgs( $sql, $params ); } $stmt = $this->connection->prepare( $sql ); $stmt->execute( $params ); return $stmt; } public function lastInsertId() : int { return $this->connection->lastInsertId(); } public function getConfig( ?string $name = NULL ) : string|array { return $name ? $this->config[ $name ] : $this->config; } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( array $config, string $name = "default" ) { $dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset={$config['charset']}"; $this->config = $config; try { $this->connection = new PDO($dsn, $config['user'], $config['pass'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); } catch (PDOException $e) { throw new PDOException('Database connection failed: ' . $e->getMessage()); } } public function __call( string $name, $args ) { if( method_exists( $this, "pseudo{$name}" ) ) { return $this->{"pseudo{$name}"}( ...$args ); } return NULL; } public static function __callStatic( string $name, $args ) { $current = Connections::current(); if( method_exists( $current, "pseudo{$name}" ) ) { return $current->{"pseudo{$name}"}( ...$args ); } return NULL; } } /** Datei: library/ARUNOX/Database/Filters.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Database; use ARUNOX\Database; class Filters { /** * Builds SQL WHERE clauses from the provided conditions array. * * @param array $conditions The conditions to build the WHERE clause. * @param array &$params The parameters for prepared statements. * @return array The list of SQL conditions. */ public static function buildWhereClause( array $conditions, array &$params, string $table, array $whereClauses = [], ?Database $database = NULL ): array { $columns = $database->getColumns( $table ) ?? []; // if( $table == "logs" ) show( $conditions ); foreach( $conditions as $key => $value ) { if( strpos( $key, "__" ) !== FALSE ) { [ $field, $operator ] = explode('__', $key); $whereClauses[] = match( strtoupper( $operator ) ) { "SQL" => self::handleSQLLogic( $field, $value, $params ), "DATE" => self::handleDateLogic( $field, $value, $params ), "TIME" => self::handleTimeLogic( $field, $value, $params ), "DATETIME" => self::handleDateTimeLogic( $field, $value, $params ), "JSON" => self::handleJsonLogic( $field, $value, $params) , "QUERY" => self::handleQueryLogic ($field, $value, $params ), "META" => self::handleMetaLogic( $field, $value, $params, $table ), default => self::handleDefaultLogic( $operator, $columns, $field, $value, $params ), }; continue; } if( is_array( $value ) ) { $relation = "AND" ; $subConds = &$value; if( isset( $subConds["RELATION"] ) ) { $relation = $subConds["RELATION"]; unset( $subConds["RELATION"] ); } if( isset( $subConds["CONDITIONS"] ) ) { $subConds = $subConds["CONDITIONS"]; } // show( $subConds, FALSE ); $relation = strtoupper( $relation ); $subConds = self::buildWhereClause( $subConds, $params, $table, [], $database ); $whereClauses[] = "(" . implode(" {$relation} ", $subConds ) . ")"; // show( $subConds, FALSE ); continue; } if( $columns && !in_array( $key, $columns ) ) continue; // show( $key, $value, $params, FALSE ); $paramKey = ":{$key}_" . count($params); $whereClauses[] = "{$key} = {$paramKey}"; $params[$paramKey] = $value; } return array_filter( $whereClauses, fn( $item ) => $item !== NULL ); } private static function handleMetaLogic( string $field, string $value, array &$params, string $table ) : string { $metaTable = "{$table}_meta"; // Haupttabelle bestimmen $paramKeyValue = ":meta_value_" . count($params); $paramKeyKey = ":meta_key_" . count($params); $params[$paramKeyKey] = str_replace('__meta', '', $field); // Meta-Schlüssel extrahieren $params[$paramKeyValue] = $value; return "EXISTS (SELECT 1 FROM {$metaTable} WHERE {$metaTable}.origin_id = {$table}.id AND {$metaTable}.meta_key = {$paramKeyKey} AND {$metaTable}.meta_value = {$paramKeyValue})"; } private static function handleDateLogic( string $field, array $value, array &$params ) : string { $clauses = []; foreach ($value as $datePart => $dateValue) { $negation = str_starts_with($datePart, '!'); $datePart = ltrim($datePart, '!'); $paramKey = ":{$field}_{$datePart}_" . count($params); $clause = "EXTRACT({$datePart} FROM {$field})"; $clause .= $negation ? " != {$paramKey}" : " = {$paramKey}"; $clauses[] = $clause; $params[$paramKey] = $dateValue; } return '(' . implode(' AND ', $clauses) . ')'; } private static function handleTimeLogic( string $field, array $value, array &$params ) : string { $clauses = []; foreach ($value as $timePart => $timeValue) { $negation = str_starts_with($timePart, '!'); $timePart = ltrim($timePart, '!'); $paramKey = ":{$field}_{$timePart}_" . count($params); $clause = "EXTRACT({$timePart} FROM {$field})"; $clause .= $negation ? " != {$paramKey}" : " = {$paramKey}"; $clauses[] = $clause; $params[$paramKey] = $timeValue; } return '(' . implode(' AND ', $clauses) . ')'; } private static function handleJsonLogic( string $field, mixed $value, array &$params ) : string { if( is_string( $value ) ) { return "JSON_CONTAINS( `{$field}`, '\"{$value}\"')"; } return "JSON_OVERLAPS( `{$field}`, '" . json_encode( $value ) . "')"; } private static function handleDateTimeLogic( string $field, array $value, array &$params ) : string { return self::handleDateLogic($field, $value, $params) . ' AND ' . self::handleTimeLogic($field, $value, $params); } private static function handleSQLLogic( string $field, string $sql, array &$params ) : string { return $sql; } private static function handleQueryLogic( string $field, array $value, array &$params ) : string { $joinTable = $value['table'] ?? null; $onCondition = $value['on'] ?? null; $queryConditions = $value['conditions'] ?? []; if (!$joinTable || !$onCondition) { throw new \InvalidArgumentException("Invalid query logic for {$field}."); } $subParams = []; $subClauses = self::buildWhereClause($queryConditions, $subParams, $joinTable ); $params = array_merge($params, $subParams); return "EXISTS (SELECT 1 FROM {$joinTable} WHERE {$onCondition} AND " . implode(' AND ', $subClauses) . ")"; } private static function handleDefaultLogic( string $operator, array $columns, string $field, mixed $value, array &$params ) : null|string { [ $name, $operator, $replace ] = self::mapOperator( $operator ); if( $replace ) { $key = preg_replace( "/__{$operator}/", "__{$name}", $key ); } // show( $key, $replace, $field, $value, $params, $name, $operator ); if( in_array( $operator, [ 'IN', 'NOT IN' ] ) ) { $value = (array)$value; } if( $columns && !in_array( $field, $columns ) ) return NULL; $clause = "{$field} {$operator} "; if( is_array( $value ) ) { $clause .= "( "; foreach( (array)$value as $subValue ) { $paramKey = ":{$field}_" . count($params); $params[$paramKey] = $subValue; $clause .= "{$paramKey}, "; } $clause = trim( $clause, ", " ); $clause .= " )"; } else { $paramKey = ":{$field}_" . count($params); $clause .= $paramKey; $params[$paramKey] = $value; } // if( $field == "date_create" ) show( $field, $value, $clause, $params, FALSE ); return $clause; } private static function mapOperator(string $operator) : array { $operator = strtolower( $operator ); $result = []; $operators = [ "in" => "IN", "not_in" => "NOT IN", "gt" => ">", "gte" => ">=", "lt" => "<", "lte" => "<=", "ne" => "!=", "not" => "!=", "like" => "LIKE", "not_like" => "NOT LIKE", "exists" => "EXISTS", "not_exists" => "NOT EXISTS", ]; if( key_exists( $operator, $operators ) ) { $result[] = $operator; $result[] = $operators[ $operator ]; $result[] = FALSE; } if( in_array( $operator, $operators ) ) { $result[] = array_search( $operator, $operators ); $result[] = $operator; $result[] = TRUE; } if( !$result ) { $result[] = ""; $result[] = "="; $result[] = FALSE; } return $result; } } /** Datei: library/ARUNOX/Database/Connections.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.1.0 Hinzufügen von 'switch' und merken der aktuellen Verbindung * @since 1.0.0 File is created */ namespace ARUNOX\Database; use PDOException; use ARUNOX\Database; class Connections { /* ### VARIABLES ################################################ # */ protected static array $CONNECTIONS = []; private static ?Database $CONNECTION = NULL; /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function connect( array $config, string $name = "default", bool $reconnect = FALSE ) : Database { if( !$reconnect && isset( self::$CONNECTIONS[ $name ] ) ) { throw new PDOException("Connection '{$name}' already exists."); } self::$CONNECTIONS[ $name ] = Database::connect( array_merge( $config, [ "name" => $name ] ) ); return static::switch( $name ); } public static function switch( string $name = "default" ) : Database { return self::$CONNECTION = self::get( $name ); } public static function current() : Database { if( !isset( self::$CONNECTION ) ) { throw new PDOException("Current Connection not exists."); } return self::$CONNECTION; } public static function has( string $name ) : bool { return isset( self::$CONNECTIONS[ $name ] ); } public static function get( string $name ) : Database { if( !isset( self::$CONNECTIONS[ $name ] ) ) { throw new PDOException("Connection '{$name}' not found."); } return self::$CONNECTIONS[$name]; } public static function getAll() : array { return self::$CONNECTIONS; } public static function remove( string $name ) : void { unset( self::$CONNECTIONS[ $name ] ); } public static function clear() : void { self::$CONNECTIONS = []; } /* ### CHAT GPT FUNCTIONS ####################################### # */ } /** Datei: library/ARUNOX/Database/BaseEntity.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Database; use ARUNOX\API\PHP; use JsonSerializable; abstract class BaseEntity implements JsonSerializable { use PHP\Traits\Helpers\Controller; use PHP\Attributes\Helper; use PHP\Attributes\Magic\Get\Helper; use PHP\Attributes\Magic\Set\Helper; use PHP\Attributes\Magic\Call\Helper; use PHP\Attributes\Database\Attributes\Helper; use PHP\Attributes\Database\Attributes\Resolve\Helper; /* ### VARIABLES ################################################ # */ protected array $attributes = []; protected static array $COMMANDS = [ "array" => [ "LENGTH" => "count" ], ]; /* ### PUBLIC STATIC FUNCTIONS ################################## # */ /* ### PUBLIC FUNCTIONS ######################################### # */ public function getHashID() : string { return static::callModelClass( "getHashID", $this->id ) ?? $this->id; } public function save( array $data = [] ): mixed { if( $data ) { foreach( $data as $key => $value ) { $this->{$key} = $value; } $primaryKey = static::callModelClass("getPrimaryKey"); if( !isset( $data[ $primaryKey ] ) ) { $data[ $primaryKey ] = $this->{$primaryKey}; } } else $data = json_decode( json_encode( $this ), TRUE ); return static::callModelClass( "save", $data ); } public function delete(): mixed { return static::callModelClass( "delete", $this->{static::callModelClass("getPrimaryKey")} ); } public function jsonSerialize() : array { return $this->toArray(); } public function toArray() : array { foreach( static::getAttributes() as $name => $void ) { $this->loadAttributeValue( $name ); } // if( $this->id == 31 ) show( $this->attributes ); return $this->attributes; } /* ### PRIVATE FUNCTIONS ######################################## # */ private function loadAttributeValue( string $name ) { if( !static::existsAttribute( $name ) ) return; $config = static::getAttribute( $name ); if( class_exists( $config ) ) return; if( method_exists( $this, "get{$config}" ) ) return $this->{$name} = $this->{"get{$config}"}(); if( isset( $this->attributes[ $name ] ) && $this->attributes[ $name ] !== NULL ) return; if( $this->isAttributeChain( $config ) ) { $this->{$name} = $this->loadAttributeChain( $config ); } } private function loadAttributeChain( string $chain ) { if( !$this->isAttributeChain( $chain ) ) return NULL; $segments = preg_split( "/(->|\.|\(\))/", $chain ); $attribute = array_shift( $segments ); $attribute = $this->{$attribute}; $callable = $segments && end( $segments ) == ""; $segments = array_filter( $segments, fn( $value ) => $value !== NULL ); // if( static::class == \ARUNOX\Plugins\Cards\Entity::class ) show( $segments, $callable, $attribute, "STOP" ); if( $callable ) { $callable = array_pop( $segments ); } // if( $attribute && $chain == "target.name" ) show( $chain, $segments, $attribute ); if( $attribute ) { // show( $chain, $segments, $attribute, FALSE ); foreach( $segments as $segment ) { $type = gettype( $attribute ); $method = static::$COMMANDS[ $type ][ $segment ] ?? NULL; if( is_callable( $method ) ) { return $method( $attribute ); } if( is_array( $attribute ) ) { $attribute = (object)$attribute; } // if( static::class == \ARUNOX\Plugins\Cards\Entity::class ) show( $segment, $type, $method ); // show( $segment, $attribute, FALSE ); if( property_exists( $attribute, $segment ) ) { // if( $attribute && $chain == "target.name" ) show("X"); $attribute = $attribute->{$segment}; continue; } if( method_exists( $attribute, "__get" ) ) { // if( $attribute && $chain == "target.name" ) show( $chain, $attribute, $attribute->{$segment}, "STOP" ); if( $attribute->{$segment} !== NULL ) { $attribute = $attribute->{$segment}; } } } // if( $attribute && $chain == "target.name" ) show("STOP"); if( $callable ) { if( method_exists( $attribute, $callable ) ) { $attribute = $attribute->{$callable}(); } } return $attribute; } return NULL; } private function isAttributeChain( string $chain ) { return preg_match( "/(->|\.|\(\))/", $chain ); } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct() {} #[PHP\Attributes\Magic\Get( fallback: TRUE )] public function getFromHashID( $name ) : mixed { $name = "{$name}_hid"; if( isset( $this->{$name} ) ) { if( $this->{$name} === NULL ) return NULL; return static::callModelClass( "resolveHashIDPrefix", $this->{$name} ); } throw new PHP\Attributes\Magic\Get\Exceptions\FallbackFailed(); } #[PHP\Attributes\Magic\Set( fallback: TRUE )] public function setToAttributes( $name, $value ) : void { if( static::existsAttribute( $name ) ) { if( class_exists( static::getAttribute( $name ) ) ) { $value = new (static::getAttribute( $name ))( $value ); } } if( method_exists( $this, "set{$name}" ) ) { $this->{"set{$name}"}( $value ); } else $this->attributes[ $name ] = $value; } #[PHP\Attributes\Magic\Get( fallback: TRUE )] public function getFromAttributes( $name ) : mixed { if( $this->isAttributeChain( $name ) ) { return $this->loadAttributeChain( $name ); } if( static::existsAttribute( $name ) ) { $this->loadAttributeValue( $name ); } if( key_exists( $name, $this->attributes ) ) { return $this->attributes[ $name ]; } if( method_exists( $this, "get{$name}" ) ) { // if( $name == "plugins" ) show($name, $this->attributes, FALSE); return $this->attributes[ $name ] = $this->{"get{$name}"}(); } $alternate = str_replace( "_", "", $name ); if( method_exists( $this, "get{$alternate}" ) ) { return $this->attributes[ $name ] = $this->{"get{$alternate}"}(); } return NULL; } public function __isset( $name ) : bool { return key_exists( $name, $this->attributes ); } } /** Datei: library/ARUNOX/Database/BaseModel.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created * @since 1.1.0 Modified get() and delete() to accept string values */ namespace ARUNOX\Database; use ARUNOX\API; use ARUNOX\Database; use ARUNOX\Core\Hooks; abstract class BaseModel { use API\PHP\Attributes\Helper; use API\PHP\Attributes\Magic\Call\Helper; use API\PHP\Traits\Helpers\Controller; /* ### VARIABLES ################################################ # */ protected static ?string $PRIMARY_KEY = "id"; /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function get( int|string|array $value, string|array $columns = ["*"] ) : array|false|BaseEntity { if( is_numeric( $value ) ) $value = (int)$value; if( is_string( $value ) ) $value = static::resolveHashID( $value ); if( is_numeric( $value ) ) $value = [ static::$PRIMARY_KEY => (int)$value ]; if( $value === FALSE ) return FALSE; // if( static::getTable() == "projects_types" ) show( $value, is_numeric( $value ), $columns ); // show( static::class, static::getTable(), $columns ); $query = QueryBuilder::select( static::getTable(), $columns )->where( $value ); $query = Hooks::filterWithNames( "model::query", static::getTable(), $query, static::getTable() ); $query = Hooks::filterWithNames( "model::get->query", static::getTable(), $query, static::getTable() ); $result = $query->fetch( static::getEntityClass() ); $result = Hooks::filterWithNames( "model::fetched", static::getTable(), $result, static::getTable(), $query ); return $result; } private static function getRawData( array $where ) : array|false { return QueryBuilder::select( static::getTable() )->where( $where )->fetch( FALSE ); } public static function getAll( null|array|int|string $where = [], string|array $columns = ['*'] ) : array { if( $where === NULL ) $where = []; if( is_string( $where ) || is_numeric( $where ) ) { $where = [ static::$PRIMARY_KEY => $where ]; } $columns = (array)$columns; $columns = empty( $columns ) ? ["*"] : $columns; $columns = array_intersect( $columns, array_merge( static::getColumns(), ["*"] ) ); $columns[] = static::$PRIMARY_KEY; $query = QueryBuilder::select( static::getTable(), $columns, $where ); $query = Hooks::filterWithNames( "model::query", static::getTable(), $query, static::getTable() ); $query = Hooks::filterWithNames( "model::getall->query", static::getTable(), $query, static::getTable() ); $result = $query->fetchAll( static::getEntityClass() ); $result = Hooks::filterWithNames( "model::fetchedAll", static::getTable(), $result, static::getTable(), $query ); return $result; } public static function getCount( null|array|int|string $where = [], string|array $columns = ['*'] ) { return count( self::getAll( $where, $columns ) ); } public static function getValue( int|string|array $value, null|string $column = NULL ) : mixed { $column = $column ?? static::$PRIMARY_KEY; $result = static::get( $value, $column ); // if( static::getTable() == "agents" ) show( $result, $value, $column ); return $result?->{$column} ?? NULL; } public static function getAllValue( null|array|int|string $where = [], null|string $column = NULL ) : array { $column = $column ?? static::$PRIMARY_KEY; $where = $where ?? []; if( is_scalar( $where ) ) $where = [ static::$PRIMARY_KEY => $where ]; $query = QueryBuilder::select( static::getTable(), $column, $where ); $result = $query->fetchColumns(); return $result; } public static function getByColumn( string $column, int|string $value, string|array $columns = ["*"] ) : array|false|BaseEntity { return static::get([ $column => $value ]); } public static function getAllByColumn( string $column, $value, string|array $columns = ['*'] ) : array { return static::getAll([ $column => $value ], $columns ); } public static function getAsOptions( string|array $labelColumn = "name", null|string $valueColumn = NULL, array $excluded = [] ) : array { $entity = static::getEntityClass(); $status = $entity::getAttribute( "status" ); $where = $status ? [ "status" => $status::Active ] : []; return static::getAsOptionsArray( $where, $labelColumn, $valueColumn, $excluded ); } public static function getColumns() { return Database::getColumns( static::getTable() ); } public static function existsColumn( string $column ) { return Database::existsColumn( static::getTable(), $column ); } public static function getPrimaryKey() { return static::$PRIMARY_KEY; } public static function fileUplaodAll( array $data = [] ) : array { foreach( $_FILES as $column => $file ) { if( !$file["tmp_name"] ) continue; if( isset( $data[ $column ] ) ) { $current = STORAGE_DIR . $data[ $column ]; if( $current && API\Filesystem::existsFile( $current ) ) { API\Filesystem::deleteFile( $current ); } } $fileObject = API\Filesystem::upload( $file, UPLOADS_DIR . "/database/", guid() ); $data[ $column ] = $fileObject?->url ?? NULL; } return $data; } public static function save( array $data ) : mixed { $data = static::fileUplaodAll( $data ); $columns = static::getColumns(); $columns = array_flip( $columns ); $newData = array_intersect_key( $data, $columns ); $oldData = []; // if( static::getTable() == "pwa_images" ) show( "STOP", FALSE ); if( isset( $data[ static::$PRIMARY_KEY ] ) ) { $finalHook = "updated"; $queryHook = "update"; $where = [ static::$PRIMARY_KEY => $data[ static::$PRIMARY_KEY ] ]; $oldData = static::getRawData( $where ); $oldData = json_encode( $oldData ); $oldData = json_decode( $oldData, TRUE ); $oldData = array_intersect_key( $oldData, $newData ); // if( static::getTable() == "pwa_images" ) show( $oldData, FALSE ); $query = QueryBuilder::update( static::getTable(), $newData, $where ); } else { $finalHook = "created"; $queryHook = "insert"; $query = QueryBuilder::insert( static::getTable(), $newData ); // ->onDuplicateKeyUpdate([ static::$PRIMARY_KEY ]); } $query = Hooks::filterWithNames( "model::query", static::getTable(), $query, static::getTable() ); $query = Hooks::filterWithNames( "model::save->query", static::getTable(), $query, static::getTable() ); $query = Hooks::filterWithNames( "model::{$queryHook}->query", static::getTable(), $query, static::getTable() ); $result = $query->execute(); $changed = array_diff_assoc( $newData, $oldData ); // if( static::getTable() == "pwa_images" ) show( $query, FALSE ); if( $result === TRUE ) $result = $data[ static::$PRIMARY_KEY ]; $result = Hooks::filterWithNames( "model::{$finalHook}", static::getTable(), $result, static::getTable(), $query, $finalHook, $changed, static::class ); $result = Hooks::filterWithNames( "model::saved", static::getTable(), $result, static::getTable(), $query, $changed, static::class ); return $result; } public static function delete( int|string|array $value ) : int { if( is_string( $value ) ) $value = static::resolveHashID( $value ); if( $value === FALSE ) return FALSE; if( is_numeric( $value ) ) $value = [ static::$PRIMARY_KEY => (int)$value ]; // if( is_string( $value ) ) $value = [ "hashid" => $value ]; $query = QueryBuilder::delete( static::getTable(), $value ); $query = Hooks::filterWithNames( "model::query", static::getTable(), $query, static::getTable() ); $query = Hooks::filterWithNames( "model::delete->query", static::getTable(), $query, static::getTable() ); $result = $query->execute(); $result = Hooks::filterWithNames( "model::deleted", static::getTable(), $result, static::getTable(), $query ); return $result; } /* ### PROTECTED STATIC FUNCTIONS ############################### # */ protected static function getAsOptionsArray( string|array $where, string|array $labelColumn = "name", null|string $valueColumn = NULL, array $excluded = [], null|callable $walker = NULL ) : array { $labels = (array)$labelColumn; $options = []; $primary = $valueColumn ?? static::getPrimaryKey(); $results = static::getAll( $where ); foreach( $results as $item ) { if( in_array( $item->{$primary}, $excluded, ) ) continue; $optionLabel = []; foreach( $labels as $labelColumn ) { $optionLabel[] = $item->{$labelColumn}; } $option = [ "value" => $item->{$primary}, "label" => implode( " ", $optionLabel ), "icon" => $item->icon ?? $item->icon_name ?? NULL, "color" => $item->color ?? $item->icon_color ?? NULL, ]; if( is_callable( $walker ) ) { $option = call_user_func( $walker, $option, $item ); } $options[] = $option; } $options = array_column( $options, NULL, "value" ); $options = array_filter( $options, fn( $item ) => $item["label"] ); return $options; } } /** Datei: library/ARUNOX/Database/BaseEntityWithMeta.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Database; abstract class BaseEntityWithMeta extends BaseEntity { /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### PUBLIC META FUNCTIONS #################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ }