commit 3ab7279214190fa03f1f9988881b12d59d8a7080 Author: Llewellyn van der Merwe Date: Fri Jun 28 04:13:00 2024 +0200 First release diff --git a/README.md b/README.md new file mode 100644 index 0000000..04c4116 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# Vdm Data (1.0.0) + +VDM Data Library + +## Details + +- Packager: [OctoPower v2.0](https://git.vdm.dev/octoleo/octopower) +- Author: [Llewellyn van der Merwe](https://io.vdm.dev) +- Creation Date: June 2024 + +### Installation via Composer + +Setup this registry in your `~/.composer/config.json` file: +``` +{ + "repositories": [{ + "type": "composer", + "url": "https://git.vdm.dev/api/packages/joomla/composer" + } + ] +} +``` + +To install the package using Composer, run the following command: +``` +composer require vdm/data:1.0.0 +``` + +## Joomla Framework Dependencies + +>We have added the following framework classes to the required list of this Composer package. + +- Joomla/DI "^3.0" + - [VastDevelopmentMethod\Joomla\Data\Factory](src/VastDevelopmentMethod/Joomla/Data/Factory.php) + - [VastDevelopmentMethod\Joomla\Abstraction\Factory](src/VastDevelopmentMethod/Joomla/Abstraction/Factory.php) + - [VastDevelopmentMethod\Joomla\Interfaces\FactoryInterface](src/VastDevelopmentMethod/Joomla/Interfaces/FactoryInterface.php) + - [VastDevelopmentMethod\Joomla\Service\Table](src/VastDevelopmentMethod/Joomla/Service/Table.php) + - [VastDevelopmentMethod\Joomla\Componentbuilder\Table\Schema](src/VastDevelopmentMethod/Joomla/Componentbuilder/Table/Schema.php) + - [VastDevelopmentMethod\Joomla\Service\Database](src/VastDevelopmentMethod/Joomla/Service/Database.php) + - [VastDevelopmentMethod\Joomla\Database\Load](src/VastDevelopmentMethod/Joomla/Database/Load.php) + - [VastDevelopmentMethod\Joomla\Service\Model](src/VastDevelopmentMethod/Joomla/Service/Model.php) + - [VastDevelopmentMethod\Joomla\Model\Load](src/VastDevelopmentMethod/Joomla/Model/Load.php) + - [VastDevelopmentMethod\Joomla\Abstraction\Model](src/VastDevelopmentMethod/Joomla/Abstraction/Model.php) + - [VastDevelopmentMethod\Joomla\Service\Data](src/VastDevelopmentMethod/Joomla/Service/Data.php) + - [VastDevelopmentMethod\Joomla\Data\Action\Load](src/VastDevelopmentMethod/Joomla/Data/Action/Load.php) + - [VastDevelopmentMethod\Joomla\Interfaces\Data\LoadInterface](src/VastDevelopmentMethod/Joomla/Interfaces/Data/LoadInterface.php) +- Joomla/Filter "^3.0" + - [VastDevelopmentMethod\Joomla\Utilities\StringHelper](src/VastDevelopmentMethod/Joomla/Utilities/StringHelper.php) +- Joomla/Input "^3.0" + - [VastDevelopmentMethod\Joomla\Utilities\Component\Helper](src/VastDevelopmentMethod/Joomla/Utilities/Component/Helper.php) + - [VastDevelopmentMethod\Joomla\Utilities\String\NamespaceHelper](src/VastDevelopmentMethod/Joomla/Utilities/String/NamespaceHelper.php) +- Joomla/Registry "^3.0" + - [VastDevelopmentMethod\Joomla\Utilities\Component\Helper](src/VastDevelopmentMethod/Joomla/Utilities/Component/Helper.php) + - [VastDevelopmentMethod\Joomla\Utilities\String\NamespaceHelper](src/VastDevelopmentMethod/Joomla/Utilities/String/NamespaceHelper.php) + + +## Joomla CMS Dependencies + +- Joomla\CMS\Component\ComponentHelper + - [VastDevelopmentMethod\Joomla\Utilities\Component\Helper](src/VastDevelopmentMethod/Joomla/Utilities/Component/Helper.php) + - [VastDevelopmentMethod\Joomla\Utilities\String\NamespaceHelper](src/VastDevelopmentMethod/Joomla/Utilities/String/NamespaceHelper.php) +- Joomla\CMS\Date\Date + - [VastDevelopmentMethod\Joomla\Database\Insert](src/VastDevelopmentMethod/Joomla/Database/Insert.php) +- Joomla\CMS\Factory + - [VastDevelopmentMethod\Joomla\Abstraction\Schema](src/VastDevelopmentMethod/Joomla/Abstraction/Schema.php) + - [VastDevelopmentMethod\Joomla\Abstraction\Database](src/VastDevelopmentMethod/Joomla/Abstraction/Database.php) + - [VastDevelopmentMethod\Joomla\Utilities\Component\Helper](src/VastDevelopmentMethod/Joomla/Utilities/Component/Helper.php) + - [VastDevelopmentMethod\Joomla\Utilities\String\NamespaceHelper](src/VastDevelopmentMethod/Joomla/Utilities/String/NamespaceHelper.php) +- Joomla\CMS\Language\Language + - [VastDevelopmentMethod\Joomla\Utilities\StringHelper](src/VastDevelopmentMethod/Joomla/Utilities/StringHelper.php) +- Joomla\CMS\Version + - [VastDevelopmentMethod\Joomla\Abstraction\Schema](src/VastDevelopmentMethod/Joomla/Abstraction/Schema.php) + + +### License +> GNU General Public License version 2 or later + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..1e7d25f --- /dev/null +++ b/composer.json @@ -0,0 +1,31 @@ +{ + "name": "vdm/data", + "type": "library", + "description": "VDM Data Library", + "homepage": "https://git.vdm.dev/joomla/vdm-data", + "license": "GNU General Public License version 2 or later", + "require": { + "php": "^8.1.0", + "Joomla/DI": "^3.0", + "Joomla/Filter": "^3.0", + "Joomla/Input": "^3.0", + "Joomla/Registry": "^3.0" + }, + "autoload": { + "psr-4": { + "VastDevelopmentMethod\\Joomla\\": "src/VastDevelopmentMethod/Joomla" + } + }, + "keywords": [ + "VDM", + "Data", + "SuperPower" + ], + "authors": [ + { + "name": "Llewellyn van der Merwe", + "email": "joomla@vdm.io", + "homepage": "https://io.vdm.dev" + } + ] +} diff --git a/src/VastDevelopmentMethod/Joomla/Abstraction/Database.php b/src/VastDevelopmentMethod/Joomla/Abstraction/Database.php new file mode 100644 index 0000000..c71e1da --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Abstraction/Database.php @@ -0,0 +1,115 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Abstraction; + + +use Joomla\CMS\Factory as JoomlaFactory; +use VastDevelopmentMethod\Joomla\Utilities\Component\Helper; + + +/** + * Database + * + * @since 3.2.0 + */ +abstract class Database +{ + /** + * Database object to query local DB + * + * @since 3.2.0 + */ + protected $db; + + /** + * Core Component Table Name + * + * @var string + * @since 3.2.0 + */ + protected string $table; + + /** + * Constructor + * + * @throws \Exception + * @since 3.2.0 + */ + public function __construct() + { + $this->db = JoomlaFactory::getDbo(); + + // set the component table + $this->table = '#__' . Helper::getCode(); + } + + /** + * Set a value based on data type + * + * @param mixed $value The value to set + * + * @return mixed + * @since 3.2.0 + **/ + protected function quote($value) + { + if ($value === null) // hmm the null does pose an issue (will keep an eye on this) + { + return 'NULL'; + } + + if (is_numeric($value)) + { + if (filter_var($value, FILTER_VALIDATE_INT)) + { + return (int) $value; + } + elseif (filter_var($value, FILTER_VALIDATE_FLOAT)) + { + return (float) $value; + } + } + elseif (is_bool($value)) // not sure if this will work well (but its correct) + { + return $value ? 'TRUE' : 'FALSE'; + } + + // For date and datetime values + if ($value instanceof \DateTime) + { + return $this->db->quote($value->format('Y-m-d H:i:s')); + } + + // For other data types, just escape it + return $this->db->quote($value); + } + + /** + * Set a table name, adding the + * core component as needed + * + * @param string $table The table string + * + * @return string + * @since 3.2.0 + **/ + protected function getTable(string $table): string + { + if (strpos($table, '#__') === false) + { + return $this->table . '_' . $table; + } + + return $table; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Abstraction/Factory.php b/src/VastDevelopmentMethod/Joomla/Abstraction/Factory.php new file mode 100644 index 0000000..99bdbe3 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Abstraction/Factory.php @@ -0,0 +1,83 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Abstraction; + + +use Joomla\DI\Container; +use VastDevelopmentMethod\Joomla\Interfaces\FactoryInterface; + + +/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + ** + ** In realms of code where purists frown, the anti-pattern wears a crown, + ** A paradox of chaos bright, where complex paths lose all its slight. + ** For in its tangled, wild embrace, lies raw creativity's face, + ** No rigid forms, no strict decree, just boundless, daring artistry. + ** In flaws, we find the freedom's key, where messy code and brilliance spree, + ** A dance of thought, unchained, unbound, in anti-pattern, beauty's found. + ** + ** Perfect Paradox and True Nature of the Anti-Pattern by ChatGPT + ** + ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** + ** + ** @since 0.0.0 + **/ +abstract class Factory implements FactoryInterface +{ + /** + * Global Package Container + * + * @var Container|null + * @since 0.0.0 + **/ + protected static ?Container $container = null; + + /** + * Get any class from the package container + * + * @param string $key The container class key + * + * @return Mixed + * @since 0.0.0 + */ + public static function _($key) + { + return static::getContainer()->get($key); + } + + /** + * Get the global package container + * + * @return Container + * @since 0.0.0 + */ + public static function getContainer(): Container + { + if (!static::$container) + { + static::$container = static::createContainer(); + } + + return static::$container; + } + + /** + * Create a container object + * + * @return Container + * @since 0.0.0 + */ + abstract protected static function createContainer(): Container; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Abstraction/Model.php b/src/VastDevelopmentMethod/Joomla/Abstraction/Model.php new file mode 100644 index 0000000..4341c60 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Abstraction/Model.php @@ -0,0 +1,503 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Abstraction; + + +use VastDevelopmentMethod\Joomla\Utilities\StringHelper; +use VastDevelopmentMethod\Joomla\Utilities\ArrayHelper; +use VastDevelopmentMethod\Joomla\Interfaces\Tableinterface as Table; +use VastDevelopmentMethod\Joomla\Interfaces\ModelInterface; + + +/** + * Base Model + * + * @since 3.2.0 + */ +abstract class Model implements ModelInterface +{ + /** + * Last ID + * + * @var array + * @since 3.2.0 + */ + protected array $last; + + /** + * Search Table + * + * @var Table + * @since 3.2.0 + */ + protected Table $table; + + /** + * Table Name + * + * @var string + * @since 3.2.0 + */ + protected string $tableName; + + /** + * The switch to control the behaviour of empty values + * + * @var bool + * @since 3.2.2 + */ + protected bool $allowEmpty = true; + + /** + * Constructor + * + * @param Table $table The search table object. + * @param string|null $tableName The table + * @param bool|null $allowEmpty The switch to control the behaviour of empty values (default true) + * + * @since 3.2.0 + */ + public function __construct(Table $table, ?string $tableName = null, bool $allowEmpty = null) + { + $this->table = $table; + if ($tableName !== null) + { + $this->setTable($tableName); + } + if ($allowEmpty !== null) + { + $this->setAllowEmpty($allowEmpty); + } + } + + /** + * Set the current active table + * + * @param string $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(string $table): self + { + $this->setTable($table); + + return $this; + } + + /** + * Model the value + * Example: $this->value(value, 'value_key', 'table_name'); + * + * @param mixed $value The value to model + * @param string $field The field key + * @param string|null $table The table + * + * @return mixed + * @since 3.2.0 + */ + abstract public function value($value, string $field, ?string $table = null); + + /** + * Model a value of multiple items + * Example: $this->items(Array, 'value_key', 'table_name'); + * + * @param array|null $items The array of values + * @param string $field The field key + * @param string|null $table The table + * + * @return array|null + * @since 3.2.2 + */ + public function values(?array $items = null, string $field, ?string $table = null): ?array + { + // check if this is a valid table + if (ArrayHelper::check($items)) + { + // set the table name + if (empty($table)) + { + $table = $this->getTable(); + } + + // validate if field exist in table + if (!$this->table->exist($table, $field)) + { + return null; + } + + // value counter + $value_number = 0; + + // check if this is a valid table + $item_bucket = []; + + foreach ($items as $value) + { + if (!$this->validateBefore($value, $field, $table)) + { + continue; + } + + $value = $this->value($value, $field, $table); + + if (!$this->validateAfter($value, $field, $table)) + { + continue; + } + + $item_bucket[] = $value; + + $value_number++; + } + + // do we have any values left + if ($value_number > 0) + { + return $item_bucket; + } + } + + return null; + } + + /** + * Model the values of an item + * Example: $this->item(Object, 'table_name'); + * + * @param object|null $item The item object + * @param string|null $table The table + * + * @return object|null + * @since 3.2.0 + */ + public function item(?object $item, ?string $table = null): ?object + { + // we must have an object + if (empty($item)) + { + return null; + } + + // set the table name + if (empty($table)) + { + $table = $this->getTable(); + } + + if (($fields = $this->getTableFields($table, true)) !== null) + { + // field counter + $field_number = 0; + + // check if this is a valid table + $item_bucket = new \stdClass(); + + foreach ($fields as $field) + { + // model a value if it exists + if(isset($item->{$field})) + { + if (!$this->validateBefore($item->{$field}, $field, $table)) + { + continue; + } + + $item->{$field} = $this->value($item->{$field}, $field, $table); + + if (!$this->validateAfter($item->{$field}, $field, $table)) + { + continue; + } + + $item_bucket->{$field} = $item->{$field}; + + $field_number++; + } + } + + // all items must have more than one field or its empty (1 = key) + if ($field_number > 1) + { + return $item_bucket; + } + } + + return null; + } + + /** + * Model the values of multiple items + * Example: $this->items(Array, 'table_name'); + * + * @param array|null $items The array of item objects + * @param string|null $table The table + * + * @return array|null + * @since 3.2.0 + */ + public function items(?array $items = null, ?string $table = null): ?array + { + // check if this is a valid table + if (ArrayHelper::check($items)) + { + // set the table name + if (empty($table)) + { + $table = $this->getTable(); + } + + foreach ($items as $id => &$item) + { + // model the item + if (($item = $this->item($item, $table)) !== null) + { + // add the last ID + $this->last[$table] = $item->id ?? $this->last[$table] ?? null; + } + else + { + unset($items[$id]); + } + } + + if (ArrayHelper::check($items)) + { + return $items; + } + } + + return null; + } + + /** + * Model the values of an row + * Example: $this->item(Array, 'table_name'); + * + * @param array|null $item The item array + * @param string|null $table The table + * + * @return array|null + * @since 3.2.0 + */ + public function row(?array $item, ?string $table = null): ?array + { + // we must have an array + if (empty($item)) + { + return null; + } + + // set the table name + if (empty($table)) + { + $table = $this->getTable(); + } + + if (($fields = $this->getTableFields($table, true)) !== null) + { + // field counter + $field_number = 0; + + // check if this is a valid table + $item_bucket = []; + + foreach ($fields as $field) + { + // model a value if it exists + if(isset($item[$field])) + { + if (!$this->validateBefore($item[$field], $field, $table)) + { + continue; + } + + $item[$field] = $this->value($item[$field], $field, $table); + + if (!$this->validateAfter($item[$field], $field, $table)) + { + continue; + } + + $item_bucket[$field] = $item[$field]; + + $field_number++; + } + } + + // all items must have more than one field or its empty (1 = id or guid) + if ($field_number > 1) + { + return $item_bucket; + } + } + + return null; + } + + /** + * Model the values of multiple rows + * Example: $this->items(Array, 'table_name'); + * + * @param array|null $items The array of item array + * @param string|null $table The table + * + * @return array|null + * @since 3.2.0 + */ + public function rows(?array $items = null, ?string $table = null): ?array + { + // check if this is a valid table + if (ArrayHelper::check($items)) + { + // set the table name + if (empty($table)) + { + $table = $this->getTable(); + } + + foreach ($items as $id => &$item) + { + // model the item + if (($item = $this->row($item, $table)) !== null) + { + // add the last ID + $this->last[$table] = $item['id'] ?? $this->last[$table] ?? null; + } + else + { + unset($items[$id]); + } + } + + if (ArrayHelper::check($items)) + { + return $items; + } + } + + return null; + } + + /** + * Get last modeled ID + * Example: $this->last('table_name'); + * + * @param string|null $table The table + * + * @return int|null + * @since 3.2.0 + */ + public function last(?string $table = null): ?int + { + // set the table name + if (empty($table)) + { + $table = $this->getTable(); + } + + // check if this is a valid table + if ($table && isset($this->last[$table])) + { + return $this->last[$table]; + } + + return null; + } + + /** + * Set the current active table + * + * @param string $tableName The table name + * + * @return void + * @since 3.2.2 + */ + public function setTable(string $tableName): void + { + $this->tableName = $tableName; + } + + /** + * Set the switch to control the behaviour of empty values + * + * @param bool $allowEmpty The switch + * + * @return void + * @since 3.2.2 + */ + public function setAllowEmpty(bool $allowEmpty): void + { + $this->allowEmpty = $allowEmpty; + } + + /** + * Get the current active table + * + * @return string + * @since 3.2.0 + */ + protected function getTable(): string + { + return $this->tableName; + } + + /** + * Get the switch to control the behaviour of empty values + * + * @return bool + * @since 3.2.2 + */ + protected function getAllowEmpty(): bool + { + return $this->allowEmpty; + } + + /** + * Get the current active table's fields (including defaults) + * + * @param string $table The area + * @param bool $default Add the default fields + * + * @return array + * @since 3.2.0 + */ + protected function getTableFields(string $table, bool $default = false): ?array + { + return $this->table->fields($table, $default); + } + + /** + * Validate before the value is modelled (basic, override in child class) + * + * @param mixed $value The field value + * @param string|null $field The field key + * @param string|null $table The table + * + * @return bool + * @since 3.2.0 + */ + abstract protected function validateBefore(&$value, ?string $field = null, ?string $table = null): bool; + + /** + * Validate after the value is modelled (basic, override in child class) + * + * @param mixed $value The field value + * @param string|null $field The field key + * @param string|null $table The table + * + * @return bool + * @since 3.2.0 + */ + abstract protected function validateAfter(&$value, ?string $field = null, ?string $table = null): bool; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Abstraction/Schema.php b/src/VastDevelopmentMethod/Joomla/Abstraction/Schema.php new file mode 100644 index 0000000..2634aa0 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Abstraction/Schema.php @@ -0,0 +1,821 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Abstraction; + + +use Joomla\CMS\Factory; +use Joomla\CMS\Version; +use VastDevelopmentMethod\Joomla\Interfaces\Tableinterface as Table; +use VastDevelopmentMethod\Joomla\Interfaces\SchemaInterface; + + +/** + * Schema Checking + * + * @since 3.2.1 + */ +abstract class Schema implements SchemaInterface +{ + /** + * The Table Class. + * + * @var Table + * @since 3.2.1 + */ + protected Table $table; + + /** + * The Database Class + * + * @since 3.2.1 + */ + protected $db; + + /** + * The local tables + * + * @var array + * @since 3.2.1 + */ + private array $tables; + + /** + * The component table prefix + * + * @var string + * @since 3.2.1 + */ + private string $prefix; + + /** + * The field unique keys + * + * @var array + * @since 3.2.1 + */ + private array $uniqueKeys; + + /** + * The field keys + * + * @var array + * @since 3.2.1 + */ + private array $keys; + + /** + * The current table columns + * + * @var array + * @since 3.2.1 + */ + private array $columns; + + /** + * The success messages of the action + * + * @var array + * @since 3.2.1 + */ + private array $success; + + /** + * Current Joomla Version We are IN + * + * @var int + * @since 3.2.1 + **/ + protected $currentVersion; + + /** + * Constructor. + * + * @param Table $table The Table Class. + * + * @since 3.2.1 + * @throws \Exception If the database fails + */ + public function __construct(Table $table) + { + $this->table = $table; + + try { + // set the database object + $this->db = Factory::getDbo(); + + // get current component tables + $this->tables = $this->db->getTableList(); + + // set the component table + $this->prefix = $this->db->getPrefix() . $this->getCode(); + + // set the current version + $this->currentVersion = Version::MAJOR_VERSION; + } catch (\Exception $e) { + throw new \Exception("Error: failed to initialize schema class due to a database error."); + } + } + + /** + * Check and update database schema for missing fields or tables. + * + * @return array The array of successful updates/actions, if empty no update/action was taken. + * @since 3.2.1 + * @throws \Exception If there is an error during the update process. + */ + public function update(): array + { + try { + $this->success = [ + "Success: scan of the component tables started." + ]; + foreach ($this->table->tables() as $table) + { + $this->uniqueKeys = []; + $this->keys = []; + + if ($this->tableExists($table)) + { + $this->updateSchema($table); + } + else + { + $this->createTable($table); + } + } + } catch (\Exception $e) { + throw new \Exception("Error: updating database schema. " . $e->getMessage()); + } + + if (count($this->success) == 1) + { + $this->success[] = "Success: scan of the component tables completed with no update needed."; + } + else + { + $this->success[] = "Success: scan of the component tables completed."; + } + + return $this->success; + } + + /** + * Get the targeted component code + * + * @return string + * @since 3.2.1 + */ + abstract protected function getCode(): string; + + /** + * Check if a table exists in the database. + * + * @param string $table The name of the table to check. + * + * @return bool True if table exists, False otherwise. + * @since 3.2.1 + */ + protected function tableExists(string $table): bool + { + return in_array($this->getTable($table), $this->tables); + } + + /** + * Update the schema of an existing table. + * + * @param string $table The table to update. + * + * @return void + * @since 3.2.1 + * @throws \Exception If there is an error while updating the schema. + */ + public function updateSchema(string $table): void + { + try { + $existingColumns = $this->getExistingColumns($table); + $expectedColumns = $this->table->fields($table, true); + + $missingColumns = array_diff($expectedColumns, $existingColumns); + + if (!empty($missingColumns)) + { + $this->addMissingColumns($table, $missingColumns); + } + + $this->checkColumnsDataType($table, $expectedColumns); + + } catch (\Exception $e) { + throw new \Exception("Error: updating schema for $table table. " . $e->getMessage()); + } + + if (!empty($missingColumns)) + { + $column_s = (count($missingColumns) == 1) ? 'column' : 'columns'; + $missingColumns = implode(', ', $missingColumns); + $this->success[] = "Success: added missing ($missingColumns) $column_s to $table table."; + } + } + + /** + * Create a table with all necessary fields. + * + * @param string $table The name of the table to create. + * + * @return void + * @since 3.2.1 + * @throws \Exception If there is an error creating the table. + */ + public function createTable(string $table): void + { + try { + $columns = []; + $fields = $this->table->fields($table, true); + $createTable = 'CREATE TABLE IF NOT EXISTS ' . $this->db->quoteName($this->getTable($table)); + + foreach ($fields as $field) + { + if (($def = $this->getColumnDefinition($table, $field)) !== null) + { + $columns[] = $def; + } + } + + $columnDefinitions = implode(', ', $columns); + + $keys = $this->getTableKeys(); + + $createTableSql = "$createTable ($columnDefinitions, $keys)"; + + $this->db->setQuery($createTableSql); + $this->db->execute(); + } catch (\Exception $e) { + throw new \Exception("Error: failed to create missing $table table. " . $e->getMessage()); + } + + $this->success[] = "Success: created missing $table table."; + } + + /** + * Fetch existing columns from a database table. + * + * @param string $table The name of the table. + * + * @return array An array of column names. + * @since 3.2.1 + */ + protected function getExistingColumns(string $table): array + { + $this->columns = $this->db->getTableColumns($this->getTable($table), false); + + return array_keys($this->columns); + } + + /** + * Add missing columns to a table. + * + * @param string $table The table to update. + * @param array $columns List of missing columns/fields. + * + * @return void + * @since 3.2.1 + * @throws \Exception If there is an error adding columns. + */ + protected function addMissingColumns(string $table, array $columns): void + { + try { + $query = $this->db->getQuery(true); + $alterTable = 'ALTER TABLE ' . $this->db->quoteName($this->getTable($table)) . ' '; + + // Start an ALTER TABLE query + $alterQueries = []; + foreach ($columns as $column) + { + if (($def = $this->getColumnDefinition($table, $column)) !== null) + { + $alterQueries[] = " ADD " . $def; + } + } + + $this->db->setQuery($alterTable . implode(', ', $alterQueries)); + $this->db->execute(); + } catch (\Exception $e) { + $column_s = (count($columns) == 1) ? 'column' : 'columns'; + $columns = implode(', ', $columns); + throw new \Exception("Error: failed to add ($columns) $column_s to $table table. " . $e->getMessage()); + } + } + + /** + * Validate and update the data type of existing fields/columns + * + * @param string $table The table to update. + * @param array $columns List of columns/fields to check. + * + * @return void + * @since 3.2.1 + */ + protected function checkColumnsDataType(string $table, array $columns): void + { + $requireUpdate = []; + foreach ($columns as $column) + { + $current = $this->columns[$column] ?? null; + if ($current === null || ($expected = $this->table->get($table, $column, 'db')) === null) + { + continue; + } + + // check if the data type and size match + if ($this->isDataTypeChangeSignificant($current->Type, $expected['type'])) + { + $requireUpdate[$column] = [ + 'column' => $column, + 'current' => $current->Type, + 'expected' => $expected['type'] + ]; + } + // check if update of default values is needed + elseif ($this->checkDefault($table, $column)) + { + $requireUpdate[$column] = [ + 'column' => $column, + 'current' => $current->Type, + 'expected' => $expected['type'] + ]; + } + // check if update of null is needed + elseif ($this->checkNull($table, $column)) + { + $requireUpdate[$column] = [ + 'column' => $column, + 'current' => $current->Type, + 'expected' => $expected['type'] + ]; + } + } + + if (!empty($requireUpdate)) + { + $this->updateColumnsDataType($table, $requireUpdate); + } + } + + /** + * Generates a SQL snippet for defining a table column, incorporating column type, + * default value, nullability, and auto-increment properties. + * + * @param string $table The table name to be used. + * @param string $field The field name in the table to generate SQL for. + * + * @return string|null The SQL snippet for the column definition. + * @since 3.2.1 + * @throws \Exception If the schema details cannot be retrieved or the SQL statement cannot be constructed properly. + */ + protected function getColumnDefinition(string $table, string $field): ?string + { + try { + // Retrieve the database schema details for the specified table and field + if (($db = $this->table->get($table, $field, 'db')) === null) + { + return null; + } + + // Prepare the column name + $column_name = $this->db->quoteName($field); + $db['name'] = $field; + + // Prepare the type and default value SQL statement + $type = $db['type'] ?? 'TEXT'; + $db_default = isset($db['default']) ? $db['default'] : null; + $default = $this->getDefaultValue($type, $db_default); + + // Prepare the null switch, and auto increment statement + $null_switch = !empty($db['null_switch']) ? ' ' . $db['null_switch'] : ''; + + // Prepare the auto increment statement + $auto_increment = !empty($db['auto_increment']) ? " AUTO_INCREMENT" : ''; + + // If there's a default value, the column should not be nullable + if ($default !== '') + { + $null_switch = ''; + } + + $this->setKeys($db); + + // Assemble the SQL snippet for the column definition + return "{$column_name} {$type}{$null_switch}{$auto_increment}{$default}"; + } catch (\Exception $e) { + throw new \Exception("Error: failed to generate column definition for ($table.$field). " . $e->getMessage()); + } + } + + /** + * Check and Update the default values if needed, including existing data adjustments + * + * @param string $table The table to update. + * @param string $column The column/field to check. + * + * @return bool + * @since 3.2.1 + */ + protected function checkDefault(string $table, string $column): bool + { + // Retrieve the expected column configuration + $expected = $this->table->get($table, $column, 'db'); + + // Skip updates if the column is auto_increment + if (isset($expected['auto_increment']) && $expected['auto_increment'] === true) + { + return false; + } + + // Retrieve the current column configuration + $current = $this->columns[$column]; + + // Determine the new default value based on the expected settings + $type = $expected['type'] ?? 'TEXT'; + $db_default = isset($expected['default']) ? $expected['default'] : null; + $newDefault = $this->getDefaultValue($type, $db_default, true); + + // First, adjust existing rows to conform to the new default if necessary + if (is_numeric($newDefault) && $this->adjustExistingDefaults($table, $column, $current->Default, $newDefault)) + { + $this->success[] = "Success: updated the ($column) defaults in $table table."; + + return true; + } + + if (is_string($expected['default']) && strtoupper($expected['default']) === 'EMPTY' && + is_string($current->Default) && strpos($current->Default, 'EMPTY') !== false) + { + return true; // little fix + } + + return false; + } + + /** + * Check and Update the null value if needed, including existing data adjustments + * + * @param string $table The table to update. + * @param string $column The column/field to check. + * + * @return bool + * @since 3.2.2 + */ + protected function checkNull(string $table, string $column): bool + { + // Retrieve the expected column configuration + $expected = $this->table->get($table, $column, 'db'); + + // Skip updates if the null_switch is not set + if (!isset($expected['null_switch'])) + { + return false; + } + + // Retrieve the current column configuration + $current = $this->columns[$column]; + + // Skip updates if the Null is not set + if (!isset($current->Null)) + { + return false; + } + + // set the expected NULL + $expected_null = 'NO'; + if ($expected['null_switch'] === "NULL") + { + $expected_null = 'YES'; + } + + // set the current NULL + $current_null = $current->Null; + + // Prepare the type and default value SQL statement + $type = $expected['type'] ?? 'TEXT'; + $db_default = isset($expected['default']) ? $expected['default'] : null; + $default = $this->getDefaultValue($type, $db_default, true); + + // check the null values if they match + if ($current_null !== $expected_null && $current_null === 'NO' && empty($default)) + { + $this->success[] = "Success: updated the ($column) null state in $table table."; + + return true; + } + + return false; + } + + /** + * Update the data type of the given fields. + * + * @param string $table The table to update. + * @param array $columns List of columns/fields that must be updated. + * + * @return void + * @since 3.2.1 + */ + protected function updateColumnsDataType(string $table, array $columns): void + { + $alterTable = 'ALTER TABLE ' . $this->db->quoteName($this->getTable($table)); + foreach ($columns as $column => $types) + { + if (($def = $this->getColumnDefinition($table, $column)) === null) + { + continue; + } + + $dbField = $this->db->quoteName($column); + $alterQuery = "$alterTable CHANGE $dbField ". $def; + + if ($this->updateColumnDataType($alterQuery, $table, $column)) + { + $current = $types['current'] ?? 'error'; + $expected = $types['expected'] ?? 'error'; + $this->success[] = "Success: updated ($column) column datatype $current to $expected in $table table."; + } + } + } + + /** + * Add the component name to get the full table name. + * + * @param string $table The table name. + * + * @return void + * @since 3.2.1 + */ + protected function getTable(string $table): string + { + return $this->prefix . '_' . $table; + } + + /** + * Determines if the change in data type between two definitions is significant. + * + * This function checks if there's a significant difference between the current + * data type and the expected data type that would require updating the database schema. + * It ignores display width for numeric types where MySQL considers these attributes + * irrelevant for storage but considers size and other modifiers for types like VARCHAR. + * + * @param string $currentType The current data type from the database schema. + * @param string $expectedType The expected data type to validate against. + * + * @return bool Returns true if the data type change is significant, otherwise false. + * @since 3.2.1 + */ + protected function isDataTypeChangeSignificant(string $currentType, string $expectedType): bool + { + // Normalize both input types to lowercase for case-insensitive comparison + $currentType = strtolower($currentType); + $expectedType = strtolower($expectedType); + + // Regex to extract the base data type and numeric parameters with named groups + $typePattern = '/^(?\w+)(\((?\d+(,\d+)?)\))?/'; + + // Match types and parameters + preg_match($typePattern, $currentType, $currentMatches); + preg_match($typePattern, $expectedType, $expectedMatches); + + // Compare base types + if ($currentMatches['datatype'] !== $expectedMatches['datatype']) + { + return true; // Base types differ + } + + // Define types where size and other modifiers are irrelevant + $sizeIrrelevantTypes = [ + 'int', 'tinyint', 'smallint', 'mediumint', 'bigint', + 'float', 'double', 'decimal', 'numeric' // Numeric types where display width is irrelevant + ]; + + // If the type is not in the size irrelevant list, compare full definitions + if (!in_array($currentMatches['datatype'], $sizeIrrelevantTypes)) + { + return $currentType !== $expectedType; // Use full definition for types where size matters + } + + // For size irrelevant types, only compare base type, ignoring size and unsigned + $currentBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $currentType); + $expectedBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $expectedType); + + // Perform a final comparison for numeric types ignoring size + return $currentBaseType !== $expectedBaseType; + } + + /** + * Updates existing rows in a column to a new default value + * + * @param string $table The table to update. + * @param string $column The column to update. + * @param mixed $currentDefault Current default value. + * @param mixed $newDefault The new default value to be set. + * + * @return void + * @since 3.2.1 + * @throws \Exception If there is an error updating column defaults. + */ + protected function adjustExistingDefaults(string $table, string $column, $currentDefault, $newDefault): bool + { + // Determine if adjustment is needed based on new and current defaults + if ($newDefault !== $currentDefault) + { + try { + // Format the new default for SQL use + $sqlDefault = $this->db->quote($newDefault); + + $updateTable = 'UPDATE ' . $this->db->quoteName($this->getTable($table)); + $dbField = $this->db->quoteName($column); + + // Update SQL to set new default on existing rows where the default is currently the old default + $sql = $updateTable . " SET $dbField = $sqlDefault WHERE $dbField IS NULL OR $dbField = ''"; + + // Execute the update + $this->db->setQuery($sql); + return $this->db->execute(); + } catch (\Exception $e) { + throw new \Exception("Error: failed to update ($column) column defaults in $table table. " . $e->getMessage()); + } + } + return false; + } + + /** + * Update the data type of the given field. + * + * @param string $updateString The SQL command to update the column data type + * @param string $table The table to update. + * @param string $field Column/field that must be updated. + * + * @return bool true on succes + * @since 3.2.1 + * @throws \Exception If there is an error adding columns. + */ + protected function updateColumnDataType(string $updateString, string $table, string $field): bool + { + try { + $this->db->setQuery($updateString); + return $this->db->execute(); + } catch (\Exception $e) { + throw new \Exception("Error: failed to update the datatype of ($field) column in $table table. " . $e->getMessage()); + } + } + + /** + * Key all needed keys for this table + * + * @return string of keys + * @since 3.2.1 + */ + protected function getTableKeys(): string + { + $keys = []; + $keys[] = 'PRIMARY KEY (`id`)'; // TODO (we may want this to be dynamicly set) + + if (!empty($this->uniqueKeys)) + { + $keys[] = implode(', ', $this->uniqueKeys); + } + + if (!empty($this->keys)) + { + $keys[] = implode(', ', $this->keys); + } + + return implode(', ', $keys); + } + + /** + * Function to set the view keys + * + * @param string $column The field column database array values + * + * @return void + * @since 3.2.1 + */ + protected function setKeys(array $column): void + { + $this->setUniqueKey($column); + $this->setKey($column); + } + + /** + * Function to set the unique key + * + * @param string $column The field column database array values + * + * @return void + * @since 3.2.1 + */ + protected function setUniqueKey(array $column): void + { + if (isset($column['unique_key']) && $column['unique_key']) + { + $key = $column['unique_key_name'] ?? $column['name']; + $this->uniqueKeys[] = "UNIQUE KEY `idx_" . $key . "` (`" . $column['name'] . "`)"; + } + } + + /** + * Function to set the key + * + * @param string $column The field column database array values + * + * @return void + * @since 3.2.1 + */ + protected function setKey(array $column): void + { + if (isset($column['key']) && $column['key']) + { + $key = $column['key_name'] ?? $column['name']; + $this->keys[] = "KEY `idx_" . $key . "` (`" . $column['name'] . "`)"; + } + } + + /** + * Adjusts the default value SQL fragment for a database field based on its type and specific rules. + * + * If the field is of type DATETIME and the Joomla version is not 3, it sets the default to CURRENT_TIMESTAMP + * if not explicitly specified otherwise. For all other types it handles defaults by either leaving them unset or applying + * the provided default, properly quoted for SQL safety. When a 'EMPTY' default is specified, it returns no default at all. (:) + * + * @param string $type The type of the database field (e.g., 'DATETIME'). + * @param string|null $defaultValue Optional default value for the field, null if not provided. + * @param bool $pure Optional to add the 'DEFAULT' string or not. + * + * @return string The SQL fragment to set the default value for a field. + * @since 3.2.1 + */ + protected function getDefaultValue(string $type, ?string $defaultValue, bool $pure = false): string + { + if ($defaultValue === null || strtoupper($defaultValue) === 'EMPTY') + { + return ''; + } + + // Set default for DATETIME fields in Joomla versions above 3 + if (strtoupper($type) === 'DATETIME' && $this->currentVersion != 3) + { + return $pure ? "CURRENT_TIMESTAMP" : " DEFAULT CURRENT_TIMESTAMP"; + } + + // Apply and quote the default value + $sql_default = $this->quote($defaultValue); + return $pure ? $defaultValue : " DEFAULT $sql_default"; + } + + /** + * Set a value based on data type + * + * @param mixed $value The value to set + * + * @return mixed + * @since 3.2.0 + **/ + protected function quote($value) + { + if ($value === null) // hmm the null does pose an issue (will keep an eye on this) + { + return 'NULL'; + } + + if (is_numeric($value)) + { + if (filter_var($value, FILTER_VALIDATE_INT)) + { + return (int) $value; + } + elseif (filter_var($value, FILTER_VALIDATE_FLOAT)) + { + return (float) $value; + } + } + elseif (is_bool($value)) // not sure if this will work well (but its correct) + { + return $value ? 'TRUE' : 'FALSE'; + } + // For date and datetime values + elseif ($value instanceof \DateTime) + { + return $this->db->quote($value->format('Y-m-d H:i:s')); + } + + // For other data types, just escape it + return $this->db->quote($value); + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Componentbuilder/Table/Schema.php b/src/VastDevelopmentMethod/Joomla/Componentbuilder/Table/Schema.php new file mode 100644 index 0000000..b74edbb --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Componentbuilder/Table/Schema.php @@ -0,0 +1,52 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Componentbuilder\Table; + + +use VastDevelopmentMethod\Joomla\Componentbuilder\Table; +use VastDevelopmentMethod\Joomla\Interfaces\SchemaInterface; +use VastDevelopmentMethod\Joomla\Abstraction\Schema as ExtendingSchema; + + +/** + * Componentbuilder Tables Schema + * + * @since 3.2.1 + */ +final class Schema extends ExtendingSchema implements SchemaInterface +{ + /** + * Constructor. + * + * @param Table $table The Table Class. + * + * @since 3.2.1 + */ + public function __construct(?Table $table = null) + { + $table ??= new Table; + + parent::__construct($table); + } + + /** + * Get the targeted component code + * + * @return string + * @since 3.2.1 + */ + protected function getCode(): string + { + return 'componentbuilder'; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Data/Action/Delete.php b/src/VastDevelopmentMethod/Joomla/Data/Action/Delete.php new file mode 100644 index 0000000..b5b5175 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Data/Action/Delete.php @@ -0,0 +1,112 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Data\Action; + + +use VastDevelopmentMethod\Joomla\Interfaces\DeleteInterface as Database; +use VastDevelopmentMethod\Joomla\Interfaces\Data\DeleteInterface; + + +/** + * Data Delete + * + * @since 3.2.2 + */ +class Delete implements DeleteInterface +{ + /** + * The Delete Class. + * + * @var Database + * @since 3.2.2 + */ + protected Database $database; + + /** + * Table Name + * + * @var string + * @since 3.2.2 + */ + protected string $table; + + /** + * Constructor. + * + * @param Database $database The Delete Class. + * @param string|null $table The table name. + * + * @since 3.2.2 + */ + public function __construct(Database $database, ?string $table = null) + { + $this->database = $database; + if ($table !== null) + { + $this->table = $table; + } + } + + /** + * Set the current active table + * + * @param string|null $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(?string $table): self + { + if ($table !== null) + { + $this->table = $table; + } + + return $this; + } + + /** + * Delete all items in the database that match these conditions + * + * @param array $conditions Conditions by which to delete the data in database [array of arrays (key => value)] + * + * @return bool + * @since 3.2.2 + **/ + public function items(array $conditions): bool + { + return $this->database->items($conditions, $this->getTable()); + } + + /** + * Truncate a table + * + * @return void + * @since 3.2.2 + **/ + public function truncate(): void + { + $this->database->truncate($this->getTable()); + } + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string + { + return $this->table; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Data/Action/Insert.php b/src/VastDevelopmentMethod/Joomla/Data/Action/Insert.php new file mode 100644 index 0000000..330e93b --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Data/Action/Insert.php @@ -0,0 +1,202 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Data\Action; + + +use VastDevelopmentMethod\Joomla\Interfaces\ModelInterface as Model; +use VastDevelopmentMethod\Joomla\Interfaces\InsertInterface as Database; +use VastDevelopmentMethod\Joomla\Interfaces\Data\InsertInterface; + + +/** + * Data Insert (GUID) + * + * @since 3.2.2 + */ +class Insert implements InsertInterface +{ + /** + * Model + * + * @var Model + * @since 3.2.0 + */ + protected Model $model; + + /** + * Database + * + * @var Database + * @since 3.2.0 + */ + protected Database $database; + + /** + * Table Name + * + * @var string + * @since 3.2.1 + */ + protected string $table; + + /** + * Constructor + * + * @param Model $model The set model object. + * @param Database $database The insert database object. + * @param string|null $table The table name. + * + * @since 3.2.2 + */ + public function __construct(Model $model, Database $database, ?string $table = null) + { + $this->model = $model; + $this->database = $database; + if ($table !== null) + { + $this->table = $table; + } + } + + /** + * Set the current active table + * + * @param string|null $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(?string $table): self + { + if ($table !== null) + { + $this->table = $table; + } + + return $this; + } + + /** + * Insert a value to a given table + * Example: $this->value(Value, 'value_key', 'GUID'); + * + * @param mixed $value The field value + * @param string $field The field key + * @param string $keyValue The key value + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function value($value, string $field, string $keyValue, string $key = 'guid'): bool + { + // build the array + $item = []; + $item[$key] = $keyValue; + $item[$field] = $value; + + // Insert the column of this table + return $this->row($item); + } + + /** + * Insert single row with multiple values to a given table + * Example: $this->item(Array); + * + * @param array $item The item to save + * + * @return bool + * @since 3.2.0 + */ + public function row(array $item): bool + { + // check if object could be modelled + if (($item = $this->model->row($item, $this->getTable())) !== null) + { + // Insert the column of this table + return $this->database->row($item, $this->getTable()); + } + return false; + } + + /** + * Insert multiple rows to a given table + * Example: $this->items(Array); + * + * @param array|null $items The items updated in database (array of arrays) + * + * @return bool + * @since 3.2.0 + */ + public function rows(?array $items): bool + { + // check if object could be modelled + if (($items = $this->model->rows($items, $this->getTable())) !== null) + { + // Insert the column of this table + return $this->database->rows($items, $this->getTable()); + } + return false; + } + + /** + * Insert single item with multiple values to a given table + * Example: $this->item(Object); + * + * @param object $item The item to save + * + * @return bool + * @since 3.2.0 + */ + public function item(object $item): bool + { + // check if object could be modelled + if (($item = $this->model->item($item, $this->getTable())) !== null) + { + // Insert the column of this table + return $this->database->item($item, $this->getTable()); + } + return false; + } + + /** + * Insert multiple items to a given table + * Example: $this->items(Array); + * + * @param array|null $items The items updated in database (array of objects) + * + * @return bool + * @since 3.2.0 + */ + public function items(?array $items): bool + { + // check if object could be modelled + if (($items = $this->model->items($items, $this->getTable())) !== null) + { + // Update the column of this table using guid as the primary key. + return $this->database->items($items, $this->getTable()); + } + return false; + } + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string + { + return $this->table; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Data/Action/Load.php b/src/VastDevelopmentMethod/Joomla/Data/Action/Load.php new file mode 100644 index 0000000..2f0bd9e --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Data/Action/Load.php @@ -0,0 +1,224 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Data\Action; + + +use VastDevelopmentMethod\Joomla\Interfaces\ModelInterface as Model; +use VastDevelopmentMethod\Joomla\Interfaces\LoadInterface as Database; +use VastDevelopmentMethod\Joomla\Interfaces\Data\LoadInterface; + + +/** + * Data Load (GUID) + * + * @since 3.2.2 + */ +class Load implements LoadInterface +{ + /** + * Model Load + * + * @var Model + * @since 2.0.1 + */ + protected Model $model; + + /** + * Database Load + * + * @var Database + * @since 2.0.1 + */ + protected Database $load; + + /** + * Table Name + * + * @var string + * @since 3.2.1 + */ + protected string $table; + + /** + * Constructor + * + * @param Model $model The model object. + * @param Database $load The database object. + * @param string|null $table The table name. + * + * @since 2.0.1 + */ + public function __construct(Model $model, Database $load, ?string $table = null) + { + $this->model = $model; + $this->load = $load; + if ($table !== null) + { + $this->table = $table; + } + } + + /** + * Set the current active table + * + * @param string|null $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(?string $table): self + { + if ($table !== null) + { + $this->table = $table; + } + + return $this; + } + + /** + * Get a value from a given table + * Example: $this->value( + * [ + * 'guid' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + * ], 'value_key' + * ); + * + * @param array $keys The item keys + * @param string $field The field key + * + * @return mixed + * @since 2.0.1 + */ + public function value(array $keys, string $field) + { + return $this->model->value( + $this->load->value( + ["a.{$field}" => $field], + ['a' => $this->getTable()], + $this->prefix($keys) + ), + $field, + $this->getTable() + ); + } + + /** + * Get a value from multiple rows from a given table + * Example: $this->values( + * [ + * 'guid' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + * ], 'value_key' + * ); + * + * @param array $keys The item keys + * @param string $field The field key + * + * @return array|null + * @since 3.2.2 + */ + public function values(array $keys, string $field): ?array + { + return $this->model->values( + $this->load->values( + ["a.{$field}" => $field], + ['a' => $this->getTable()], + $this->prefix($keys) + ), + $field, + $this->getTable() + ); + } + + /** + * Get values from a given table + * Example: $this->item( + * [ + * 'guid' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + * ] + * ); + * + * @param array $keys The item keys + * + * @return object|null + * @since 2.0.1 + */ + public function item(array $keys): ?object + { + return $this->model->item( + $this->load->item( + ['all' => 'a.*'], + ['a' => $this->getTable()], + $this->prefix($keys) + ), + $this->getTable() + ); + } + + /** + * Get values from a given table + * Example: $this->items( + * [ + * 'guid' => [ + * 'operator' => 'IN', + * 'value' => [''xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'', ''xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx''] + * ] + * ] + * ); + * Example: $this->items($ids); + * + * @param array $keys The item keys + * + * @return array|null + * @since 2.0.1 + */ + public function items(array $keys): ?array + { + return $this->model->items( + $this->load->items( + ['all' => 'a.*'], ['a' => $this->getTable()], $this->prefix($keys) + ), + $this->getTable() + ); + } + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string + { + return $this->table; + } + + /** + * Add prefix to the keys + * + * @param array $keys The query keys + * + * @return array + * @since 2.0.1 + */ + private function prefix(array &$keys): array + { + // update the key values + $bucket = []; + foreach ($keys as $k => $v) + { + $bucket['a.' . $k] = $v; + } + return $bucket; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Data/Action/Update.php b/src/VastDevelopmentMethod/Joomla/Data/Action/Update.php new file mode 100644 index 0000000..9e2bc86 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Data/Action/Update.php @@ -0,0 +1,206 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Data\Action; + + +use VastDevelopmentMethod\Joomla\Interfaces\ModelInterface as Model; +use VastDevelopmentMethod\Joomla\Interfaces\UpdateInterface as Database; +use VastDevelopmentMethod\Joomla\Interfaces\Data\UpdateInterface; + + +/** + * Data Update + * + * @since 3.2.2 + */ +class Update implements UpdateInterface +{ + /** + * Model + * + * @var Model + * @since 3.2.0 + */ + protected Model $model; + + /** + * Database + * + * @var Database + * @since 3.2.0 + */ + protected Database $database; + + /** + * Table Name + * + * @var string + * @since 3.2.1 + */ + protected string $table; + + /** + * Constructor + * + * @param Model $model The set model object. + * @param Database $database The update database object. + * @param string|null $table The table name. + * + * @since 3.2.0 + */ + public function __construct(Model $model, Database $database, ?string $table = null) + { + $this->model = $model; + $this->database = $database; + if ($table !== null) + { + $this->table = $table; + } + } + + /** + * Set the current active table + * + * @param string|null $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(?string $table): self + { + if ($table !== null) + { + $this->table = $table; + } + + return $this; + } + + /** + * Update a value to a given table + * Example: $this->value(Value, 'value_key', 'GUID'); + * + * @param mixed $value The field value + * @param string $field The field key + * @param string $keyValue The key value + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function value($value, string $field, string $keyValue, string $key = 'guid'): bool + { + // build the array + $item = []; + $item[$key] = $keyValue; + $item[$field] = $value; + + // Update the column of this table using $key as the primary key. + return $this->row($item, $key); + } + + /** + * Update single row with multiple values to a given table + * Example: $this->item(Array); + * + * @param array $item The item to save + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function row(array $item, string $key = 'guid'): bool + { + // check if object could be modelled + if (($item = $this->model->row($item, $this->getTable())) !== null) + { + // Update the column of this table using $key as the primary key. + return $this->database->row($item, $key, $this->getTable()); + } + return false; + } + + /** + * Update multiple rows to a given table + * Example: $this->items(Array); + * + * @param array|null $items The items updated in database (array of arrays) + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function rows(?array $items, string $key = 'guid'): bool + { + // check if object could be modelled + if (($items = $this->model->rows($items, $this->getTable())) !== null) + { + // Update the column of this table using $key as the primary key. + return $this->database->rows($items, $key, $this->getTable()); + } + return false; + } + + /** + * Update single item with multiple values to a given table + * Example: $this->item(Object); + * + * @param object $item The item to save + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function item(object $item, string $key = 'guid'): bool + { + // check if object could be modelled + if (($item = $this->model->item($item, $this->getTable())) !== null) + { + // Update the column of this table using $key as the primary key. + return $this->database->item($item, $key, $this->getTable()); + } + return false; + } + + /** + * Update multiple items to a given table + * Example: $this->items(Array); + * + * @param array|null $items The items updated in database (array of objects) + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function items(?array $items, string $key = 'guid'): bool + { + // check if object could be modelled + if (($items = $this->model->items($items, $this->getTable())) !== null) + { + // Update the column of this table using $key as the primary key. + return $this->database->items($items, $key, $this->getTable()); + } + return false; + } + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string + { + return $this->table; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Data/Factory.php b/src/VastDevelopmentMethod/Joomla/Data/Factory.php new file mode 100644 index 0000000..4bbf01d --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Data/Factory.php @@ -0,0 +1,46 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Data; + + +use Joomla\DI\Container; +use VastDevelopmentMethod\Joomla\Service\Table; +use VastDevelopmentMethod\Joomla\Service\Database; +use VastDevelopmentMethod\Joomla\Service\Model; +use VastDevelopmentMethod\Joomla\Service\Data; +use VastDevelopmentMethod\Joomla\Interfaces\FactoryInterface; +use VastDevelopmentMethod\Joomla\Abstraction\Factory as ExtendingFactory; + + +/** + * Data Factory + * + * @since 3.2.2 + */ +abstract class Factory extends ExtendingFactory implements FactoryInterface +{ + /** + * Create a container object + * + * @return Container + * @since 3.2.2 + */ + protected static function createContainer(): Container + { + return (new Container()) + ->registerServiceProvider(new Table()) + ->registerServiceProvider(new Database()) + ->registerServiceProvider(new Model()) + ->registerServiceProvider(new Data()); + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Data/Item.php b/src/VastDevelopmentMethod/Joomla/Data/Item.php new file mode 100644 index 0000000..7274835 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Data/Item.php @@ -0,0 +1,245 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Data; + + +use VastDevelopmentMethod\Joomla\Interfaces\Data\LoadInterface as Load; +use VastDevelopmentMethod\Joomla\Interfaces\Data\InsertInterface as Insert; +use VastDevelopmentMethod\Joomla\Interfaces\Data\UpdateInterface as Update; +use VastDevelopmentMethod\Joomla\Interfaces\Data\DeleteInterface as Delete; +use VastDevelopmentMethod\Joomla\Interfaces\LoadInterface as Database; +use VastDevelopmentMethod\Joomla\Interfaces\Data\ItemInterface; + + +/** + * Data Item + * + * @since 3.2.2 + */ +final class Item implements ItemInterface +{ + /** + * The Load Class. + * + * @var Load + * @since 3.2.2 + */ + protected Load $load; + + /** + * The Insert Class. + * + * @var Insert + * @since 3.2.2 + */ + protected Insert $insert; + + /** + * The Update Class. + * + * @var Update + * @since 3.2.2 + */ + protected Update $update; + + /** + * The Delete Class. + * + * @var Delete + * @since 3.2.2 + */ + protected Delete $delete; + + /** + * The Load Class. + * + * @var Database + * @since 3.2.2 + */ + protected Database $database; + + /** + * Table Name + * + * @var string + * @since 3.2.1 + */ + protected string $table; + + /** + * Constructor. + * + * @param Load $load The LoadInterface Class. + * @param Insert $insert The InsertInterface Class. + * @param Update $update The UpdateInterface Class. + * @param Delete $delete The UpdateInterface Class. + * @param Database $database The Database Load Class. + * @param string|null $table The table name. + * + * @since 3.2.2 + */ + public function __construct(Load $load, Insert $insert, Update $update, + Delete $delete, Database $database, ?string $table = null) + { + $this->load = $load; + $this->insert = $insert; + $this->update = $update; + $this->delete = $delete; + $this->database = $database; + if ($table !== null) + { + $this->table = $table; + } + } + + /** + * Set the current active table + * + * @param string $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(string $table): self + { + $this->table = $table; + + return $this; + } + + /** + * Get an item + * + * @param string $value The item key value + * @param string $key The item key + * + * @return object|null The item object or null + * @since 3.2.2 + */ + public function get(string $value, string $key = 'guid'): ?object + { + return $this->load->table($this->getTable())->item([$key => $value]); + } + + /** + * Get the value + * + * @param string $value The item key value + * @param string $key The item key + * @param string $get The key of the values we want back + * + * @return mixed + * @since 3.2.2 + */ + public function value(string $value, string $key = 'guid', string $get = 'id') + { + return $this->load->table($this->getTable())->value([$key => $value], $get); + } + + /** + * Set an item + * + * @param object $item The item + * @param string $key The item key + * @param string|null $action The action to load power + * + * @return bool + * @since 3.2.2 + */ + public function set(object $item, string $key = 'guid', ?string $action = null): bool + { + if ($action !== null || (isset($item->{$key}) && ($action = $this->action($item->{$key}, $key)) !== null)) + { + return method_exists($this, $action) ? $this->{$action}($item, $key) : false; + } + + return false; + } + + /** + * Delete an item + * + * @param string $value The item key value + * @param string $key The item key + * + * @return bool + * @since 3.2.2 + */ + public function delete(string $value, string $key = 'guid'): bool + { + return $this->delete->table($this->getTable())->items([$key => $value]); + } + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string + { + return $this->table; + } + + /** + * Insert a item + * + * @param object $item The item + * + * @return bool + * @since 3.2.2 + */ + private function insert(object $item): bool + { + return $this->insert->table($this->getTable())->item($item); + } + + /** + * Update a item + * + * @param object $item The item + * @param string $key The item key + * + * @return bool + * @since 3.2.2 + */ + private function update(object $item, string $key): bool + { + return $this->update->table($this->getTable())->item($item, $key); + } + + /** + * Get loading action + * + * @param string $value The key value the item + * @param string $key The item key + * + * @return string + * @since 3.2.2 + */ + private function action(string $value, string $key): string + { + $id = $this->database->value( + ["a.id" => 'id'], + ["a" => $this->getTable()], + ["a.$key" => $value] + ); + + if ($id !== null && $id > 0) + { + return 'update'; + } + + return 'insert'; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Data/Items.php b/src/VastDevelopmentMethod/Joomla/Data/Items.php new file mode 100644 index 0000000..cd2296d --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Data/Items.php @@ -0,0 +1,342 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Data; + + +use VastDevelopmentMethod\Joomla\Interfaces\Data\LoadInterface as Load; +use VastDevelopmentMethod\Joomla\Interfaces\Data\InsertInterface as Insert; +use VastDevelopmentMethod\Joomla\Interfaces\Data\UpdateInterface as Update; +use VastDevelopmentMethod\Joomla\Interfaces\Data\DeleteInterface as Delete; +use VastDevelopmentMethod\Joomla\Interfaces\LoadInterface as Database; +use VastDevelopmentMethod\Joomla\Interfaces\Data\ItemsInterface; + + +/** + * Data Items + * + * @since 3.2.2 + */ +final class Items implements ItemsInterface +{ + /** + * The LoadInterface Class. + * + * @var Load + * @since 3.2.2 + */ + protected Load $load; + + /** + * The InsertInterface Class. + * + * @var Insert + * @since 3.2.2 + */ + protected Insert $insert; + + /** + * The UpdateInterface Class. + * + * @var Update + * @since 3.2.2 + */ + protected Update $update; + + /** + * The DeleteInterface Class. + * + * @var Delete + * @since 3.2.2 + */ + protected Delete $delete; + + /** + * The Load Class. + * + * @var Database + * @since 3.2.2 + */ + protected Database $database; + + /** + * Table Name + * + * @var string + * @since 3.2.1 + */ + protected string $table; + + /** + * Constructor. + * + * @param Load $load The LoadInterface Class. + * @param Insert $insert The InsertInterface Class. + * @param Update $update The UpdateInterface Class. + * @param Delete $delete The DeleteInterface Class. + * @param Database $database The Database Load Class. + * @param string|null $table The table name. + * + * @since 3.2.2 + */ + public function __construct(Load $load, Insert $insert, Update $update, Delete $delete, + Database $database, ?string $table = null) + { + $this->load = $load; + $this->insert = $insert; + $this->update = $update; + $this->delete = $delete; + $this->database = $database; + if ($table !== null) + { + $this->table = $table; + } + } + + /** + * Set the current active table + * + * @param string $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(string $table): self + { + $this->table = $table; + + return $this; + } + + /** + * Get list of items + * + * @param array $values The ids of the items + * @param string $key The key of the values + * + * @return array|null The item object or null + * @since 3.2.2 + */ + public function get(array $values, string $key = 'guid'): ?array + { + return $this->load->table($this->getTable())->items([ + $key => [ + 'operator' => 'IN', + 'value' => array_values($values) + ] + ]); + } + + /** + * Get the values + * + * @param array $values The list of values (to search by). + * @param string $key The key on which the values being searched. + * @param string $get The key of the values we want back + * + * @return array|null The array of found values. + * @since 3.2.2 + */ + public function values(array $values, string $key = 'guid', string $get = 'id'): ?array + { + // Perform the database query + return $this->load->table($this->getTable())->values([ + $key => [ + 'operator' => 'IN', + 'value' => array_values($values) + ] + ], $get); + } + + /** + * Set items + * + * @param array $items The list of items + * @param string $key The key on which the items should be set + * + * @return bool + * @since 3.2.2 + */ + public function set(array $items, string $key = 'guid'): bool + { + if (($sets = $this->sort($items, $key)) !== null) + { + foreach ($sets as $action => $items) + { + $this->{$action}($items, $key); + } + return true; + } + + return false; + } + + /** + * Delete items + * + * @param array $values The item key value + * @param string $key The item key + * + * @return bool + * @since 3.2.2 + */ + public function delete(array $values, string $key = 'guid'): bool + { + return $this->delete->table($this->getTable())->items([$key => ['operator' => 'IN', 'value' => $values]]); + } + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string + { + return $this->table; + } + + /** + * Insert a item + * + * @param array $items The item + * + * @return bool + * @since 3.2.2 + */ + private function insert(array $items): bool + { + return $this->insert->table($this->getTable())->rows($items); + } + + /** + * Update a item + * + * @param object $item The item + * @param string $key The item key + * + * @return bool + * @since 3.2.2 + */ + private function update(array $items, string $key): bool + { + return $this->update->table($this->getTable())->rows($items, $key); + } + + /** + * Sort items between insert and update. + * + * @param array $items The list of items. + * @param string $key The key on which the items should be sorted. + * + * @return array|null The sorted sets. + * @since 3.2.2 + */ + private function sort(array $items, string $key): ?array + { + // Extract relevant items based on the key. + $values = $this->extractValues($items, $key); + if ($values === null) + { + return null; + } + + $sets = [ + 'insert' => [], + 'update' => [] + ]; + + // Check for existing items. + $existingItems = $this->database->values( + ["a.$key" => $key], + ["a" => $this->getTable()], + ["a.$key" => ['operator' => 'IN', 'value' => $values]] + ); + + if ($existingItems !== null) + { + $sets['update'] = $this->extractSet($items, $existingItems, $key) ?? []; + $sets['insert'] = $this->extractSet($items, $existingItems, $key, true) ?? []; + } + else + { + $sets['insert'] = $items; + } + + // If either set is empty, remove it from the result. + $sets = array_filter($sets); + + return !empty($sets) ? $sets : null; + } + + /** + * Extracts values for a given key from an array of items. + * Items can be either arrays or objects. + * + * @param array $items Array of items (arrays or objects) + * @param string $key The key to extract values for + * + * @return array|null Extracted values + * @since 3.2.2 + */ + private function extractValues(array $items, string $key): ?array + { + $result = []; + + foreach ($items as $item) + { + if (is_array($item) && !empty($item[$key])) + { + $result[] = $item[$key]; + } + elseif (is_object($item) && !empty($item->{$key})) + { + $result[] = $item->{$key}; + } + } + + return ($result === []) ? null : $result; + } + + /** + * Extracts items from an array of items based on a set. + * Items can be either arrays or objects. + * + * @param array $items Array of items (arrays or objects) + * @param array $set The set to match values against + * @param string $key The key of the set values + * @param bool $inverse Whether to extract items not in the set + * + * @return array|null Extracted values + * @since 3.2.2 + */ + private function extractSet(array $items, array $set, string $key, bool $inverse = false): ?array + { + $result = []; + + foreach ($items as $item) + { + $value = is_array($item) ? ($item[$key] ?? null) : ($item->{$key} ?? null); + + if ($value !== null) + { + $inSet = in_array($value, $set); + if (($inSet && !$inverse) || (!$inSet && $inverse)) + { + $result[] = is_array($item) ? $item : (array) $item; // convert all to arrays + } + } + } + + return empty($result) ? null : $result; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Data/MultiSubform.php b/src/VastDevelopmentMethod/Joomla/Data/MultiSubform.php new file mode 100644 index 0000000..1dddd6f --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Data/MultiSubform.php @@ -0,0 +1,511 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Data; + + +use VastDevelopmentMethod\Joomla\Interfaces\Data\SubformInterface as Subform; +use VastDevelopmentMethod\Joomla\Interfaces\Data\MultiSubformInterface; + + +/** + * CRUD the data of multi subform to another views (tables) + * + * @since 3.2.2 + */ +final class MultiSubform implements MultiSubformInterface +{ + /** + * The Subform Class. + * + * @var Subform + * @since 3.2.2 + */ + protected Subform $subform; + + /** + * Constructor. + * + * @param Subform $subform The Subform Class. + * + * @since 3.2.2 + */ + public function __construct(Subform $subform) + { + $this->subform = $subform; + } + + /** + * Get a subform items + * + * @param array $getMap The the map to get the subfrom data + * + * Example: + * $getMap = [ + * '_core' => [ + * 'table' =>'data', + * 'linkValue' => $item->guid ?? '', + * 'linkKey' => 'look', + * 'field' => 'data', + * 'get' => ['guid','email','image','mobile_phone','website','dateofbirth'] + * ], + * 'countries' => [ + * 'table' =>'data_country', + * 'linkValue' => 'data:guid', // coretable:fieldname + * 'linkKey' => 'data', + * 'get' => ['guid','country','currency'] + * ] + * ]; + * + * @return array|null The subform + * @since 3.2.2 + */ + public function get(array $getMap): ?array + { + // Validate the core map presence and structure + if (!isset($getMap['_core']) || !is_array($getMap['_core']) || !$this->validGetMap($getMap['_core'])) + { + return null; + } + + // Initialize the core data + $coreData = $this->getSubformData($getMap['_core']); + + // Return null if fetching core data fails + if (null === $coreData) + { + return null; + } + $table = $getMap['_core']['table']; + unset($getMap['_core']); + + // Recursively get data for all nested subforms + return $this->getNestedSubforms($getMap, $coreData, $table); + } + + /** + * Set a subform items + * + * @param array $items The list of items from the subform to set + * @param array $setMap The the map to set the subfrom data + * + * Example: + * $items, + * $setMap = [ + * '_core' => [ + * 'table' =>'data', + * 'indexKey' => 'guid', + * 'linkKey' => 'look', + * 'linkValue' => $data['guid'] ?? '' + * ], + * 'countries' => [ + * 'table' =>'data_country', + * 'indexKey' => 'guid', + * 'linkKey' => 'data', + * 'linkValue' => 'data:guid' // coretable:fieldname + * ] + * ]; + * + * @return bool + * @since 3.2.2 + */ + public function set(array $items, array $setMap): bool + { + // Validate the core map presence and structure + if (!isset($setMap['_core']) || !is_array($setMap['_core']) || !$this->validSetMap($setMap['_core'])) + { + return false; + } + + // Save the core data + if (!$this->setSubformData($items, $setMap['_core'])) + { + return false; + } + $table = $setMap['_core']['table']; + unset($setMap['_core']); + + // Recursively set data for all nested subforms + return $this->setNestedSubforms($setMap, $items, $table); + } + + /** + * Fetch data based on provided map configuration. + * + * @param array $map Map configuration + * @param array|null $coreData The core data to be appended with subform data + * + * @return array|null Fetched data or null on failure + * @since 3.2.2 + */ + private function getSubformData(array $map, ?array $coreData = null): ?array + { + $map['linkValue'] = $this->setLinkValue($map['linkValue'], $coreData); + + if (empty($map['linkValue']) || strpos($map['linkValue'], ':') !== false) + { + return null; + } + + return $this->subform->table($map['table'])->get( + $map['linkValue'], + $map['linkKey'], + $map['field'], + $map['get'] + ); + } + + /** + * Set data based on provided map configuration. + * + * @param array $items The list of items from the subform to set + * @param array $map The the map to set the subfrom data + * @param array|null $coreData The core data to be appended with subform data + * + * @return bool + * @since 3.2.2 + */ + private function setSubformData(array $items, array $map, ?array $coreData = null): bool + { + $map['linkValue'] = $this->setLinkValue($map['linkValue'], $coreData); + + if (empty($map['linkValue']) || strpos($map['linkValue'], ':') !== false) + { + return false; + } + + return $this->subform->table($map['table'])->set( + $items, + $map['indexKey'], + $map['linkKey'], + $map['linkValue'] + ); + } + + /** + * Set the linked value if needed, and posible. + * + * @param string $linkValue The current linkValue + * @param array|null $data The already found data as table => dataSet[field] => value + * + * @return string|null The actual linkValue + * @since 3.2.2 + */ + private function setLinkValue(string $linkValue, ?array $data = null): ?string + { + if ($data !== null && strpos($linkValue, ':') !== false) + { + [$table, $field] = explode(':', $linkValue); + $linkValue = $data[$table][$field] ?? null; + } + + return $linkValue; + } + + /** + * Recursively process additional subform data. + * + * @param array $getMap The nested map of data to process + * @param array $subformData The core subform data + * @param string $table The core table + * + * @return array The core data with nested subforms included + * @since 3.2.2 + */ + private function getNestedSubforms(array $getMap, array $subformData, string $table): array + { + foreach ($subformData as &$subform) + { + $subform = $this->processGetSubform($getMap, $subform, $table); + } + + return $subformData; + } + + /** + * Recursively process additional subform data. + * + * @param array $setMap The nested map of data to process + * @param array $subformData The core subform data + * @param string $table The core table + * + * @return bool + * @since 3.2.2 + */ + private function setNestedSubforms(array $setMap, array $subformData, string $table): bool + { + $status = true; + foreach ($subformData as $subform) + { + if (!$this->processSetSubform($setMap, $subform, $table)) + { + $status = false; + } + } + + return $status; + } + + /** + * Process each subform entry based on the map. + * + * @param array $getMap Mapping data for processing subforms + * @param array $subform A single subform entry + * @param string $table The table name used for linking values + * + * @return array Updated subform + * @since 3.2.2 + */ + private function processGetSubform(array $getMap, array $subform, string $table): array + { + foreach ($getMap as $key => $map) + { + if (!is_array($map) || isset($subform[$key])) + { + continue; + } + + $this->processGetMap($subform, $map, $key, $table); + } + + return $subform; + } + + /** + * Process each subform entry based on the map. + * + * @param array $setMap Mapping data for processing subforms + * @param array $subform A single subform entry + * @param string $table The table name used for linking values + * + * @return bool + * @since 3.2.2 + */ + private function processSetSubform(array $setMap, array $subform, string $table): bool + { + $status = true; + foreach ($setMap as $key => $map) + { + if (!is_array($map) || !isset($subform[$key])) + { + continue; + } + + if (!$this->processSetMap($subform, $map, $key, $table)) + { + $status = false; + } + } + + return $status; + } + + /** + * Process a given map by either fetching nested subforms or handling them directly. + * + * @param array &$subform Reference to subform data + * @param array $map Map configuration for subform processing + * @param string $key Key associated with the map + * @param string $table Core table name for linking values + * + * @return void + * @since 3.2.2 + */ + private function processGetMap(array &$subform, array $map, string $key, string $table): void + { + if (isset($map['_core'])) + { + $this->handleCoreGetMap($subform, $map, $key, $table); + } + else + { + $this->handleRegularGetMap($subform, $map, $key, $table); + } + } + + /** + * Process a given map by either setting nested subforms or handling them directly. + * + * @param array $subform Subform data + * @param array $map Map configuration for subform processing + * @param string $key Key associated with the map + * @param string $table Core table name for linking values + * + * @return bool + * @since 3.2.2 + */ + private function processSetMap(array $subform, array $map, string $key, string $table): bool + { + if (isset($map['_core'])) + { + return $this->handleCoreSetMap($subform, $map, $key, $table); + } + + return $this->handleRegularSetMap($subform, $map, $key, $table); + } + + /** + * Handle the processing of '_core' maps in a subform. + * + * @param array &$subform Reference to subform data + * @param array $map Map configuration for core subform processing + * @param string $key Key associated with the map + * @param string $table Core table name for linking values + * + * @return void + * @since 3.2.2 + */ + private function handleCoreGetMap(array &$subform, array $map, string $key, string $table): void + { + if (is_array($map['_core']) && $this->validGetMap($map['_core'])) + { + $map['_core']['linkValue'] = $this->setLinkValue($map['_core']['linkValue'], [$table => $subform]); + + $subCoreData = $this->get($map); + if ($subCoreData !== null) + { + $subform[$key] = $subCoreData; + } + } + } + + /** + * Handle the processing of '_core' maps in a subform. + * + * @param array $subform Subform data + * @param array $map Map configuration for core subform processing + * @param string $key Key associated with the map + * @param string $table Core table name for linking values + * + * @return bool + * @since 3.2.2 + */ + private function handleCoreSetMap(array $subform, array $map, string $key, string $table): bool + { + if (is_array($map['_core']) && $this->validGetMap($map['_core'])) + { + $map['_core']['linkValue'] = $this->setLinkValue($map['_core']['linkValue'], [$table => $subform]); + + return $this->set($subform[$key], $map); + } + + return false; + } + + /** + * Handle the processing of regular maps in a subform. + * + * @param array &$subform Reference to subform data + * @param array $map Map configuration for regular subform processing + * @param string $key Key associated with the map + * @param string $table Core table name for linking values + * + * @return void + * @since 3.2.2 + */ + private function handleRegularGetMap(array &$subform, array $map, string $key, string $table): void + { + $map['field'] = $key; + if ($this->validGetMap($map)) + { + $subformData = $this->getSubformData($map, [$table => $subform]); + if ($subformData !== null) + { + $subform[$key] = $subformData; + } + } + } + + /** + * Handle the processing of regular maps in a subform. + * + * @param array $subform Subform data + * @param array $map Map configuration for regular subform processing + * @param string $key Key associated with the map + * @param string $table Core table name for linking values + * + * @return bool + * @since 3.2.2 + */ + private function handleRegularSetMap(array $subform, array $map, string $key, string $table): bool + { + if ($this->validSetMap($map)) + { + return $this->setSubformData($subform[$key], $map, [$table => $subform]); + } + + return false; + } + + /** + * Validate the get map configuration for fetching subform data. + * Ensures all required keys are present and have valid values. + * + * @param array $map The map configuration to validate. + * + * @return bool Returns true if the map is valid, false otherwise. + * @since 3.2.2 + */ + private function validGetMap(array $map): bool + { + // List of required keys with their expected types or validation functions + $requiredKeys = [ + 'table' => 'is_string', + 'linkValue' => 'is_string', + 'linkKey' => 'is_string', + 'field' => 'is_string', + 'get' => 'is_array' + ]; + + // Iterate through each required key and validate + foreach ($requiredKeys as $key => $validator) + { + if (empty($map[$key]) || !$validator($map[$key])) + { + return false; // Key missing or validation failed + } + } + + return true; // All checks passed + } + + /** + * Validate the set map configuration for fetching subform data. + * Ensures all required keys are present and have valid values. + * + * @param array $map The map configuration to validate. + * + * @return bool Returns true if the map is valid, false otherwise. + * @since 3.2.2 + */ + private function validSetMap(array $map): bool + { + // List of required keys with their expected types or validation functions + $requiredKeys = [ + 'table' => 'is_string', + 'indexKey' => 'is_string', + 'linkKey' => 'is_string', + 'linkValue' => 'is_string' + ]; + + // Iterate through each required key and validate + foreach ($requiredKeys as $key => $validator) + { + if (empty($map[$key]) || !$validator($map[$key])) + { + return false; // Key missing or validation failed + } + } + + return true; // All checks passed + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Data/Subform.php b/src/VastDevelopmentMethod/Joomla/Data/Subform.php new file mode 100644 index 0000000..269a124 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Data/Subform.php @@ -0,0 +1,310 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Data; + + +use VastDevelopmentMethod\Joomla\Interfaces\Data\ItemsInterface as Items; +use VastDevelopmentMethod\Joomla\Interfaces\Data\SubformInterface; + + +/** + * CRUD the data of any sub-form to another view (table) + * + * @since 3.2.2 + */ +final class Subform implements SubformInterface +{ + /** + * The ItemsInterface Class. + * + * @var Items + * @since 3.2.2 + */ + protected Items $items; + + /** + * Table Name + * + * @var string + * @since 3.2.1 + */ + protected string $table; + + /** + * Constructor. + * + * @param Items $items The ItemsInterface Class. + * @param string|null $table The table name. + * + * @since 3.2.2 + */ + public function __construct(Items $items, ?string $table = null) + { + $this->items = $items; + if ($table !== null) + { + $this->table = $table; + } + } + + /** + * Set the current active table + * + * @param string $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(string $table): self + { + $this->table = $table; + + return $this; + } + + /** + * Get a subform items + * + * @param string $linkValue The value of the link key in child table. + * @param string $linkKey The link key on which the items where linked in the child table. + * @param string $field The parent field name of the subform in the parent view. + * @param array $get The array get:set of the keys of each row in the subform. + * + * @return array|null The subform + * @since 3.2.2 + */ + public function get(string $linkValue, string $linkKey, string $field, array $get): ?array + { + if (($items = $this->items->table($this->getTable())->get([$linkValue], $linkKey)) !== null) + { + return $this->converter($items, $get, $field); + } + return null; + } + + /** + * Set a subform items + * + * @param array $items The list of items from the subform to set + * @param string $indexKey The index key on which the items should be observed as it relates to insert/update/delete. + * @param string $linkKey The link key on which the items where linked in the child table. + * @param string $linkValue The value of the link key in child table. + * + * @return bool + * @since 3.2.2 + */ + public function set(array $items, string $indexKey, string $linkKey, string $linkValue): bool + { + $items = $this->process($items, $indexKey, $linkKey, $linkValue); + + $this->purge($items, $indexKey, $linkKey, $linkValue); + + return $this->items->table($this->getTable())->set( + $items, $indexKey + ); + } + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string + { + return $this->table; + } + + /** + * Purge all items no longer in subform + * + * @param array $items The list of items to set. + * @param string $indexKey The index key on which the items should be observed as it relates to insert/update/delete + * @param string $linkKey The link key on which the items where linked in the child table. + * @param string $linkValue The value of the link key in child table. + * + * @return void + * @since 3.2.2 + */ + private function purge(array $items, string $indexKey, string $linkKey, string $linkValue): void + { + // Get the current index values from the database + $currentIndexValues = $this->items->table($this->getTable())->values([$linkValue], $linkKey, $indexKey); + + if ($currentIndexValues !== null) + { + // Extract the index values from the items array + $activeIndexValues = array_values(array_map(function($item) use ($indexKey) { + return $item[$indexKey] ?? null; + }, $items)); + + // Find the index values that are no longer in the items array + $inactiveIndexValues = array_diff($currentIndexValues, $activeIndexValues); + + // Delete the inactive index values + if (!empty($inactiveIndexValues)) + { + $this->items->table($this->getTable())->delete($inactiveIndexValues, $indexKey); + } + } + } + + /** + * Filters the specified keys from an array of objects or arrays, converts them to arrays, + * and sets them by association with a specified key and an incrementing integer. + * + * @param array $items Array of objects or arrays to be filtered. + * @param array $keySet Array of keys to retain in each item. + * @param string $field The field prefix for the resulting associative array. + * + * @return array Array of filtered arrays set by association. + * @since 3.2.2 + */ + private function converter(array $items, array $keySet, string $field): array + { + /** + * Filters keys for a single item and converts it to an array. + * + * @param object|array $item The item to filter. + * @param array $keySet The keys to retain. + * + * @return array The filtered array. + * @since 3.2.2 + */ + $filterKeys = function ($item, array $keySet) { + $filteredArray = []; + foreach ($keySet as $key) { + if (is_object($item) && property_exists($item, $key)) { + $filteredArray[$key] = $item->{$key}; + } elseif (is_array($item) && array_key_exists($key, $item)) { + $filteredArray[$key] = $item[$key]; + } + } + return $filteredArray; + }; + + $result = []; + foreach ($items as $index => $item) + { + $filteredArray = $filterKeys($item, $keySet); + $result[$field . $index] = $filteredArray; + } + + return $result; + } + + /** + * Processes an array of arrays based on the specified key. + * + * @param array $items Array of arrays to be processed. + * @param string $indexKey The index key on which the items should be observed as it relates to insert/update/delete + * @param string $linkKey The link key on which the items where linked in the child table. + * @param string $linkValue The value of the link key in child table. + * + * @return array The processed array of arrays. + * @since 3.2.2 + */ + private function process(array $items, string $indexKey, string $linkKey, string $linkValue): array + { + foreach ($items as &$item) + { + $value = $item[$indexKey] ?? ''; + switch ($indexKey) { + case 'guid': + if (empty($value)) + { + // set INDEX + $item[$indexKey] = $this->setGuid($indexKey); + } + break; + case 'id': + if (empty($value)) + { + $item[$indexKey] = 0; + } + break; + default: + // No action for other keys if empty + break; + } + // set LINK + $item[$linkKey] = $linkValue; + } + + return array_values($items); + } + + /** + * Returns a GUIDv4 string + * + * Thanks to Dave Pearson (and other) + * https://www.php.net/manual/en/function.com-create-guid.php#119168 + * + * Uses the best cryptographically secure method + * for all supported platforms with fallback to an older, + * less secure version. + * + * @param string $key The key to check and modify values. + * @param bool $trim + * + * @return string + * + * @since 3.0.9 + */ + private function setGuid(string $key, bool $trim = true): string + { + // Windows + if (function_exists('com_create_guid')) + { + if ($trim) + { + return trim(\com_create_guid(), '{}'); + } + return \com_create_guid(); + } + + // set the braces if needed + $lbrace = $trim ? "" : chr(123); // "{" + $rbrace = $trim ? "" : chr(125); // "}" + + // OSX/Linux + if (function_exists('openssl_random_pseudo_bytes')) + { + $data = \openssl_random_pseudo_bytes(16); + $data[6] = chr( ord($data[6]) & 0x0f | 0x40); // set version to 0100 + $data[8] = chr( ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10 + return $lbrace . vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)) . $lbrace; + } + + // Fallback (PHP 4.2+) + mt_srand((double) microtime() * 10000); + $charid = strtolower( md5( uniqid( rand(), true))); + $hyphen = chr(45); // "-" + $guidv4 = $lbrace. + substr($charid, 0, 8). $hyphen. + substr($charid, 8, 4). $hyphen. + substr($charid, 12, 4). $hyphen. + substr($charid, 16, 4). $hyphen. + substr($charid, 20, 12). + $rbrace; + + // check that it does not already exist (one in a billion chance ;) + // but we do it any way... + if ($this->items->table($this->getTable())->values([$guidv4], $key)) + { + return $this->setGuid($key); + } + + return $guidv4; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Database/Delete.php b/src/VastDevelopmentMethod/Joomla/Database/Delete.php new file mode 100644 index 0000000..d82c205 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Database/Delete.php @@ -0,0 +1,132 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Database; + + +use VastDevelopmentMethod\Joomla\Utilities\ArrayHelper; +use VastDevelopmentMethod\Joomla\Interfaces\DeleteInterface; +use VastDevelopmentMethod\Joomla\Abstraction\Database; + + +/** + * Database Delete Class + * + * @since 3.2.0 + */ +final class Delete extends Database implements DeleteInterface +{ + /** + * Delete all items in the database that match these conditions + * + * @param array $conditions Conditions by which to delete the data in database [array of arrays (key => value)] + * @param string $table The table where the data is being deleted + * + * @return bool + * @since 3.2.2 + **/ + public function items(array $conditions, string $table): bool + { + // set the update columns + if ($conditions === []) + { + return false; + } + + // get a query object + $query = $this->db->getQuery(true); + + // start the conditions bucket + $_conditions = []; + foreach ($conditions as $key => $value) + { + if (ArrayHelper::check($value)) + { + if (isset($value['value']) && isset($value['operator'])) + { + // check if value needs to be quoted + $quote = $value['quote'] ?? true; + if (!$quote) + { + if (ArrayHelper::check($value['value'])) + { + // add the where by array + $_conditions[] = $this->db->quoteName($key) + . ' ' . $value['operator'] + . ' ' . ' (' . + implode(',', $value['value']) + . ')'; + } + else + { + // add the conditions + $_conditions[] = $this->db->quoteName($key) + . ' ' . $value['operator'] + . ' ' . $value['value']; + } + } + else + { + if (ArrayHelper::check($value['value'])) + { + // add the where by array + $_conditions[] = $this->db->quoteName($key) + . ' ' . $value['operator'] + . ' ' . ' (' . + implode(',', array_map(fn($val) => $this->quote($val), $value['value'])) + . ')'; + } + else + { + // add the conditions + $_conditions[] = $this->db->quoteName($key) + . ' ' . $value['operator'] + . ' ' . $this->quote($value['value']); + } + } + } + else + { + // we should through an exception + // for security we just return false for now + return false; + } + } + else + { + // add default condition + $_conditions[] = $this->db->quoteName($key) . ' = ' . $this->quote($value); + } + } + + // set the query targets + $query->delete($this->db->quoteName($this->getTable($table))); + $query->where($_conditions); + + $this->db->setQuery($query); + + return $this->db->execute(); + } + + /** + * Truncate a table + * + * @param string $table The table that should be truncated + * + * @return void + * @since 3.2.2 + **/ + public function truncate(string $table): void + { + $this->db->truncateTable($this->getTable($table)); + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Database/Insert.php b/src/VastDevelopmentMethod/Joomla/Database/Insert.php new file mode 100644 index 0000000..519a6e4 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Database/Insert.php @@ -0,0 +1,290 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Database; + + +use Joomla\CMS\Date\Date; +use VastDevelopmentMethod\Joomla\Utilities\ArrayHelper; +use VastDevelopmentMethod\Joomla\Interfaces\InsertInterface; +use VastDevelopmentMethod\Joomla\Abstraction\Database; + + +/** + * Database Insert Class + * + * @since 3.2.0 + */ +final class Insert extends Database implements InsertInterface +{ + /** + * Switch to set the defaults + * + * @var bool + * @since 1.2.0 + **/ + protected bool $defaults = true; + + /** + * Switch to prevent/allow defaults from being added. + * + * @param bool $trigger toggle the defaults + * + * @return void + * @since 3.2.0 + **/ + public function defaults(bool $trigger = true) + { + $this->defaults = $trigger; + } + + /** + * Insert rows to the database (with remapping and filtering columns option) + * + * @param array $data Dataset to store in database [array of arrays (key => value)] + * @param string $table The table where the data is being added + * @param array $columns Data columns for remapping and filtering + * + * @return bool + * @since 3.2.0 + **/ + public function rows(array $data, string $table, array $columns = []): bool + { + if (!ArrayHelper::check($data)) + { + return false; + } + + if ($columns === []) + { + $columns = $this->getArrayColumns($data); + } + + return ($columns === []) ? false : $this->insert($data, $table, $columns, true); + } + + /** + * Insert items to the database (with remapping and filtering columns option) + * + * @param array $data Data to store in database (array of objects) + * @param string $table The table where the data is being added + * @param array $columns Data columns for remapping and filtering + * + * @return bool + * @since 3.2.0 + **/ + public function items(array $data, string $table, array $columns = []): bool + { + if (!ArrayHelper::check($data)) + { + return false; + } + + if ($columns === []) + { + $columns = $this->getObjectsColumns($data); + } + + return ($columns === []) ? false : $this->insert($data, $table, $columns, false); + } + + /** + * Insert row to the database + * + * @param array $data Dataset to store in database (key => value) + * @param string $table The table where the data is being added + * + * @return bool + * @since 3.2.0 + **/ + public function row(array $data, string $table): bool + { + return $this->rows([$data], $table); + } + + /** + * Insert item to the database + * + * @param object $data Dataset to store in database (key => value) + * @param string $table The table where the data is being added + * + * @return bool + * @since 3.2.0 + **/ + public function item(object $data, string $table): bool + { + return $this->items([$data], $table); + } + + /** + * Get columns from data array + * + * @param array $data Data array + * + * @return array + * @since 3.2.0 + **/ + protected function getArrayColumns(array &$data): array + { + $row = array_values($data)[0]; + + if (!ArrayHelper::check($row)) + { + return []; + } + + $columns = array_keys($row); + + return array_combine($columns, $columns); + } + + /** + * Get columns from data objects + * + * @param array $data Data objects + * + * @return array + * @since 3.2.0 + **/ + protected function getObjectsColumns(array &$data): array + { + $row = array_values($data)[0]; + + if (!is_object($row)) + { + return []; + } + + $columns = get_object_vars($row); + + return array_combine(array_keys($columns), array_keys($columns)); + } + + /** + * Insert data into the database + * + * @param array $data Data to store in database + * @param string $table The table where the data is being added + * @param array $columns Data columns for remapping and filtering + * @param bool $isArray Whether the data is an array of arrays or an array of objects + * + * @return bool + * @since 3.2.0 + **/ + protected function insert(array &$data, string $table, array $columns, bool $isArray): bool + { + // set joomla default columns + $add_created = false; + $add_version = false; + $add_published = false; + + // check if we should load the defaults + if ($this->defaults) + { + // get the date + $date = (new Date())->toSql(); + + if (!isset($columns['created'])) + { + $columns['created'] = ' (o_O) '; + $add_created = true; + } + + if (!isset($columns['version'])) + { + $columns['version'] = ' (o_O) '; + $add_version = true; + } + + if (!isset($columns['published'])) + { + $columns['published'] = ' (o_O) '; + $add_published = true; + } + // the (o_O) prevents an empty value from being loaded + } + + // get a query object + $query = $this->db->getQuery(true); + + // set the query targets + $query->insert($this->db->quoteName($this->getTable($table)))->columns($this->db->quoteName(array_keys($columns))); + + // limiting factor on the amount of rows to insert before we reset the query + $limit = 300; + + // set the insert values + foreach ($data as $nr => $value) + { + // check the limit + if ($limit <= 1) + { + // execute and reset the query + $this->db->setQuery($query); + $this->db->execute(); + + // reset limit + $limit = 300; + + // get a query object + $query = $this->db->getQuery(true); + + // set the query targets + $query->insert($this->db->quoteName($this->getTable($table)))->columns($this->db->quoteName(array_keys($columns))); + } + + $row = []; + foreach ($columns as $column => $key) + { + if (' (o_O) ' !== $key) + { + $row[] = ($isArray && isset($value[$key])) ? $this->quote($value[$key]) + : ((!$isArray && isset($value->{$key})) ? $this->quote($value->{$key}) : ''); + } + } + + // set joomla default columns + if ($add_created) + { + $row[] = $this->db->quote($date); + } + + if ($add_version) + { + $row[] = 1; + } + + if ($add_published) + { + $row[] = 1; + } + + // add to query + $query->values(implode(',', $row)); + + // decrement the limiter + $limit--; + + // clear the data from memory + unset($data[$nr]); + } + + // execute the final query + $this->db->setQuery($query); + $this->db->execute(); + + // always reset the default switch + $this->defaults(); + + return true; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Database/Load.php b/src/VastDevelopmentMethod/Joomla/Database/Load.php new file mode 100644 index 0000000..e5ff2d7 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Database/Load.php @@ -0,0 +1,502 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Database; + + +use VastDevelopmentMethod\Joomla\Utilities\ArrayHelper; +use VastDevelopmentMethod\Joomla\Interfaces\LoadInterface; +use VastDevelopmentMethod\Joomla\Abstraction\Database; + + +/** + * Database Load + * + * @since 3.2.0 + */ +final class Load extends Database implements LoadInterface +{ + /** + * Load data rows as an array of associated arrays + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * @param int|null $limit Limit the number of values returned + * + * @return array|null + * @since 3.2.0 + **/ + public function rows(array $select, array $tables, ?array $where = null, + ?array $order = null, ?int $limit = null): ?array + { + // set key if found + $key = $this->getKey($select); + + // check if we can get many rows + if ($this->many($select, $tables, $where, $order, $limit)) + { + // return associated arrays from the table records + return $this->db->loadAssocList($key); + } + + // data does not exist + return null; + } + + /** + * Load data rows as an array of objects + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * @param int|null $limit Limit the number of values returned + * + * @return array|null + * @since 3.2.0 + **/ + public function items(array $select, array $tables, ?array $where = null, + ?array $order = null, ?int $limit = null): ?array + { + // set key if found + $key = $this->getKey($select); + + // check if we can get many rows + if ($this->many($select, $tables, $where, $order, $limit)) + { + // return associated arrays from the table records + return $this->db->loadObjectList($key); + } + + // data does not exist + return null; + } + + /** + * Load data row as an associated array + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * + * @return array|null + * @since 3.2.0 + **/ + public function row(array $select, array $tables, ?array $where = null, ?array $order = null): ?array + { + // check if we can get one row + if ($this->one($select, $tables, $where, $order)) + { + return $this->db->loadAssoc(); + } + + // data does not exist + return null; + } + + /** + * Load data row as an object + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * + * @return object|null + * @since 3.2.0 + **/ + public function item(array $select, array $tables, ?array $where = null, ?array $order = null): ?object + { + // check if we can get one row + if ($this->one($select, $tables, $where, $order)) + { + return $this->db->loadObject(); + } + + // data does not exist + return null; + } + + /** + * Get the max value based on a filtered result from a given table + * + * @param string $field The field key + * @param string $tables The tables + * @param array $filter The filter keys + * + * @return int|null + * @since 3.2.0 + **/ + public function max($field, array $tables, array $filter): ?int + { + // only do check if we have the table set + if (isset($tables['a'])) + { + // get the query + $query = $this->query(["all" => "MAX(`$field`)"], $tables, $filter); + + // Load the max number + $this->db->setQuery($query); + $this->db->execute(); + + // check if we have values + if ($this->db->getNumRows()) + { + return (int) $this->db->loadResult(); + } + } + + // data does not exist + return null; + } + + /** + * Count the number of items based on filter result from a given table + * + * @param string $tables The table + * @param array $filter The filter keys + * + * @return int|null + * @since 3.2.0 + **/ + public function count(array $tables, array $filter): ?int + { + // only do check if we have the table set + if (isset($tables['a'])) + { + // get the query + $query = $this->query(["all" => 'COUNT(*)'], $tables, $filter); + + // Load the max number + $this->db->setQuery($query); + $this->db->execute(); + + // check if we have values + if ($this->db->getNumRows()) + { + return (int) $this->db->loadResult(); + } + } + + // data does not exist + return null; + } + + /** + * Load one value from a row + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * + * @return mixed + * @since 3.2.0 + **/ + public function value(array $select, array $tables, ?array $where = null, ?array $order = null) + { + // check if we can get one value + if ($this->one($select, $tables, $where, $order)) + { + return $this->db->loadResult(); + } + + // data does not exist + return null; + } + + /** + * Load values from multiple rows + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * @param int|null $limit Limit the number of values returned + * + * @return array|null + * @since 3.2.2 + **/ + public function values(array $select, array $tables, ?array $where = null, + ?array $order = null, ?int $limit = null): ?array + { + // check if we can get many rows + if ($this->many($select, $tables, $where, $order, $limit)) + { + return $this->db->loadColumn(); + } + + // data does not exist + return null; + } + + /** + * Load many + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * @param int|null $limit Limit the number of values returned + * + * @return bool + * @since 3.2.0 + **/ + protected function many(array $select, array $tables, ?array $where = null, + ?array $order = null, ?int $limit = null): bool + { + // only do check if we have the table set + if (isset($tables['a'])) + { + // get the query + $query = $this->query($select, $tables, $where, $order, $limit); + + // Load the items + $this->db->setQuery($query); + $this->db->execute(); + + // check if we have values + if ($this->db->getNumRows()) + { + return true; + } + } + + // data does not exist + return false; + } + + /** + * Load one + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * + * @return bool + * @since 3.2.0 + **/ + protected function one(array $select, array $tables, ?array $where = null, ?array $order = null): bool + { + // only do check if we have the table set + if (isset($tables['a'])) + { + // get the query + $query = $this->query($select, $tables, $where, $order); + + // Load the item + $this->db->setQuery($query, 0, 1); + $this->db->execute(); + + // check if we have values + if ($this->db->getNumRows()) + { + return true; + } + } + + // data does not exist + return false; + } + + /** + * Get the query object + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * @param int|null $limit Limit the number of values returned + * + * @return object|null The query object (DatabaseQuery) + * @since 3.2.0 + **/ + protected function query(array $select, array $tables, ?array $where = null, + ?array $order = null, ?int $limit = null): ?object + { + $query = $this->db->getQuery(true); + + // check if we have an all selection set + if (isset($select['all'])) + { + // all selection example array: ['all' => ['a.*', 'b.*']] + if (ArrayHelper::check($select['all'])) + { + foreach ($select['all'] as $select_all) + { + // set target selection + $query->select( + $select_all + ); + } + } + // all selection example string: ['all' =>'a.*'] + elseif (is_string($select['all'])) + { + // set target selection + $query->select( + $select['all'] + ); + } + unset($select['all']); + } + + // load the table where join + if (ArrayHelper::check($select)) + { + // set target selection + $query->select( + $this->db->quoteName( + array_keys($select), + array_values($select) + ) + ); + } + + // set main table + $query->from($this->db->quoteName($this->getTable($tables['a']), 'a')); + + // remove main table + unset($tables['a']); + + // load the table where join + if (ArrayHelper::check($tables)) + { + foreach ($tables as $as => $table) + { + $query->join( + 'LEFT', $this->db->quoteName( + $this->getTable($table['name']), $as + ) . ' ON (' . $this->db->quoteName($table['join_on']) + . ' = ' . $this->db->quoteName($table['as_on']) . ')' + ); + } + } + + // load the table where getters + if (ArrayHelper::check($where)) + { + foreach ($where as $key => $value) + { + if (ArrayHelper::check($value)) + { + if (isset($value['value']) && isset($value['operator'])) + { + // check if value needs to be quoted + $quote = $value['quote'] ?? true; + if (!$quote) + { + if (ArrayHelper::check($value['value'])) + { + // add the where by array + $query->where($this->db->quoteName($key) . ' ' . + $value['operator'] . ' (' . + implode(',', $value['value']) + . ')' + ); + } + else + { + // add the where + $query->where($this->db->quoteName($key) . ' ' . + $value['operator'] . ' ' . $value['value']); + } + } + else + { + if (ArrayHelper::check($value['value'])) + { + // add the where by array + $query->where($this->db->quoteName($key) . ' ' . + $value['operator'] . ' (' . + implode(',', + array_map( + fn($val) => $this->quote($val), + $value['value'] + ) + ) + . ')' + ); + } + else + { + // add the where + $query->where($this->db->quoteName($key) . ' ' . + $value['operator'] . ' ' . $this->quote($value['value'])); + } + } + } + else + { + // we should through an exception + // for security we just return nothing for now + return null; + } + } + else + { + // add the where + $query->where($this->db->quoteName($key) . + ' = ' . $this->quote($value)); + } + } + } + + // load the row ordering + if (ArrayHelper::check($order)) + { + foreach ($order as $key => $direction) + { + // add the ordering + $query->order($this->db->quoteName($key) . + ' ' . $direction); + } + } + + // only return a limited number + if (is_numeric($limit)) + { + $query->setLimit($limit); + } + + return $query; + } + + /** + * Get the key from the selection array. + * + * This function retrieves a key from the provided selection array. + * The key is removed from the array after being retrieved. + * + * @param array $select Array of selection keys. + * + * @return string|null The key, or null if no key is found. + * @since 3.2.2 + **/ + protected function getKey(array &$select): ?string + { + $key = null; + + // Check for 'key' first and ensure it's a string. + if (isset($select['key']) && is_string($select['key'])) + { + $key = $select['key']; + unset($select['key']); // Remove 'key' from the array. + } + + return $key; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Database/Update.php b/src/VastDevelopmentMethod/Joomla/Database/Update.php new file mode 100644 index 0000000..f6deac3 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Database/Update.php @@ -0,0 +1,188 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Database; + + +use VastDevelopmentMethod\Joomla\Interfaces\UpdateInterface; +use VastDevelopmentMethod\Joomla\Abstraction\Database; + + +/** + * Database Update Class + * + * @since 3.2.0 + */ +final class Update extends Database implements UpdateInterface +{ + /** + * Update rows in the database (with remapping and filtering columns option) + * + * @param array $data Dataset to update in database [array of arrays (key => value)] + * @param string $key Dataset key column to use in updating the values in the Database + * @param string $table The table where the data is being updated + * @param array $columns Data columns for remapping and filtering + * + * @return bool + * @since 3.2.0 + **/ + public function rows(array $data, string $key, string $table, array $columns = []): bool + { + // set the update columns + if ($data === [] || strlen($key) == 0) + { + return false; + } + + // set the update values + foreach ($data as $values) + { + if ($columns !== []) + { + // load only what is part of the columns set + $row = []; + foreach ($columns as $column => $key_) + { + if (isset($values[$key_])) + { + $row[$column] = $values[$key_]; + } + } + + // update the row + $this->row($row, $key, $table); + } + else + { + // update the row + $this->row((array) $values, $key, $table); + } + } + + return true; + } + + /** + * Update items in the database (with remapping and filtering columns option) + * + * @param array $data Data to updated in database (array of objects) + * @param string $key Dataset key column to use in updating the values in the Database + * @param string $table The table where the data is being update + * @param array $columns Data columns for remapping and filtering + * + * @return bool + * @since 3.2.0 + **/ + public function items(array $data, string $key, string $table, array $columns = []): bool + { + // set the update columns + if ($data === [] || strlen($key) == 0) + { + return false; + } + + // set the update values + foreach ($data as $nr => $values) + { + if ($columns !== []) + { + // load only what is part of the columns set + $row = []; + foreach ($columns as $column => $key_) + { + if (isset($values->{$key_})) + { + $row[$column] = $values->{$key_}; + } + } + + // update the row + $this->row($row, $key, $table); + } + else + { + // update the row + $this->row((array) $values, $key, $table); + } + } + + return true; + } + + /** + * Update row in the database + * + * @param array $data Dataset to update in database (key => value) + * @param string $key Dataset key column to use in updating the values in the Database + * @param string $table The table where the data is being updated + * + * @return bool + * @since 3.2.0 + **/ + public function row(array $data, string $key, string $table): bool + { + // set the update columns + if ($data === [] || strlen($key) == 0) + { + return false; + } + + // get a query object + $query = $this->db->getQuery(true); + + // set the query targets + $query->update($this->db->quoteName($this->getTable($table))); + + // set the update values + $key_ = null; + foreach ($data as $column => $value) + { + if ($column === $key) + { + $key_ = $value; + } + else + { + $query->set($this->db->quoteName($column) . ' = ' . $this->quote($value)); + } + } + + // add the key condition + if ($key_ !== null) + { + $query->where($this->db->quoteName($key) . ' = ' . $this->quote($key_)); + + // execute the final query + $this->db->setQuery($query); + + return $this->db->execute(); + } + + return false; + } + + /** + * Update item in the database + * + * @param object $data Dataset to update in database (key => value) + * @param string $key Dataset key column to use in updating the values in the Database + * @param string $table The table where the data is being updated + * + * @return bool + * @since 3.2.0 + **/ + public function item(object $data, string $key, string $table): bool + { + // convert to an array + return $this->row((array) get_object_vars($data), $key, $table); + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/Data/DeleteInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/DeleteInterface.php new file mode 100644 index 0000000..eb3e00c --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/DeleteInterface.php @@ -0,0 +1,60 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces\Data; + + +/** + * Data Delete + * + * @since 3.2.2 + */ +interface DeleteInterface +{ + /** + * Set the current active table + * + * @param string|null $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(?string $table): self; + + /** + * Delete all items in the database that match these conditions + * + * @param array $conditions Conditions by which to delete the data in database [array of arrays (key => value)] + * + * @return bool + * @since 3.2.2 + **/ + public function items(array $conditions): bool; + + /** + * Truncate a table + * + * @param string|null $table The table that should be truncated + * + * @return void + * @since 3.2.2 + **/ + public function truncate(): void; + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/Data/InsertInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/InsertInterface.php new file mode 100644 index 0000000..5842e0a --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/InsertInterface.php @@ -0,0 +1,98 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces\Data; + + +/** + * Data Insert + * + * @since 3.2.2 + */ +interface InsertInterface +{ + /** + * Set the current active table + * + * @param string|null $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(?string $table): self; + + /** + * Insert a value to a given table + * Example: $this->value(Value, 'value_key', 'GUID'); + * + * @param mixed $value The field value + * @param string $field The field key + * @param string $keyValue The key value + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function value($value, string $field, string $keyValue, string $key = 'guid'): bool; + + /** + * Insert single row with multiple values to a given table + * Example: $this->item(Array); + * + * @param array $item The item to save + * + * @return bool + * @since 3.2.0 + */ + public function row(array $item): bool; + + /** + * Insert multiple rows to a given table + * Example: $this->items(Array); + * + * @param array|null $items The items updated in database (array of arrays) + * + * @return bool + * @since 3.2.0 + */ + public function rows(?array $items): bool; + + /** + * Insert single item with multiple values to a given table + * Example: $this->item(Object); + * + * @param object $item The item to save + * + * @return bool + * @since 3.2.0 + */ + public function item(object $item): bool; + + /** + * Insert multiple items to a given table + * Example: $this->items(Array); + * + * @param array|null $items The items updated in database (array of objects) + * + * @return bool + * @since 3.2.0 + */ + public function items(?array $items): bool; + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/Data/ItemInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/ItemInterface.php new file mode 100644 index 0000000..a947d5f --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/ItemInterface.php @@ -0,0 +1,86 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces\Data; + + +/** + * Data Item Interface + * + * @since 3.2.2 + */ +interface ItemInterface +{ + /** + * Set the current active table + * + * @param string $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(string $table): self; + + /** + * Get an item + * + * @param string $value The item key value + * @param string $key The item key + * + * @return object|null The item object or null + * @since 3.2.2 + */ + public function get(string $value, string $key = 'guid'): ?object; + + /** + * Get the value + * + * @param string $value The item key value + * @param string $key The item key + * @param string $get The key of the values we want back + * + * @return mixed + * @since 3.2.2 + */ + public function value(string $value, string $key = 'guid', string $get = 'id'); + + /** + * Set an item + * + * @param object $item The item + * @param string $key The item key + * @param string|null $action The action to load power + * + * @return bool + * @since 3.2.2 + */ + public function set(object $item, string $key = 'guid', ?string $action = null): bool; + + /** + * Delete an item + * + * @param string $value The item key value + * @param string $key The item key + * + * @return bool + * @since 3.2.2 + */ + public function delete(string $value, string $key = 'guid'): bool; + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/Data/ItemsInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/ItemsInterface.php new file mode 100644 index 0000000..a906c42 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/ItemsInterface.php @@ -0,0 +1,85 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces\Data; + + +/** + * Data Items Interface + * + * @since 3.2.2 + */ +interface ItemsInterface +{ + /** + * Set the current active table + * + * @param string $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(string $table): self; + + /** + * Get list of items + * + * @param array $values The ids of the items + * @param string $key The key of the values + * + * @return array|null The item object or null + * @since 3.2.2 + */ + public function get(array $values, string $key = 'guid'): ?array; + + /** + * Get the values + * + * @param array $values The list of values (to search by). + * @param string $key The key on which the values being searched. + * @param string $get The key of the values we want back + * + * @return array|null The array of found values. + * @since 3.2.2 + */ + public function values(array $values, string $key = 'guid', string $get = 'id'): ?array; + + /** + * Set items + * + * @param array $items The list of items + * @param string $key The key on which the items should be set + * + * @return bool + * @since 3.2.2 + */ + public function set(array $items, string $key = 'guid'): bool; + + /** + * Delete items + * + * @param array $values The item key value + * @param string $key The item key + * + * @return bool + * @since 3.2.2 + */ + public function delete(array $values, string $key = 'guid'): bool; + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/Data/LoadInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/LoadInterface.php new file mode 100644 index 0000000..e0886d9 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/LoadInterface.php @@ -0,0 +1,106 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces\Data; + + +/** + * Data Load Interface + * + * @since 3.2.2 + */ +interface LoadInterface +{ + /** + * Set the current active table + * + * @param string|null $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(?string $table): self; + + /** + * Get a value from a given table + * Example: $this->value( + * [ + * 'guid' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + * ], 'value_key' + * ); + * + * @param array $keys The item keys + * @param string $field The field key + * + * @return mixed + * @since 2.0.1 + */ + public function value(array $keys, string $field); + + /** + * Get a value from multiple rows from a given table + * Example: $this->values( + * [ + * 'guid' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + * ], 'value_key' + * ); + * + * @param array $keys The item keys + * @param string $field The field key + * + * @return array|null + * @since 3.2.2 + */ + public function values(array $keys, string $field): ?array; + + /** + * Get values from a given table + * Example: $this->item( + * [ + * 'guid' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + * ] + * ); + * + * @param array $keys The item keys + * + * @return object|null + * @since 2.0.1 + */ + public function item(array $keys): ?object; + + /** + * Get values from a given table + * Example: $this->items( + * [ + * 'guid' => [ + * 'operator' => 'IN', + * 'value' => [''xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'', ''xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx''] + * ] + * ] + * ); + * Example: $this->items($keys); + * + * @param array $keys The item keys + * + * @return array|null + * @since 2.0.1 + */ + public function items(array $keys): ?array; + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/Data/MultiSubformInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/MultiSubformInterface.php new file mode 100644 index 0000000..ca5e1d8 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/MultiSubformInterface.php @@ -0,0 +1,77 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces\Data; + + +/** + * Data Multi Subform Interface + * + * @since 3.2.2 + */ +interface MultiSubformInterface +{ + /** + * Get a subform items + * + * @param array $getMap The the map to get the subfrom data + * + * Example: + * $getMap = [ + * '_core' => [ + * 'table' =>'data', + * 'linkValue' => $item->guid ?? '', + * 'linkKey' => 'look', + * 'field' => 'data', + * 'get' => ['guid','email','image','mobile_phone','website','dateofbirth'] + * ], + * 'countries' => [ + * 'table' =>'data_country', + * 'linkValue' => 'data:guid', // coretable:fieldname + * 'linkKey' => 'data', + * 'get' => ['guid','country','currency'] + * ] + * ]; + * + * @return array|null The subform + * @since 3.2.2 + */ + public function get(array $getMap): ?array; + + /** + * Set a subform items + * + * @param array $items The list of items from the subform to set + * @param array $setMap The the map to set the subfrom data + * + * Example: + * $items, + * $setMap = [ + * '_core' => [ + * 'table' =>'data', + * 'indexKey' => 'guid', + * 'linkKey' => 'look', + * 'linkValue' => $data['guid'] ?? '' + * ], + * 'countries' => [ + * 'table' =>'data_country', + * 'indexKey' => 'guid', + * 'linkKey' => 'data', + * 'linkValue' => 'data:guid' // coretable:fieldname + * ] + * ]; + * + * @return bool + * @since 3.2.2 + */ + public function set(array $items, array $setMap): bool; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/Data/SubformInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/SubformInterface.php new file mode 100644 index 0000000..3153566 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/SubformInterface.php @@ -0,0 +1,66 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces\Data; + + +/** + * Data Subform Interface + * + * @since 3.2.2 + */ +interface SubformInterface +{ + /** + * Set the current active table + * + * @param string $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(string $table): self; + + /** + * Get a subform items + * + * @param string $linkValue The value of the link key in child table. + * @param string $linkKey The link key on which the items where linked in the child table. + * @param string $field The parent field name of the subform in the parent view. + * @param array $set The array SET of the keys of each row in the subform. + * + * @return array|null The subform + * @since 3.2.2 + */ + public function get(string $linkValue, string $linkKey, string $field, array $set): ?array; + + /** + * Set a subform items + * + * @param array $items The list of items from the subform to set + * @param string $indexKey The index key on which the items should be observed as it relates to insert/update/delete. + * @param string $linkKey The link key on which the items where linked in the child table. + * @param string $linkValue The value of the link key in child table. + * + * @return bool + * @since 3.2.2 + */ + public function set(array $items, string $indexKey, string $linkKey, string $linkValue): bool; + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/Data/UpdateInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/UpdateInterface.php new file mode 100644 index 0000000..b0353aa --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/Data/UpdateInterface.php @@ -0,0 +1,102 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces\Data; + + +/** + * Data Update + * + * @since 3.2.2 + */ +interface UpdateInterface +{ + /** + * Set the current active table + * + * @param string|null $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(?string $table): self; + + /** + * Update a value to a given table + * Example: $this->value(Value, 'value_key', 'GUID'); + * + * @param mixed $value The field value + * @param string $field The field key + * @param string $keyValue The key value + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function value($value, string $field, string $keyValue, string $key = 'guid'): bool; + + /** + * Update single row with multiple values to a given table + * Example: $this->item(Array); + * + * @param array $item The item to save + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function row(array $item, string $key = 'guid'): bool; + + /** + * Update multiple rows to a given table + * Example: $this->items(Array); + * + * @param array|null $items The items updated in database (array of arrays) + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function rows(?array $items, string $key = 'guid'): bool; + + /** + * Update single item with multiple values to a given table + * Example: $this->item(Object); + * + * @param object $item The item to save + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function item(object $item, string $key = 'guid'): bool; + + /** + * Update multiple items to a given table + * Example: $this->items(Array); + * + * @param array|null $items The items updated in database (array of objects) + * @param string $key The key name + * + * @return bool + * @since 3.2.0 + */ + public function items(?array $items, string $key = 'guid'): bool; + + /** + * Get the current active table + * + * @return string + * @since 3.2.2 + */ + public function getTable(): string; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/DeleteInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/DeleteInterface.php new file mode 100644 index 0000000..3bb041a --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/DeleteInterface.php @@ -0,0 +1,43 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces; + + +/** + * Database Delete Interface + * + * @since 3.2.0 + */ +interface DeleteInterface +{ + /** + * Delete all rows in the database that match these conditions + * + * @param array $conditions Conditions by which to delete the data in database [array of arrays (key => value)] + * @param string $table The table where the data is being deleted + * + * @return bool + * @since 3.2.0 + **/ + public function items(array $conditions, string $table): bool; + + /** + * Truncate a table + * + * @param string $table The table that should be truncated + * + * @return void + * @since 3.2.2 + **/ + public function truncate(string $table): void; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/FactoryInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/FactoryInterface.php new file mode 100644 index 0000000..b211f2e --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/FactoryInterface.php @@ -0,0 +1,43 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces; + + +use Joomla\DI\Container; + + +/** + * The Container Factory Interface + * + * @since 0.0.0 + */ +interface FactoryInterface +{ + /** + * Get any class from the container + * + * @param string $key The container class key + * + * @return Mixed + * @since 0.0.0 + */ + public static function _(string $key); + + /** + * Get the global container + * + * @return Container + * @since 0.0.0 + */ + public static function getContainer(): Container; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/InsertInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/InsertInterface.php new file mode 100644 index 0000000..4405755 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/InsertInterface.php @@ -0,0 +1,78 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces; + + +/** + * Database Insert Interface + * + * @since 3.2.0 + */ +interface InsertInterface +{ + /** + * Switch to prevent/allow defaults from being added. + * + * @param bool $trigger toggle the defaults + * + * @return void + * @since 3.2.0 + **/ + public function defaults(bool $trigger = true); + + /** + * Insert rows to the database (with remapping and filtering columns option) + * + * @param array $data Dataset to store in database [array of arrays (key => value)] + * @param string $table The table where the data is being added + * @param array $columns Data columns for remapping and filtering + * + * @return bool + * @since 3.2.0 + **/ + public function rows(array $data, string $table, array $columns = []): bool; + + /** + * Insert items to the database (with remapping and filtering columns option) + * + * @param array $data Data to store in database (array of objects) + * @param string $table The table where the data is being added + * @param array $columns Data columns for remapping and filtering + * + * @return bool + * @since 3.2.0 + **/ + public function items(array $data, string $table, array $columns = []): bool; + + /** + * Insert row to the database + * + * @param array $data Dataset to store in database (key => value) + * @param string $table The table where the data is being added + * + * @return bool + * @since 3.2.0 + **/ + public function row(array $data, string $table): bool; + + /** + * Insert item to the database + * + * @param object $data Dataset to store in database (key => value) + * @param string $table The table where the data is being added + * + * @return bool + * @since 3.2.0 + **/ + public function item(object $data, string $table): bool; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/LoadInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/LoadInterface.php new file mode 100644 index 0000000..be4a302 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/LoadInterface.php @@ -0,0 +1,129 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces; + + +/** + * Database Load Interface + * + * @since 3.2.0 + */ +interface LoadInterface +{ + /** + * Load data rows as an array of associated arrays + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * @param int|null $limit Limit the number of values returned + * + * @return array|null + * @since 3.2.0 + **/ + public function rows(array $select, array $tables, ?array $where = null, + ?array $order = null, ?int $limit = null): ?array; + + /** + * Load data rows as an array of objects + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * @param int|null $limit Limit the number of values returned + * + * @return array|null + * @since 3.2.0 + **/ + public function items(array $select, array $tables, ?array $where = null, + ?array $order = null, ?int $limit = null): ?array; + + /** + * Load data row as an associated array + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * + * @return array|null + * @since 3.2.0 + **/ + public function row(array $select, array $tables, ?array $where = null, ?array $order = null): ?array; + + /** + * Load data row as an object + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * + * @return object|null + * @since 3.2.0 + **/ + public function item(array $select, array $tables, ?array $where = null, ?array $order = null): ?object; + + /** + * Get the max value based on a filtered result from a given table + * + * @param string $field The field key + * @param string $tables The table + * @param array $filter The filter keys + * + * @return int|null + * @since 3.2.0 + **/ + public function max($field, array $tables, array $filter): ?int; + + /** + * Count the number of items based on filter result from a given table + * + * @param string $tables The table + * @param array $filter The filter keys + * + * @return int|null + * @since 3.2.0 + **/ + public function count(array $tables, array $filter): ?int; + + /** + * Load one value from a row + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * + * @return mixed + * @since 3.2.0 + **/ + public function value(array $select, array $tables, ?array $where = null, ?array $order = null); + + /** + * Load values from multiple rows + * + * @param array $select Array of selection keys + * @param array $tables Array of tables to search + * @param array|null $where Array of where key=>value match exist + * @param array|null $order Array of how to order the data + * @param int|null $limit Limit the number of values returned + * + * @return array|null + * @since 3.2.2 + **/ + public function values(array $select, array $tables, ?array $where = null, + ?array $order = null, ?int $limit = null): ?array; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/ModelInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/ModelInterface.php new file mode 100644 index 0000000..0a46284 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/ModelInterface.php @@ -0,0 +1,137 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces; + + +/** + * Model Interface + * + * @since 3.2.0 + */ +interface ModelInterface +{ + /** + * Set the current active table + * + * @param string $table The table that should be active + * + * @return self + * @since 3.2.2 + */ + public function table(string $table): self; + + /** + * Model the value + * Example: $this->value(value, 'value_key', 'table_name'); + * + * @param mixed $value The value to model + * @param string $field The field key + * @param string|null $table The table + * + * @return mixed + * @since 3.2.0 + */ + public function value($value, string $field, ?string $table = null); + + /** + * Model a value of multiple items + * Example: $this->items(Array, 'value_key', 'table_name'); + * + * @param array|null $items The array of values + * @param string $field The field key + * @param string|null $table The table + * + * @return array|null + * @since 3.2.0 + */ + public function values(?array $items = null, string $field, ?string $table = null): ?array; + + /** + * Model the values of an item + * Example: $this->item(Object, 'table_name'); + * + * @param object|null $item The item object + * @param string|null $table The table + * + * @return object|null + * @since 3.2.0 + */ + public function item(?object $item, ?string $table = null): ?object; + + /** + * Model the values of multiple items + * Example: $this->items(Array, 'table_name'); + * + * @param array|null $items The array of item objects + * @param string|null $table The table + * + * @return array|null + * @since 3.2.0 + */ + public function items(?array $items = null, ?string $table = null): ?array; + + /** + * Model the values of an row + * Example: $this->item(Array, 'table_name'); + * + * @param array|null $item The item array + * @param string|null $table The table + * + * @return array|null + * @since 3.2.0 + */ + public function row(?array $item, ?string $table = null): ?array; + + /** + * Model the values of multiple rows + * Example: $this->items(Array, 'table_name'); + * + * @param array|null $items The array of item array + * @param string|null $table The table + * + * @return array|null + * @since 3.2.0 + */ + public function rows(?array $items = null, ?string $table = null): ?array; + + /** + * Get last modeled ID + * Example: $this->last('table_name'); + * + * @param string|null $table The table + * + * @return int|null + * @since 3.2.0 + */ + public function last(?string $table = null): ?int; + + /** + * Set the current active table + * + * @param string $tableName The table name + * + * @return void + * @since 3.2.2 + */ + public function setTable(string $tableName): void; + + /** + * Set the switch to control the behaviour of empty values + * + * @param bool $allowEmpty The switch + * + * @return void + * @since 3.2.2 + */ + public function setAllowEmpty(bool $allowEmpty): void; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/SchemaInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/SchemaInterface.php new file mode 100644 index 0000000..cd987c9 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/SchemaInterface.php @@ -0,0 +1,53 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces; + + +/** + * Schema Checking Interface + * + * @since 3.2.1 + */ +interface SchemaInterface +{ + /** + * Check and update database schema for missing fields or tables. + * + * @return array The array of successful updates/actions, if empty no update/action was taken. + * @since 3.2.1 + * @throws \Exception If there is an error during the update process. + */ + public function update(): array; + + /** + * Create a table with all necessary fields. + * + * @param string $table The name of the table to create. + * + * @return void + * @since 3.2.1 + * @throws \Exception If there is an error creating the table. + */ + public function createTable(string $table): void; + + /** + * Update the schema of an existing table. + * + * @param string $table The table to update. + * + * @return void + * @since 3.2.1 + * @throws \Exception If there is an error while updating the schema. + */ + public function updateSchema(string $table): void; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/Tableinterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/Tableinterface.php new file mode 100644 index 0000000..a866759 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/Tableinterface.php @@ -0,0 +1,91 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces; + + +/** + * The VDM Core Table Interface + */ +interface Tableinterface +{ + /** + * Get any value from a item/field/column of an area/view/table + * Example: $this->get('table_name', 'field_name', 'value_key'); + * Get an item/field/column of an area/view/table + * Example: $this->get('table_name', 'field_name'); + * Get all items/fields/columns of an area/view/table + * Example: $this->get('table_name'); + * Get all areas/views/tables with all their item/field/column details + * Example: $this->get('All'); + * Example: $this->get(); + * + * @param string|null $table The table + * @param string|null $field The field + * @param string|null $key The value key + * + * @return mixed + * @since 3.2.0 + */ + public function get(?string $table = null, ?string $field = null, ?string $key = null); + + /** + * Get title field from an area/view/table + * + * @param string $table The area + * + * @return ?array + * @since 3.2.0 + */ + public function title(string $table): ?array; + + /** + * Get title field name + * + * @param string $table The area + * + * @return string + * @since 3.2.0 + */ + public function titleName(string $table): string; + + /** + * Get all tables + * + * @return array + * @since 3.2.0 + */ + public function tables(): array; + + /** + * Check if a table (and field) exist + * + * @param string $table The area + * @param string|null $field The area + * + * @return bool + * @since 3.2.0 + */ + public function exist(string $table, ?string $field = null): bool; + + /** + * Get all fields of an area/view/table + * + * @param string $table The area + * @param bool $default Add the default fields + * @param bool $details Add/Leave fields the details + * + * @return array|null On success an array of fields + * @since 3.2.0 + */ + public function fields(string $table, bool $default = false, bool $details = false): ?array; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Interfaces/UpdateInterface.php b/src/VastDevelopmentMethod/Joomla/Interfaces/UpdateInterface.php new file mode 100644 index 0000000..e34bc9d --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Interfaces/UpdateInterface.php @@ -0,0 +1,72 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Interfaces; + + +/** + * Database Update Interface + * + * @since 3.2.0 + */ +interface UpdateInterface +{ + /** + * Update rows in the database (with remapping and filtering columns option) + * + * @param array $data Dataset to update in database [array of arrays (key => value)] + * @param string $key Dataset key column to use in updating the values in the Database + * @param string $table The table where the data is being updated + * @param array $columns Data columns for remapping and filtering + * + * @return bool + * @since 3.2.0 + **/ + public function rows(array $data, string $key, string $table, array $columns = []): bool; + + /** + * Update items in the database (with remapping and filtering columns option) + * + * @param array $data Data to updated in database (array of objects) + * @param string $key Dataset key column to use in updating the values in the Database + * @param string $table The table where the data is being update + * @param array $columns Data columns for remapping and filtering + * + * @return bool + * @since 3.2.0 + **/ + public function items(array $data, string $key, string $table, array $columns = []): bool; + + /** + * Update row in the database + * + * @param array $data Dataset to update in database (key => value) + * @param string $key Dataset key column to use in updating the values in the Database + * @param string $table The table where the data is being updated + * + * @return bool + * @since 3.2.0 + **/ + public function row(array $data, string $key, string $table): bool; + + /** + * Update item in the database + * + * @param object $data Dataset to update in database (key => value) + * @param string $key Dataset key column to use in updating the values in the Database + * @param string $table The table where the data is being updated + * + * @return bool + * @since 3.2.0 + **/ + public function item(object $data, string $key, string $table): bool; +} + diff --git a/src/VastDevelopmentMethod/Joomla/Model/Load.php b/src/VastDevelopmentMethod/Joomla/Model/Load.php new file mode 100644 index 0000000..920781c --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Model/Load.php @@ -0,0 +1,118 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Model; + + +use VastDevelopmentMethod\Joomla\Utilities\StringHelper; +use VastDevelopmentMethod\Joomla\Utilities\ArrayHelper; +use VastDevelopmentMethod\Joomla\Utilities\ObjectHelper; +use VastDevelopmentMethod\Joomla\Interfaces\ModelInterface; +use VastDevelopmentMethod\Joomla\Abstraction\Model; + + +/** + * Power Model Load + * + * @since 3.2.2 + */ +final class Load extends Model implements ModelInterface +{ + /** + * Model the value + * Example: $this->value(value, 'field_key', 'table_name'); + * + * @param mixed $value The value to model + * @param string $field The field key + * @param string|null $table The table + * + * @return mixed + * @since 3.2.0 + */ + public function value($value, string $field, ?string $table = null) + { + // set the table name + if (empty($table)) + { + $table = $this->getTable(); + } + + // check if this is a valid table + if (($store = $this->table->get($table, $field, 'store')) !== null) + { + // open the value based on the store method + switch($store) + { + case 'base64': + $value = base64_decode((string) $value); + break; + case 'json': + $value = json_decode($value); + break; + } + } + + return $value; + } + + /** + * Validate before the value is modelled + * + * @param mixed $value The field value + * @param string|null $field The field key + * @param string|null $table The table + * + * @return bool + * @since 3.2.0 + */ + protected function validateBefore(&$value, ?string $field = null, ?string $table = null): bool + { + // only strings or numbers allowed + if (StringHelper::check($value) || is_numeric($value)) + { + return true; + } + // check if we allow empty + elseif ($this->getAllowEmpty() && empty($value)) + { + return true; + } + // remove empty values + return false; + } + + /** + * Validate after the value is modelled + * + * @param mixed $value The field value + * @param string|null $field The field key + * @param string|null $table The table + * + * @return bool + * @since 3.2.0 + */ + protected function validateAfter(&$value, ?string $field = null, ?string $table = null): bool + { + // only strings or numbers allowed + if (StringHelper::check($value) || ArrayHelper::check($value, true) || ObjectHelper::check($value) || is_numeric($value)) + { + return true; + } + // check if we allow empty + elseif ($this->getAllowEmpty() && empty($value)) + { + return true; + } + // remove empty values + return false; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Model/Upsert.php b/src/VastDevelopmentMethod/Joomla/Model/Upsert.php new file mode 100644 index 0000000..0f85154 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Model/Upsert.php @@ -0,0 +1,118 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Model; + + +use VastDevelopmentMethod\Joomla\Utilities\StringHelper; +use VastDevelopmentMethod\Joomla\Utilities\ArrayHelper; +use VastDevelopmentMethod\Joomla\Utilities\ObjectHelper; +use VastDevelopmentMethod\Joomla\Interfaces\ModelInterface; +use VastDevelopmentMethod\Joomla\Abstraction\Model; + + +/** + * Power Model Update or Insert + * + * @since 3.2.0 + */ +final class Upsert extends Model implements ModelInterface +{ + /** + * Model the value + * Example: $this->value(value, 'field_key', 'table_name'); + * + * @param mixed $value The value to model + * @param string $field The field key + * @param string|null $table The table + * + * @return mixed + * @since 3.2.0 + */ + public function value($value, string $field, ?string $table = null) + { + // set the table name + if (empty($table)) + { + $table = $this->getTable(); + } + + // check if this is a valid table + if (($store = $this->table->get($table, $field, 'store')) !== null) + { + // open the value based on the store method + switch($store) + { + case 'base64': + $value = base64_encode((string) $value); + break; + case 'json': + $value = json_encode($value, JSON_FORCE_OBJECT); + break; + } + } + + return $value; + } + + /** + * Validate before the value is modelled + * + * @param mixed $value The field value + * @param string|null $field The field key + * @param string|null $table The table + * + * @return bool + * @since 3.2.0 + */ + protected function validateBefore(&$value, ?string $field = null, ?string $table = null): bool + { + // check values + if (StringHelper::check($value) || ArrayHelper::check($value, true) || ObjectHelper::check($value) || is_numeric($value)) + { + return true; + } + // check if we allow empty + elseif ($this->getAllowEmpty() && empty($value)) + { + return true; + } + // remove empty values + return false; + } + + /** + * Validate after the value is modelled + * + * @param mixed $value The field value + * @param string|null $field The field key + * @param string|null $table The table + * + * @return bool + * @since 3.2.0 + */ + protected function validateAfter(&$value, ?string $field = null, ?string $table = null): bool + { + // only strings or numbers allowed + if (StringHelper::check($value) || is_numeric($value)) + { + return true; + } + // check if we allow empty + elseif ($this->getAllowEmpty() && empty($value)) + { + return true; + } + // remove empty values + return false; + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Service/Data.php b/src/VastDevelopmentMethod/Joomla/Service/Data.php new file mode 100644 index 0000000..cf220b5 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Service/Data.php @@ -0,0 +1,200 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Service; + + +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use VastDevelopmentMethod\Joomla\Data\Action\Load; +use VastDevelopmentMethod\Joomla\Data\Action\Insert; +use VastDevelopmentMethod\Joomla\Data\Action\Update; +use VastDevelopmentMethod\Joomla\Data\Action\Delete; +use VastDevelopmentMethod\Joomla\Data\Item; +use VastDevelopmentMethod\Joomla\Data\Items; +use VastDevelopmentMethod\Joomla\Data\Subform; +use VastDevelopmentMethod\Joomla\Data\MultiSubform; + + +/** + * Data Service Provider + * + * @since 3.2.0 + */ +class Data implements ServiceProviderInterface +{ + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * @since 3.2.0 + */ + public function register(Container $container) + { + $container->alias(Load::class, 'Data.Load') + ->share('Data.Load', [$this, 'getLoad'], true); + + $container->alias(Insert::class, 'Data.Insert') + ->share('Data.Insert', [$this, 'getInsert'], true); + + $container->alias(Update::class, 'Data.Update') + ->share('Data.Update', [$this, 'getUpdate'], true); + + $container->alias(Delete::class, 'Data.Delete') + ->share('Data.Delete', [$this, 'getDelete'], true); + + $container->alias(Item::class, 'Data.Item') + ->share('Data.Item', [$this, 'getItem'], true); + + $container->alias(Items::class, 'Data.Items') + ->share('Data.Items', [$this, 'getItems'], true); + + $container->alias(Subform::class, 'Data.Subform') + ->share('Data.Subform', [$this, 'getSubform'], true); + + $container->alias(MultiSubform::class, 'Data.MultiSubform') + ->share('Data.MultiSubform', [$this, 'getMultiSubform'], true); + } + + /** + * Get The Load Class. + * + * @param Container $container The DI container. + * + * @return Load + * @since 3.2.0 + */ + public function getLoad(Container $container): Load + { + return new Load( + $container->get('Model.Load'), + $container->get('Load') + ); + } + + /** + * Get The Insert Class. + * + * @param Container $container The DI container. + * + * @return Insert + * @since 3.2.0 + */ + public function getInsert(Container $container): Insert + { + return new Insert( + $container->get('Model.Upsert'), + $container->get('Insert') + ); + } + + /** + * Get The Update Class. + * + * @param Container $container The DI container. + * + * @return Update + * @since 3.2.0 + */ + public function getUpdate(Container $container): Update + { + return new Update( + $container->get('Model.Upsert'), + $container->get('Update') + ); + } + + /** + * Get The Delete Class. + * + * @param Container $container The DI container. + * + * @return Delete + * @since 3.2.0 + */ + public function getDelete(Container $container): Delete + { + return new Delete( + $container->get('Delete') + ); + } + + /** + * Get The Item Class. + * + * @param Container $container The DI container. + * + * @return Item + * @since 3.2.0 + */ + public function getItem(Container $container): Item + { + return new Item( + $container->get('Data.Load'), + $container->get('Data.Insert'), + $container->get('Data.Update'), + $container->get('Data.Delete'), + $container->get('Load') + ); + } + + /** + * Get The Items Class. + * + * @param Container $container The DI container. + * + * @return Items + * @since 3.2.0 + */ + public function getItems(Container $container): Items + { + return new Items( + $container->get('Data.Load'), + $container->get('Data.Insert'), + $container->get('Data.Update'), + $container->get('Data.Delete'), + $container->get('Load') + ); + } + + /** + * Get The Subform Class. + * + * @param Container $container The DI container. + * + * @return Subform + * @since 3.2.0 + */ + public function getSubform(Container $container): Subform + { + return new Subform( + $container->get('Data.Items') + ); + } + + /** + * Get The MultiSubform Class. + * + * @param Container $container The DI container. + * + * @return MultiSubform + * @since 3.2.0 + */ + public function getMultiSubform(Container $container): MultiSubform + { + return new MultiSubform( + $container->get('Data.Subform') + ); + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Service/Database.php b/src/VastDevelopmentMethod/Joomla/Service/Database.php new file mode 100644 index 0000000..275ddad --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Service/Database.php @@ -0,0 +1,105 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Service; + + +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use VastDevelopmentMethod\Joomla\Database\Load; +use VastDevelopmentMethod\Joomla\Database\Insert; +use VastDevelopmentMethod\Joomla\Database\Update; +use VastDevelopmentMethod\Joomla\Database\Delete; + + +/** + * Database Service Provider + * + * @since 3.2.0 + */ +class Database implements ServiceProviderInterface +{ + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * @since 3.2.0 + */ + public function register(Container $container) + { + $container->alias(Load::class, 'Load') + ->share('Load', [$this, 'getLoad'], true); + + $container->alias(Insert::class, 'Insert') + ->share('Insert', [$this, 'getInsert'], true); + + $container->alias(Update::class, 'Update') + ->share('Update', [$this, 'getUpdate'], true); + + $container->alias(Delete::class, 'Delete') + ->share('Delete', [$this, 'getDelete'], true); + } + + /** + * Get the Core Load Database + * + * @param Container $container The DI container. + * + * @return Load + * @since 3.2.0 + */ + public function getLoad(Container $container): Load + { + return new Load(); + } + + /** + * Get the Core Insert Database + * + * @param Container $container The DI container. + * + * @return Insert + * @since 3.2.0 + */ + public function getInsert(Container $container): Insert + { + return new Insert(); + } + + /** + * Get the Core Update Database + * + * @param Container $container The DI container. + * + * @return Update + * @since 3.2.0 + */ + public function getUpdate(Container $container): Update + { + return new Update(); + } + + /** + * Get the Core Delete Database + * + * @param Container $container The DI container. + * + * @return Delete + * @since 3.2.2 + */ + public function getDelete(Container $container): Delete + { + return new Delete(); + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Service/Model.php b/src/VastDevelopmentMethod/Joomla/Service/Model.php new file mode 100644 index 0000000..c0992a0 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Service/Model.php @@ -0,0 +1,75 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Service; + + +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use VastDevelopmentMethod\Joomla\Model\Load; +use VastDevelopmentMethod\Joomla\Model\Upsert; + + +/** + * Model Service Provider + * + * @since 3.2.0 + */ +class Model implements ServiceProviderInterface +{ + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * @since 3.2.0 + */ + public function register(Container $container) + { + $container->alias(Load::class, 'Model.Load') + ->share('Model.Load', [$this, 'getLoad'], true); + + $container->alias(Upsert::class, 'Model.Upsert') + ->share('Model.Upsert', [$this, 'getUpsert'], true); + } + + /** + * Get The Load Class. + * + * @param Container $container The DI container. + * + * @return Load + * @since 3.2.0 + */ + public function getLoad(Container $container): Load + { + return new Load( + $container->get('Table') + ); + } + + /** + * Get The Upsert Class. + * + * @param Container $container The DI container. + * + * @return Upsert + * @since 3.2.0 + */ + public function getUpsert(Container $container): Upsert + { + return new Upsert( + $container->get('Table') + ); + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Service/Table.php b/src/VastDevelopmentMethod/Joomla/Service/Table.php new file mode 100644 index 0000000..5b73b05 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Service/Table.php @@ -0,0 +1,73 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Service; + + +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use VastDevelopmentMethod\Joomla\Componentbuilder\Table as DataTable; +use VastDevelopmentMethod\Joomla\Componentbuilder\Table\Schema; + + +/** + * Table Service Provider + * + * @since 3.2.2 + */ +class Table implements ServiceProviderInterface +{ + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * @since 3.2.2 + */ + public function register(Container $container) + { + $container->alias(DataTable::class, 'Table') + ->share('Table', [$this, 'getTable'], true); + + $container->alias(Schema::class, 'Table.Schema') + ->share('Table.Schema', [$this, 'getSchema'], true); + } + + /** + * Get The Componentbuilder Data Table Class. + * + * @param Container $container The DI container. + * + * @return DataTable + * @since 3.2.2 + */ + public function getTable(Container $container): DataTable + { + return new DataTable(); + } + + /** + * Get The Schema Class. + * + * @param Container $container The DI container. + * + * @return Schema + * @since 3.2.2 + */ + public function getSchema(Container $container): Schema + { + return new Schema( + $container->get('Table') + ); + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Utilities/ArrayHelper.php b/src/VastDevelopmentMethod/Joomla/Utilities/ArrayHelper.php new file mode 100644 index 0000000..55b3436 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Utilities/ArrayHelper.php @@ -0,0 +1,107 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Utilities; + + +/** + * Some array tricks helper + * + * @since 3.0.9 + */ +abstract class ArrayHelper +{ + /** + * Check if have an array with a length + * + * @input array The array to check + * + * @returns int|false number of items in array on success + * + * @since 3.2.0 + */ + public static function check($array, $removeEmptyString = false) + { + if (is_array($array) && ($nr = count((array) $array)) > 0) + { + // also make sure the empty strings are removed + if ($removeEmptyString) + { + $array = array_filter($array); + + if ($array === []) + { + return false; + } + + return count($array); + } + + return $nr; + } + + return false; + } + + /** + * Merge an array of array's + * + * @input array The arrays you would like to merge + * + * @returns array|null merged array on success + * + * @since 3.0.9 + */ + public static function merge($arrays): ?array + { + if(self::check($arrays)) + { + $merged = []; + foreach ($arrays as $array) + { + if (self::check($array)) + { + $merged = array_merge($merged, $array); + } + } + return $merged; + } + return null; + } + + /** + * Check if arrays intersect + * + * @input array The first array + * @input array The second array + * + * @returns bool true if intersect else false + * + * @since 3.1.1 + */ + public static function intersect($a_array, $b_array): bool + { + // flip the second array + $b_array = array_flip($b_array); + + // loop the first array + foreach ($a_array as $v) + { + if (isset($b_array[$v])) + { + return true; + } + } + return false; + } + +} + diff --git a/src/VastDevelopmentMethod/Joomla/Utilities/Component/Helper.php b/src/VastDevelopmentMethod/Joomla/Utilities/Component/Helper.php new file mode 100644 index 0000000..f869010 --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Utilities/Component/Helper.php @@ -0,0 +1,296 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Utilities\Component; + + +use Joomla\CMS\Factory; +use Joomla\CMS\Component\ComponentHelper; +use Joomla\Input\Input; +use Joomla\Registry\Registry; +use VastDevelopmentMethod\Joomla\Utilities\String\NamespaceHelper; + + +/** + * Some component helper + * + * @since 3.0.11 + */ +abstract class Helper +{ + /** + * The current option + * + * @var string|null + * @since 3.0.11 + */ + public static ?string $option = null; + + /** + * The component manifest list cache + * + * @var array + * @since 3.2.0 + */ + public static array $manifest = []; + + /** + * The component params list cache + * + * @var Registry[] + * @since 3.0.11 + */ + protected static array $params = []; + + /** + * Gets the parameter object for the component + * + * @param string|null $option The option for the component. + * + * @return Registry A Registry object. + * @see Registry + * @since 3.0.11 + */ + public static function getParams(?string $option = null): Registry + { + // check that we have an option + if (empty($option)) + { + $option = self::getOption(); + } + + // get global value + if (!isset(self::$params[$option]) || !self::$params[$option] instanceof Registry) + { + self::$params[$option] = ComponentHelper::getParams($option); + } + + return self::$params[$option]; + } + + /** + * Set the component option + * + * @param string|null $option The option + * + * @return void + * @since 3.2.0 + */ + public static function setOption(?string $option): void + { + self::$option = $option; + } + + /** + * Get the component option + * + * @param string|null $default The default return value if none is found + * + * @return string|null A component option + * @since 3.0.11 + */ + public static function getOption(?string $default = 'empty'): ?string + { + if (empty(self::$option)) + { + // get the option from the url input + self::$option = (new Input)->getString('option', null); + } + + if (empty(self::$option)) + { + $app = Factory::getApplication(); + + // Check if the getInput method exists in the application object + if (method_exists($app, 'getInput')) + { + // get the option from the application + self::$option = $app->getInput()->getCmd('option', $default); + } + else + { + // Use the default value if getInput method does not exist + self::$option = $default; + } + } + + return self::$option; + } + + /** + * Gets the component code name + * + * @param string|null $option The option for the component. + * @param string|null $default The default return value if none is found + * + * @return string|null A component code name + * @since 3.0.11 + */ + public static function getCode(?string $option = null, ?string $default = null): ?string + { + // check that we have an option + if (empty($option)) + { + $option = self::getOption(); + } + // option with com_ + if (is_string($option) && strpos($option, 'com_') === 0) + { + return strtolower(trim(substr($option, 4))); + } + + return $default; + } + + /** + * Gets the component abstract helper class + * + * @param string|null $option The option for the component. + * @param string|null $default The default return value if none is found + * + * @return string|null A component helper name + * + * @since 3.0.11 + */ + public static function get(?string $option = null, ?string $default = null): ?string + { + // check that we have an option + // and get the code name from it + if (($code_name = self::getCode($option, null)) !== null) + { + // we build the helper class name + $helper_name = '\\' . \ucfirst($code_name) . 'Helper'; + + // check if class exist + if (class_exists($helper_name)) + { + return $helper_name; + } + + // try loading namespace + if (($namespace = self::getNamespace($option)) !== null) + { + $name = \ucfirst($code_name) . 'Helper'; + $namespace_helper = '\\' . $namespace . '\Administrator\Helper\\' . NamespaceHelper::safeSegment($name); // TODO target site or admin locations not just admin... + if (class_exists($namespace_helper)) + { + return $namespace_helper; + } + } + } + + return $default; + } + + /** + * Gets the component namespace if set + * + * @param string|null $option The option for the component. + * @param string|null $default The default return value if none is found + * + * @return string|null A component namespace + * + * @since 3.0.11 + */ + public static function getNamespace(?string $option = null): ?string + { + $manifest = self::getManifest($option); + + return $manifest->namespace ?? null; + } + + /** + * Gets the component abstract helper class + * + * @param string|null $option The option for the component. + * @param string|null $default The default return value if none is found + * + * @return object|null A component helper name + * + * @since 3.0.11 + */ + public static function getManifest(?string $option = null): ?object + { + if ($option === null + && ($option = self::getOption($option)) === null) + { + return null; + } + + // get global manifest_cache values + if (!isset(self::$manifest[$option])) + { + $db = Factory::getDbo(); + $query = $db->getQuery(true); + + $query->select($db->quoteName('manifest_cache')) + ->from($db->quoteName('#__extensions')) + ->where($db->quoteName('type') . ' = ' . $db->quote('component')) + ->where($db->quoteName('element') . ' LIKE ' . $db->quote($option)); + + $db->setQuery($query); + + try { + $manifest = $db->loadResult(); + self::$manifest[$option] = json_decode($manifest); + } catch (\Exception $e) { + // Handle the database error appropriately. + self::$manifest[$option] = null; + } + } + + return self::$manifest[$option]; + } + + /** + * Check if the helper class of this component has a method + * + * @param string $method The method name to search for + * @param string|null $option The option for the component. + * + * @return bool true if method exist + * + * @since 3.0.11 + */ + public static function methodExists(string $method, ?string $option = null): bool + { + // get the helper class + return ($helper = self::get($option, null)) !== null && + method_exists($helper, $method); + } + + /** + * Check if the helper class of this component has a method, and call it with the arguments + * + * @param string $method The method name to search for + * @param array $arguments The arguments for function. + * @param string|null $option The option for the component. + * + * @return mixed return whatever the method returns or null + * @since 3.2.0 + */ + public static function _(string $method, array $arguments = [], ?string $option = null) + { + // get the helper class + if (($helper = self::get($option, null)) !== null && + method_exists($helper, $method)) + { + // we know this is not ideal... + // so we need to move these + // functions to their own classes + return call_user_func_array([$helper, $method], $arguments); + } + + return null; + } + +} + diff --git a/src/VastDevelopmentMethod/Joomla/Utilities/ObjectHelper.php b/src/VastDevelopmentMethod/Joomla/Utilities/ObjectHelper.php new file mode 100644 index 0000000..3dddb6b --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Utilities/ObjectHelper.php @@ -0,0 +1,78 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Utilities; + + +/** + * Some object tricks + * + * @since 3.0.9 + */ +abstract class ObjectHelper +{ + /** + * Check if have an object with a length + * + * @input object The object to check + * + * @returns bool true on success + * + * @since 3.0.9 + */ + public static function check($object) + { + if (is_object($object)) + { + return count((array) $object) > 0; + } + + return false; + } + + /** + * Compare two objects for equality based on their property values. + * + * Note that this method works only for simple objects that don't + * contain any nested objects or resource references. If you need + * to compare more complex objects, you may need to use a + * more advanced method such as serialization or reflection. + * + * @param object|null $obj1 The first object to compare. + * @param object|null $obj2 The second object to compare. + * + * @return bool True if the objects have the same key-value pairs and false otherwise. + */ + public static function equal(?object $obj1, ?object $obj2): bool + { + // if any is null we return false as that means there is a none object + // we are not comparing null but objects + // but we allow null as some objects while + // not instantiate are still null + if (is_null($obj1) || is_null($obj2)) + { + return false; + } + + // Convert the objects to arrays of their property values using get_object_vars. + $array1 = get_object_vars($obj1); + $array2 = get_object_vars($obj2); + + // Compare the arrays using array_diff_assoc to detect any differences. + $diff1 = array_diff_assoc($array1, $array2); + $diff2 = array_diff_assoc($array2, $array1); + + // If the arrays have the same key-value pairs, they will have no differences, so return true. + return empty($diff1) && empty($diff2); + } + +} + diff --git a/src/VastDevelopmentMethod/Joomla/Utilities/String/NamespaceHelper.php b/src/VastDevelopmentMethod/Joomla/Utilities/String/NamespaceHelper.php new file mode 100644 index 0000000..f27ef1b --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Utilities/String/NamespaceHelper.php @@ -0,0 +1,78 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Utilities\String; + + +use VastDevelopmentMethod\Joomla\Utilities\StringHelper; + + +/** + * Control the naming of a namespace helper + * + * @since 3.0.9 + */ +abstract class NamespaceHelper +{ + /** + * Making namespace safe + * + * @param string $string The namespace string you would like to make safe + * + * @return string on success + * @since 3.0.9 + */ + public static function safe(string $string): string + { + // Remove leading and trailing backslashes + $string = trim($string, '\\'); + + // Split the string into namespace segments + $segments = explode('\\', $string); + + // make each segment safe + $segments = array_map([self::class, 'safeSegment'], $segments); + + // Join the namespace segments back together + return implode('\\', $segments); + } + + /** + * Making one namespace segment safe + * + * @param string $string The namespace segment string you would like to make safe + * + * @return string on success + * @since 3.0.9 + */ + public static function safeSegment(string $string): string + { + // Check if segment starts with a number + if (preg_match("/^\d/", $string)) + { + // Extract the starting number(s) + preg_match("/^\d+/", $string, $matches); + + if (isset($matches[0])) + { + $numberWord = StringHelper::numbers($matches[0]); + $string = str_replace($matches[0], $numberWord, $string); + } + } + + // Transliterate string TODO: look again as this makes it lowercase + // $segment = StringHelper::transliterate($segment); + + // Make sure segment only contains valid characters + return preg_replace("/[^A-Za-z0-9]/", '', $string); + } +} + diff --git a/src/VastDevelopmentMethod/Joomla/Utilities/StringHelper.php b/src/VastDevelopmentMethod/Joomla/Utilities/StringHelper.php new file mode 100644 index 0000000..98e4b8e --- /dev/null +++ b/src/VastDevelopmentMethod/Joomla/Utilities/StringHelper.php @@ -0,0 +1,416 @@ + + * @git VDM Data Library + * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace VastDevelopmentMethod\Joomla\Utilities; + + +use Joomla\Filter\InputFilter; +use Joomla\CMS\Language\Language; +use VastDevelopmentMethod\Joomla\Utilities\Component\Helper; + + +/** + * Some string tricks + * + * @since 3.0.9 + */ +abstract class StringHelper +{ + /** + * The Main Active Language + * + * @var string + * + * @since 3.0.9 + */ + public static $langTag; + + /** + * Check if we have a string with a length + * + * @input string $string The string to check + * + * @returns bool true on success + * + * @since 3.0.9 + */ + public static function check($string): bool + { + return is_string($string) && strlen($string) > 0; + } + + /** + * Shorten a string + * + * @input string The sting that you would like to shorten + * + * @returns string on success + * + * @since 3.2.0 + */ + public static function shorten($string, $length = 40, $addTip = true) + { + if (self::check($string)) + { + $initial = strlen((string) $string); + $words = preg_split('/([\s\n\r]+)/', (string) $string, -1, PREG_SPLIT_DELIM_CAPTURE); + $words_count = count((array)$words); + + $word_length = 0; + $last_word = 0; + for (; $last_word < $words_count; ++$last_word) + { + $word_length += strlen($words[$last_word]); + if ($word_length > $length) + { + break; + } + } + + $newString = implode(array_slice($words, 0, $last_word)); + $final = strlen($newString); + if ($initial !== $final && $addTip) + { + $title = self::shorten($string, 400 , false); + return '' . trim($newString) . '...'; + } + elseif ($initial !== $final && !$addTip) + { + return trim($newString) . '...'; + } + } + return $string; + } + + /** + * Making strings safe (various ways) + * + * @input string The you would like to make safe + * + * @returns string on success + * + * @since 3.0.9 + */ + public static function safe($string, $type = 'L', $spacer = '_', $replaceNumbers = true, $keepOnlyCharacters = true) + { + if ($replaceNumbers === true) + { + // remove all numbers and replace with English text version (works well only up to millions) + $string = self::numbers($string); + } + // 0nly continue if we have a string + if (self::check($string)) + { + // create file name without the extension that is safe + if ($type === 'filename') + { + // make sure VDM is not in the string + $string = str_replace('VDM', 'vDm', (string) $string); + // Remove anything which isn't a word, whitespace, number + // or any of the following caracters -_() + // If you don't need to handle multi-byte characters + // you can use preg_replace rather than mb_ereg_replace + // Thanks @Ɓukasz Rysiak! + // $string = mb_ereg_replace("([^\w\s\d\-_\(\)])", '', $string); + $string = preg_replace("([^\w\s\d\-_\(\)])", '', $string); + + // http://stackoverflow.com/a/2021729/1429677 + return preg_replace('/\s+/', ' ', (string) $string); + } + // remove all other characters + $string = trim((string) $string); + $string = preg_replace('/'.$spacer.'+/', ' ', $string); + $string = preg_replace('/\s+/', ' ', $string); + // Transliterate string + $string = self::transliterate($string); + // remove all and keep only characters + if ($keepOnlyCharacters) + { + $string = preg_replace("/[^A-Za-z ]/", '', (string) $string); + } + // keep both numbers and characters + else + { + $string = preg_replace("/[^A-Za-z0-9 ]/", '', (string) $string); + } + // select final adaptations + if ($type === 'L' || $type === 'strtolower') + { + // replace white space with underscore + $string = preg_replace('/\s+/', (string) $spacer, (string) $string); + // default is to return lower + return strtolower($string); + } + elseif ($type === 'W') + { + // return a string with all first letter of each word uppercase(no underscore) + return ucwords(strtolower($string)); + } + elseif ($type === 'w' || $type === 'word') + { + // return a string with all lowercase(no underscore) + return strtolower($string); + } + elseif ($type === 'Ww' || $type === 'Word') + { + // return a string with first letter of the first word uppercase and all the rest lowercase(no underscore) + return ucfirst(strtolower($string)); + } + elseif ($type === 'WW' || $type === 'WORD') + { + // return a string with all the uppercase(no underscore) + return strtoupper($string); + } + elseif ($type === 'U' || $type === 'strtoupper') + { + // replace white space with underscore + $string = preg_replace('/\s+/', (string) $spacer, $string); + // return all upper + return strtoupper($string); + } + elseif ($type === 'F' || $type === 'ucfirst') + { + // replace white space with underscore + $string = preg_replace('/\s+/', (string) $spacer, $string); + // return with first character to upper + return ucfirst(strtolower($string)); + } + elseif ($type === 'cA' || $type === 'cAmel' || $type === 'camelcase') + { + // convert all words to first letter uppercase + $string = ucwords(strtolower($string)); + // remove white space + $string = preg_replace('/\s+/', '', $string); + // now return first letter lowercase + return lcfirst($string); + } + // return string + return $string; + } + // not a string + return ''; + } + + /** + * Convert none English strings to code usable string + * + * @input an string + * + * @returns a string + * + * @since 3.0.9 + */ + public static function transliterate($string) + { + // set tag only once + if (!self::check(self::$langTag)) + { + // get global value + self::$langTag = Helper::getParams()->get('language', 'en-GB'); + } + + // Transliterate on the language requested + $lang = Language::getInstance(self::$langTag); + + return $lang->transliterate($string); + } + + /** + * make sure a string is HTML save + * + * @input an html string + * + * @returns a string + * + * @since 3.0.9 + */ + public static function html($var, $charset = 'UTF-8', $shorten = false, $length = 40, $addTip = true) + { + if (self::check($var)) + { + $filter = new InputFilter(); + $string = $filter->clean( + html_entity_decode( + htmlentities( + (string) $var, + ENT_COMPAT, + $charset + ) + ), + 'HTML' + ); + if ($shorten) + { + return self::shorten($string, $length, $addTip); + } + return $string; + } + else + { + return ''; + } + } + + /** + * Convert all int in a string to an English word string + * + * @input an string with numbers + * + * @returns a string + * + * @since 3.0.9 + */ + public static function numbers($string) + { + // set numbers array + $numbers = []; + $search_replace= []; + + // first get all numbers + preg_match_all('!\d+!', (string) $string, $numbers); + + // check if we have any numbers + if (isset($numbers[0]) && ArrayHelper::check($numbers[0])) + { + foreach ($numbers[0] as $number) + { + $search_replace[$number] = self::number((int)$number); + } + + // now replace numbers in string + $string = str_replace(array_keys($search_replace), array_values($search_replace), (string) $string); + + // check if we missed any, strange if we did. + return self::numbers($string); + } + + // return the string with no numbers remaining. + return $string; + } + + /** + * Convert an integer into an English word string + * Thanks to Tom Nicholson + * + * @input an int + * @returns a string + * + * @since 3.0.9 + */ + public static function number($x) + { + $nwords = array( "zero", "one", "two", "three", "four", "five", "six", "seven", + "eight", "nine", "ten", "eleven", "twelve", "thirteen", + "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", + "nineteen", "twenty", 30 => "thirty", 40 => "forty", + 50 => "fifty", 60 => "sixty", 70 => "seventy", 80 => "eighty", + 90 => "ninety" ); + + if(!is_numeric($x)) + { + $w = $x; + } + elseif(fmod($x, 1) != 0) + { + $w = $x; + } + else + { + if($x < 0) + { + $w = 'minus '; + $x = -$x; + } + else + { + $w = ''; + // ... now $x is a non-negative integer. + } + + if($x < 21) // 0 to 20 + { + $w .= $nwords[$x]; + } + elseif($x < 100) // 21 to 99 + { + $w .= $nwords[10 * floor($x/10)]; + $r = fmod($x, 10); + if($r > 0) + { + $w .= ' ' . $nwords[$r]; + } + } + elseif($x < 1000) // 100 to 999 + { + $w .= $nwords[floor($x/100)] .' hundred'; + $r = fmod($x, 100); + if($r > 0) + { + $w .= ' and '. self::number($r); + } + } + elseif($x < 1000000) // 1000 to 999999 + { + $w .= self::number(floor($x/1000)) .' thousand'; + $r = fmod($x, 1000); + if($r > 0) + { + $w .= ' '; + if($r < 100) + { + $w .= 'and '; + } + $w .= self::number($r); + } + } + else // millions + { + $w .= self::number(floor($x/1000000)) .' million'; + $r = fmod($x, 1000000); + if($r > 0) + { + $w .= ' '; + if($r < 100) + { + $w .= 'and '; + } + $w .= self::number($r); + } + } + } + return $w; + } + + /** + * Random Key + * + * @input int $size The size of the random string + * + * @returns a string + * @since 3.0.9 + */ + public static function random(int $size): string + { + $bag = "abcefghijknopqrstuwxyzABCDDEFGHIJKLLMMNOPQRSTUVVWXYZabcddefghijkllmmnopqrstuvvwxyzABCEFGHIJKNOPQRSTUWXYZ"; + $key = []; + $bagsize = strlen($bag) - 1; + + for ($i = 0; $i < $size; $i++) + { + $get = rand(0, $bagsize); + $key[] = $bag[$get]; + } + + return implode($key); + } + +} +