Update 2024-09-02 22:44:12

This commit is contained in:
Robot 2024-09-02 22:45:00 +02:00
parent 4a12bd8374
commit 9ca083305b
Signed by untrusted user: Robot
GPG Key ID: 14DECD44E7E1BB95
33 changed files with 2837 additions and 207 deletions

View File

@ -47,6 +47,8 @@ This repository contains an index (see below) of all the approved powers within
- **final class Items** | [Details](src/21bca8a4-5b28-41c4-843e-8097f0ba7cca) | [Code](src/21bca8a4-5b28-41c4-843e-8097f0ba7cca/code.php) | [Settings](src/21bca8a4-5b28-41c4-843e-8097f0ba7cca/settings.json) | SPK: `Super---21bca8a4_5b28_41c4_843e_8097f0ba7cca---Power` - **final class Items** | [Details](src/21bca8a4-5b28-41c4-843e-8097f0ba7cca) | [Code](src/21bca8a4-5b28-41c4-843e-8097f0ba7cca/code.php) | [Settings](src/21bca8a4-5b28-41c4-843e-8097f0ba7cca/settings.json) | SPK: `Super---21bca8a4_5b28_41c4_843e_8097f0ba7cca---Power`
- **final class MultiSubform** | [Details](src/e0198c3f-777a-4a0b-87b7-e6a198afc8f9) | [Code](src/e0198c3f-777a-4a0b-87b7-e6a198afc8f9/code.php) | [Settings](src/e0198c3f-777a-4a0b-87b7-e6a198afc8f9/settings.json) | SPK: `Super---e0198c3f_777a_4a0b_87b7_e6a198afc8f9---Power` - **final class MultiSubform** | [Details](src/e0198c3f-777a-4a0b-87b7-e6a198afc8f9) | [Code](src/e0198c3f-777a-4a0b-87b7-e6a198afc8f9/code.php) | [Settings](src/e0198c3f-777a-4a0b-87b7-e6a198afc8f9/settings.json) | SPK: `Super---e0198c3f_777a_4a0b_87b7_e6a198afc8f9---Power`
- **final class Subform** | [Details](src/85785701-07b2-4f81-bc1e-0f423700c254) | [Code](src/85785701-07b2-4f81-bc1e-0f423700c254/code.php) | [Settings](src/85785701-07b2-4f81-bc1e-0f423700c254/settings.json) | SPK: `Super---85785701_07b2_4f81_bc1e_0f423700c254---Power` - **final class Subform** | [Details](src/85785701-07b2-4f81-bc1e-0f423700c254) | [Code](src/85785701-07b2-4f81-bc1e-0f423700c254/code.php) | [Settings](src/85785701-07b2-4f81-bc1e-0f423700c254/settings.json) | SPK: `Super---85785701_07b2_4f81_bc1e_0f423700c254---Power`
- **final class UsersSubform** | [Details](src/46b98346-ec98-42b3-a393-96c7d1282b1c) | [Code](src/46b98346-ec98-42b3-a393-96c7d1282b1c/code.php) | [Settings](src/46b98346-ec98-42b3-a393-96c7d1282b1c/settings.json) | SPK: `Super---46b98346_ec98_42b3_a393_96c7d1282b1c---Power`
- **trait Guid** | [Details](src/5acded67-0e3d-4c6b-a6ea-b533b076de0c) | [Code](src/5acded67-0e3d-4c6b-a6ea-b533b076de0c/code.php) | [Settings](src/5acded67-0e3d-4c6b-a6ea-b533b076de0c/settings.json) | SPK: `Super---5acded67_0e3d_4c6b_a6ea_b533b076de0c---Power`
- **Namespace**: [VDM\Joomla\Database](#vdm-joomla-database) - **Namespace**: [VDM\Joomla\Database](#vdm-joomla-database)
- **final class Delete** | [Details](src/92291f1f-f248-4ec0-9f2a-3d47c49eeac1) | [Code](src/92291f1f-f248-4ec0-9f2a-3d47c49eeac1/code.php) | [Settings](src/92291f1f-f248-4ec0-9f2a-3d47c49eeac1/settings.json) | SPK: `Super---92291f1f_f248_4ec0_9f2a_3d47c49eeac1---Power` - **final class Delete** | [Details](src/92291f1f-f248-4ec0-9f2a-3d47c49eeac1) | [Code](src/92291f1f-f248-4ec0-9f2a-3d47c49eeac1/code.php) | [Settings](src/92291f1f-f248-4ec0-9f2a-3d47c49eeac1/settings.json) | SPK: `Super---92291f1f_f248_4ec0_9f2a_3d47c49eeac1---Power`
@ -103,6 +105,9 @@ This repository contains an index (see below) of all the approved powers within
- **final class Schema** | [Details](src/b3d2ec33-76d4-4c3b-bb2c-86ac14a221ce) | [Code](src/b3d2ec33-76d4-4c3b-bb2c-86ac14a221ce/code.php) | [Settings](src/b3d2ec33-76d4-4c3b-bb2c-86ac14a221ce/settings.json) | SPK: `Super---b3d2ec33_76d4_4c3b_bb2c_86ac14a221ce---Power` - **final class Schema** | [Details](src/b3d2ec33-76d4-4c3b-bb2c-86ac14a221ce) | [Code](src/b3d2ec33-76d4-4c3b-bb2c-86ac14a221ce/code.php) | [Settings](src/b3d2ec33-76d4-4c3b-bb2c-86ac14a221ce/settings.json) | SPK: `Super---b3d2ec33_76d4_4c3b_bb2c_86ac14a221ce---Power`
- **final class SchemaChecker** | [Details](src/709d7294-9a43-46e2-b64e-d16a16f0eab1) | [Code](src/709d7294-9a43-46e2-b64e-d16a16f0eab1/code.php) | [Settings](src/709d7294-9a43-46e2-b64e-d16a16f0eab1/settings.json) | SPK: `Super---709d7294_9a43_46e2_b64e_d16a16f0eab1---Power` - **final class SchemaChecker** | [Details](src/709d7294-9a43-46e2-b64e-d16a16f0eab1) | [Code](src/709d7294-9a43-46e2-b64e-d16a16f0eab1/code.php) | [Settings](src/709d7294-9a43-46e2-b64e-d16a16f0eab1/settings.json) | SPK: `Super---709d7294_9a43_46e2_b64e_d16a16f0eab1---Power`
- **Namespace**: [VDM\Joomla\Componentbuilder\Utilities](#vdm-joomla-componentbuilder-utilities)
- **abstract class UserHelper** | [Details](src/7832a726-87b6-4e95-887e-7b725d3fab8f) | [Code](src/7832a726-87b6-4e95-887e-7b725d3fab8f/code.php) | [Settings](src/7832a726-87b6-4e95-887e-7b725d3fab8f/settings.json) | SPK: `Super---7832a726_87b6_4e95_887e_7b725d3fab8f---Power`
- **Namespace**: [VDM\Joomla\Data\Action](#vdm-joomla-data-action) - **Namespace**: [VDM\Joomla\Data\Action](#vdm-joomla-data-action)
- **class Delete** | [Details](src/3fc72954-a303-4cac-b53c-554be38b85e7) | [Code](src/3fc72954-a303-4cac-b53c-554be38b85e7/code.php) | [Settings](src/3fc72954-a303-4cac-b53c-554be38b85e7/settings.json) | SPK: `Super---3fc72954_a303_4cac_b53c_554be38b85e7---Power` - **class Delete** | [Details](src/3fc72954-a303-4cac-b53c-554be38b85e7) | [Code](src/3fc72954-a303-4cac-b53c-554be38b85e7/code.php) | [Settings](src/3fc72954-a303-4cac-b53c-554be38b85e7/settings.json) | SPK: `Super---3fc72954_a303_4cac_b53c_554be38b85e7---Power`
@ -112,6 +117,7 @@ This repository contains an index (see below) of all the approved powers within
- **Namespace**: [VDM\Joomla\Interfaces\Data](#vdm-joomla-interfaces-data) - **Namespace**: [VDM\Joomla\Interfaces\Data](#vdm-joomla-interfaces-data)
- **interface DeleteInterface** | [Details](src/d8f9ba53-c490-4e8b-8e9f-6757224e069c) | [Code](src/d8f9ba53-c490-4e8b-8e9f-6757224e069c/code.php) | [Settings](src/d8f9ba53-c490-4e8b-8e9f-6757224e069c/settings.json) | SPK: `Super---d8f9ba53_c490_4e8b_8e9f_6757224e069c---Power` - **interface DeleteInterface** | [Details](src/d8f9ba53-c490-4e8b-8e9f-6757224e069c) | [Code](src/d8f9ba53-c490-4e8b-8e9f-6757224e069c/code.php) | [Settings](src/d8f9ba53-c490-4e8b-8e9f-6757224e069c/settings.json) | SPK: `Super---d8f9ba53_c490_4e8b_8e9f_6757224e069c---Power`
- **interface GuidInterface** | [Details](src/576685fd-263c-46bb-9fdc-1f5eb234cbb6) | [Code](src/576685fd-263c-46bb-9fdc-1f5eb234cbb6/code.php) | [Settings](src/576685fd-263c-46bb-9fdc-1f5eb234cbb6/settings.json) | SPK: `Super---576685fd_263c_46bb_9fdc_1f5eb234cbb6---Power`
- **interface InsertInterface** | [Details](src/03bbc8d5-86e8-4d2f-ae5f-0d44a4f7af13) | [Code](src/03bbc8d5-86e8-4d2f-ae5f-0d44a4f7af13/code.php) | [Settings](src/03bbc8d5-86e8-4d2f-ae5f-0d44a4f7af13/settings.json) | SPK: `Super---03bbc8d5_86e8_4d2f_ae5f_0d44a4f7af13---Power` - **interface InsertInterface** | [Details](src/03bbc8d5-86e8-4d2f-ae5f-0d44a4f7af13) | [Code](src/03bbc8d5-86e8-4d2f-ae5f-0d44a4f7af13/code.php) | [Settings](src/03bbc8d5-86e8-4d2f-ae5f-0d44a4f7af13/settings.json) | SPK: `Super---03bbc8d5_86e8_4d2f_ae5f_0d44a4f7af13---Power`
- **interface ItemInterface** | [Details](src/05744dd3-4030-4cf8-8dda-a93ab809b473) | [Code](src/05744dd3-4030-4cf8-8dda-a93ab809b473/code.php) | [Settings](src/05744dd3-4030-4cf8-8dda-a93ab809b473/settings.json) | SPK: `Super---05744dd3_4030_4cf8_8dda_a93ab809b473---Power` - **interface ItemInterface** | [Details](src/05744dd3-4030-4cf8-8dda-a93ab809b473) | [Code](src/05744dd3-4030-4cf8-8dda-a93ab809b473/code.php) | [Settings](src/05744dd3-4030-4cf8-8dda-a93ab809b473/settings.json) | SPK: `Super---05744dd3_4030_4cf8_8dda_a93ab809b473---Power`
- **interface ItemsInterface** | [Details](src/7212e4db-371f-4cfd-8122-32e9bb100d83) | [Code](src/7212e4db-371f-4cfd-8122-32e9bb100d83/code.php) | [Settings](src/7212e4db-371f-4cfd-8122-32e9bb100d83/settings.json) | SPK: `Super---7212e4db_371f_4cfd_8122_32e9bb100d83---Power` - **interface ItemsInterface** | [Details](src/7212e4db-371f-4cfd-8122-32e9bb100d83) | [Code](src/7212e4db-371f-4cfd-8122-32e9bb100d83/code.php) | [Settings](src/7212e4db-371f-4cfd-8122-32e9bb100d83/settings.json) | SPK: `Super---7212e4db_371f_4cfd_8122_32e9bb100d83---Power`

View File

@ -0,0 +1,172 @@
```
██████╗ ██████╗ ██╗ ██╗███████╗██████╗
██╔══██╗██╔═══██╗██║ ██║██╔════╝██╔══██╗
██████╔╝██║ ██║██║ █╗ ██║█████╗ ██████╔╝
██╔═══╝ ██║ ██║██║███╗██║██╔══╝ ██╔══██╗
██║ ╚██████╔╝╚███╔███╔╝███████╗██║ ██║
╚═╝ ╚═════╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═╝
```
# final class UsersSubform (Details)
> namespace: **VDM\Joomla\Data**
```uml
@startuml
class UsersSubform << (F,LightGreen) >> #RoyalBlue {
# Items $items
# string $table
# array $user
+ __construct(Items $items, ?string $table = null)
+ table(string $table) : self
+ get(string $linkValue, string $linkKey, ...) : ?array
+ set(mixed $items, string $indexKey, ...) : bool
+ getTable() : string
# initializeUserProperties() : void
- purge(array $items, string $indexKey, ...) : void
- getUsersDetails(array $items) : array
- getUserDetails(array $item) : void
- converter(array $items, array $keySet, ...) : array
- process(mixed $items, string $indexKey, ...) : array
- setUserDetails(array $item) : int
}
note right of UsersSubform::__construct
Constructor.
since: 3.2.2
end note
note left of UsersSubform::table
Set the current active table
since: 3.2.2
return: self
end note
note right of UsersSubform::get
Get a subform items
since: 3.2.2
return: ?array
arguments:
string $linkValue
string $linkKey
string $field
array $get
end note
note left of UsersSubform::set
Set a subform items
since: 3.2.2
return: bool
arguments:
mixed $items
string $indexKey
string $linkKey
string $linkValue
end note
note right of UsersSubform::getTable
Get the current active table
since: 3.2.2
return: string
end note
note left of UsersSubform::initializeUserProperties
Initializes the user properties.
since: 5.0.2
return: void
end note
note right of UsersSubform::purge
Purge all items no longer in subform
since: 3.2.2
return: void
arguments:
array $items
string $indexKey
string $linkKey
string $linkValue
end note
note left of UsersSubform::getUsersDetails
Get the users details found in the user table.
since: 5.0.2
return: array
end note
note right of UsersSubform::getUserDetails
Get the user details found in the user table.
since: 5.0.2
return: void
end note
note left of UsersSubform::converter
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.
since: 3.2.2
return: array
arguments:
array $items
array $keySet
string $field
end note
note right of UsersSubform::process
Processes an array of arrays based on the specified key.
since: 3.2.2
return: array
arguments:
mixed $items
string $indexKey
string $linkKey
string $linkValue
end note
note left of UsersSubform::setUserDetails
Set the user details.
since: 5.0.2
return: int
end note
@enduml
```
The Power feature in JCB allows you to write PHP classes and their implementations, making it easy to include them in your Joomla project. JCB handles linking, autoloading, namespacing, and folder structure creation for you.
By using the SPK (Super Power Key) in your custom code (replacing the class name in your code with the SPK), JCB will automatically pull the power from the repository into your project. This makes it available in your JCB instance, allowing you to edit it and include the class in your generated Joomla component.
JCB uses placeholders like [[[`NamespacePrefix`]]] and [[[`ComponentNamespace`]]] in namespacing to prevent collisions and improve reusability across different JCB systems. You can also set the **JCB powers path** globally or per component under the **Dynamic Integration** tab, providing flexibility and easy maintainability.
To add this specific Power to your project in JCB:
> simply use this SPK
```
Super---46b98346_ec98_42b3_a393_96c7d1282b1c---Power
```
> remember to replace the `---` with `___` to activate this Power in your code
---
```
██╗ ██████╗██████╗
██║██╔════╝██╔══██╗
██║██║ ██████╔╝
██ ██║██║ ██╔══██╗
╚█████╔╝╚██████╗██████╔╝
╚════╝ ╚═════╝╚═════╝
```
> Build with [Joomla Component Builder](https://git.vdm.dev/joomla/Component-Builder)

View File

@ -0,0 +1,403 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 3rd September, 2020
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Data;
use Joomla\CMS\Factory;
use VDM\Joomla\Interfaces\Data\ItemsInterface as Items;
use VDM\Joomla\Data\Guid;
use VDM\Joomla\Componentbuilder\Utilities\UserHelper;
use VDM\Joomla\Interfaces\Data\GuidInterface;
use VDM\Joomla\Interfaces\Data\SubformInterface;
/**
* CRUD the user data of any sub-form to another view (table)
*
* @since 5.0.2
*/
final class UsersSubform implements GuidInterface, SubformInterface
{
/**
* The Globally Unique Identifier.
*
* @since 5.0.2
*/
use Guid;
/**
* The Items Class.
*
* @var Items
* @since 3.2.2
*/
protected Items $items;
/**
* Table Name
*
* @var string
* @since 3.2.1
*/
protected string $table;
/**
* The user properties
*
* @var array
* @since 5.0.2
*/
protected array $user;
/**
* Constructor.
*
* @param Items $items The items 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;
}
// Retrieve the user properties
$this->initializeUserProperties();
}
/**
* 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(
$this->getUsersDetails($items),
$get,
$field
);
}
return null;
}
/**
* Set a subform items
*
* @param mixed $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(mixed $items, string $indexKey, string $linkKey, string $linkValue): bool
{
$items = $this->process($items, $indexKey, $linkKey, $linkValue);
$this->purge($items, $indexKey, $linkKey, $linkValue);
if (empty($items))
{
return true; // nothing to set (already purged)
}
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;
}
/**
* Initializes the user properties.
*
* @return void
* @since 5.0.2
*/
protected function initializeUserProperties(): void
{
$user = UserHelper::getUserById(0);
// Populate user properties array excluding the 'id'
foreach (get_object_vars($user) as $property => $value)
{
if ($property !== 'id')
{
$this->user[$property] = $property;
}
}
$this->user['password2'] = 'password2';
}
/**
* 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)
{
// Check if the items array is empty
if (empty($items))
{
// Set activeIndexValues to an empty array if items is empty
$activeIndexValues = [];
}
else
{
// 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);
// $this->deleteUsers($inactiveIndexValues); (soon)
}
}
}
/**
* Get the users details found in the user table.
*
* @param array $items Array of objects or arrays to be filtered.
*
* @return array
* @since 5.0.2
*/
private function getUsersDetails(array $items): array
{
foreach ($items as $index => &$item)
{
$item = (array) $item;
$this->getUserDetails($item);
}
return $items;
}
/**
* Get the user details found in the user table.
*
* @param array $item The user map array
*
* @return void
* @since 5.0.2
*/
private function getUserDetails(array &$item): void
{
// Validate the user_id and ensure it is numeric and greater than 0
if (empty($item['user_id']) || !is_numeric($item['user_id']) || $item['user_id'] <= 0)
{
return;
}
// Retrieve the user by ID
$user = UserHelper::getUserById((int)$item['user_id']);
// Verify if the user exists and the ID matches
if ($user && $user->id === (int) $item['user_id'])
{
// Iterate over public properties of the user object
foreach (get_object_vars($user) as $property => $value)
{
// Avoid overwriting the id in the item
if ($property !== 'id')
{
$item[$property] = $value;
}
}
}
}
/**
* 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 mixed $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($items, string $indexKey, string $linkKey, string $linkValue): array
{
$items = is_array($items) ? $items : [];
foreach ($items as &$item)
{
$value = $item[$indexKey] ?? '';
switch ($indexKey) {
case 'guid':
if (empty($value))
{
// set INDEX
$item[$indexKey] = $this->getGuid($indexKey);
}
break;
case 'id':
if (empty($value))
{
$item[$indexKey] = 0;
}
break;
case 'user_id':
$item[$indexKey] = $this->setUserDetails($item);
break;
default:
// No action for other keys if empty
break;
}
// set LINK
$item[$linkKey] = $linkValue;
}
return array_values($items);
}
/**
* Set the user details.
*
* @param array $item The user details
*
* @return int
* @since 5.0.2
*/
private function setUserDetails(array &$item): int
{
$user = [];
// now load the user ID
if (isset($item['user_id']) && is_numeric($item['user_id']) && $item['user_id'] > 0)
{
$user['id'] = (int) $item['user_id'];
}
foreach ($this->user as $property)
{
if (isset($item[$property]))
{
$user[$property] = $item[$property];
unset($item[$property]);
}
}
try {
return UserHelper::save($user);
} catch(\Exception $e) {
Factory::getApplication()->enqueueMessage($e->getMessage(), 'warning');
}
return 0;
}
}

View File

@ -0,0 +1,372 @@
/**
* The Globally Unique Identifier.
*
* @since 5.0.2
*/
use Guid;
/**
* The Items Class.
*
* @var Items
* @since 3.2.2
*/
protected Items $items;
/**
* Table Name
*
* @var string
* @since 3.2.1
*/
protected string $table;
/**
* The user properties
*
* @var array
* @since 5.0.2
*/
protected array $user;
/**
* Constructor.
*
* @param Items $items The items 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;
}
// Retrieve the user properties
$this->initializeUserProperties();
}
/**
* 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(
$this->getUsersDetails($items),
$get,
$field
);
}
return null;
}
/**
* Set a subform items
*
* @param mixed $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(mixed $items, string $indexKey, string $linkKey, string $linkValue): bool
{
$items = $this->process($items, $indexKey, $linkKey, $linkValue);
$this->purge($items, $indexKey, $linkKey, $linkValue);
if (empty($items))
{
return true; // nothing to set (already purged)
}
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;
}
/**
* Initializes the user properties.
*
* @return void
* @since 5.0.2
*/
protected function initializeUserProperties(): void
{
$user = UserHelper::getUserById(0);
// Populate user properties array excluding the 'id'
foreach (get_object_vars($user) as $property => $value)
{
if ($property !== 'id')
{
$this->user[$property] = $property;
}
}
$this->user['password2'] = 'password2';
}
/**
* 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)
{
// Check if the items array is empty
if (empty($items))
{
// Set activeIndexValues to an empty array if items is empty
$activeIndexValues = [];
}
else
{
// 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);
// $this->deleteUsers($inactiveIndexValues); (soon)
}
}
}
/**
* Get the users details found in the user table.
*
* @param array $items Array of objects or arrays to be filtered.
*
* @return array
* @since 5.0.2
*/
private function getUsersDetails(array $items): array
{
foreach ($items as $index => &$item)
{
$item = (array) $item;
$this->getUserDetails($item);
}
return $items;
}
/**
* Get the user details found in the user table.
*
* @param array $item The user map array
*
* @return void
* @since 5.0.2
*/
private function getUserDetails(array &$item): void
{
// Validate the user_id and ensure it is numeric and greater than 0
if (empty($item['user_id']) || !is_numeric($item['user_id']) || $item['user_id'] <= 0)
{
return;
}
// Retrieve the user by ID
$user = UserHelper::getUserById((int)$item['user_id']);
// Verify if the user exists and the ID matches
if ($user && $user->id === (int) $item['user_id'])
{
// Iterate over public properties of the user object
foreach (get_object_vars($user) as $property => $value)
{
// Avoid overwriting the id in the item
if ($property !== 'id')
{
$item[$property] = $value;
}
}
}
}
/**
* 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 mixed $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($items, string $indexKey, string $linkKey, string $linkValue): array
{
$items = is_array($items) ? $items : [];
foreach ($items as &$item)
{
$value = $item[$indexKey] ?? '';
switch ($indexKey) {
case 'guid':
if (empty($value))
{
// set INDEX
$item[$indexKey] = $this->getGuid($indexKey);
}
break;
case 'id':
if (empty($value))
{
$item[$indexKey] = 0;
}
break;
case 'user_id':
$item[$indexKey] = $this->setUserDetails($item);
break;
default:
// No action for other keys if empty
break;
}
// set LINK
$item[$linkKey] = $linkValue;
}
return array_values($items);
}
/**
* Set the user details.
*
* @param array $item The user details
*
* @return int
* @since 5.0.2
*/
private function setUserDetails(array &$item): int
{
$user = [];
// now load the user ID
if (isset($item['user_id']) && is_numeric($item['user_id']) && $item['user_id'] > 0)
{
$user['id'] = (int) $item['user_id'];
}
foreach ($this->user as $property)
{
if (isset($item[$property]))
{
$user[$property] = $item[$property];
unset($item[$property]);
}
}
try {
return UserHelper::save($user);
} catch(\Exception $e) {
Factory::getApplication()->enqueueMessage($e->getMessage(), 'warning');
}
return 0;
}

View File

@ -0,0 +1,35 @@
{
"add_head": "1",
"add_licensing_template": "2",
"extends": "",
"guid": "46b98346-ec98-42b3-a393-96c7d1282b1c",
"implements": [
"576685fd-263c-46bb-9fdc-1f5eb234cbb6",
"34959721-415b-4b5e-8002-3d1fc84b3b2b"
],
"load_selection": null,
"name": "UsersSubform",
"power_version": "1.0.0",
"system_name": "VDM.Data.UsersSubform",
"type": "final class",
"use_selection": {
"use_selection0": {
"use": "7212e4db-371f-4cfd-8122-32e9bb100d83",
"as": "Items"
},
"use_selection1": {
"use": "5acded67-0e3d-4c6b-a6ea-b533b076de0c",
"as": "default"
},
"use_selection2": {
"use": "7832a726-87b6-4e95-887e-7b725d3fab8f",
"as": "default"
}
},
"extendsinterfaces": null,
"namespace": "[[[NamespacePrefix]]]\\Joomla\\Data.UsersSubform",
"description": "CRUD the user data of any sub-form to another view (table)\r\n\r\n@since 5.0.2",
"licensing_template": "\/**\r\n * @package Joomla.Component.Builder\r\n *\r\n * @created 3rd September, 2020\r\n * @author Llewellyn van der Merwe <https:\/\/dev.vdm.io>\r\n * @git Joomla Component Builder <https:\/\/git.vdm.dev\/joomla\/Component-Builder>\r\n * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.\r\n * @license GNU General Public License version 2 or later; see LICENSE.txt\r\n *\/\r\n",
"head": "use Joomla\\CMS\\Factory;",
"composer": ""
}

View File

@ -0,0 +1,54 @@
```
██████╗ ██████╗ ██╗ ██╗███████╗██████╗
██╔══██╗██╔═══██╗██║ ██║██╔════╝██╔══██╗
██████╔╝██║ ██║██║ █╗ ██║█████╗ ██████╔╝
██╔═══╝ ██║ ██║██║███╗██║██╔══╝ ██╔══██╗
██║ ╚██████╔╝╚███╔███╔╝███████╗██║ ██║
╚═╝ ╚═════╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═╝
```
# interface GuidInterface (Details)
> namespace: **VDM\Joomla\Interfaces\Data**
```uml
@startuml
interface GuidInterface #Lavender {
+ getGuid(string $key) : string
}
note right of GuidInterface::getGuid
Returns a GUIDv4 string.
This function uses the best cryptographically secure method
available on the platform with a fallback to an older, less secure version.
since: 5.0.2
return: string
end note
@enduml
```
The Power feature in JCB allows you to write PHP classes and their implementations, making it easy to include them in your Joomla project. JCB handles linking, autoloading, namespacing, and folder structure creation for you.
By using the SPK (Super Power Key) in your custom code (replacing the class name in your code with the SPK), JCB will automatically pull the power from the repository into your project. This makes it available in your JCB instance, allowing you to edit it and include the class in your generated Joomla component.
JCB uses placeholders like [[[`NamespacePrefix`]]] and [[[`ComponentNamespace`]]] in namespacing to prevent collisions and improve reusability across different JCB systems. You can also set the **JCB powers path** globally or per component under the **Dynamic Integration** tab, providing flexibility and easy maintainability.
To add this specific Power to your project in JCB:
> simply use this SPK
```
Super---576685fd_263c_46bb_9fdc_1f5eb234cbb6---Power
```
> remember to replace the `---` with `___` to activate this Power in your code
---
```
██╗ ██████╗██████╗
██║██╔════╝██╔══██╗
██║██║ ██████╔╝
██ ██║██║ ██╔══██╗
╚█████╔╝╚██████╗██████╔╝
╚════╝ ╚═════╝╚═════╝
```
> Build with [Joomla Component Builder](https://git.vdm.dev/joomla/Component-Builder)

View File

@ -0,0 +1,36 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 4th September, 2022
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Interfaces\Data;
/**
* Globally Unique Identifier Interface
*
* @since 5.0.2
*/
interface GuidInterface
{
/**
* Returns a GUIDv4 string.
*
* This function uses the best cryptographically secure method
* available on the platform with a fallback to an older, less secure version.
*
* @param string $key The key to check and modify values.
*
* @return string A GUIDv4 string.
*
* @since 5.0.2
*/
public function getGuid(string $key): string;
}

View File

@ -0,0 +1,13 @@
/**
* Returns a GUIDv4 string.
*
* This function uses the best cryptographically secure method
* available on the platform with a fallback to an older, less secure version.
*
* @param string $key The key to check and modify values.
*
* @return string A GUIDv4 string.
*
* @since 5.0.2
*/
public function getGuid(string $key): string;

View File

@ -0,0 +1,19 @@
{
"add_head": "0",
"add_licensing_template": "2",
"extends": "",
"guid": "576685fd-263c-46bb-9fdc-1f5eb234cbb6",
"implements": null,
"load_selection": null,
"name": "GuidInterface",
"power_version": "1.0.0",
"system_name": "VDM.Interfaces.Data.GuidInterface",
"type": "interface",
"use_selection": null,
"extendsinterfaces": null,
"namespace": "[[[NamespacePrefix]]]\\Joomla\\Interfaces.Data.GuidInterface",
"description": "Globally Unique Identifier Interface\r\n\r\n@since 5.0.2",
"licensing_template": "\/**\r\n * @package Joomla.Component.Builder\r\n *\r\n * @created 4th September, 2022\r\n * @author Llewellyn van der Merwe <https:\/\/dev.vdm.io>\r\n * @git Joomla Component Builder <https:\/\/git.vdm.dev\/joomla\/Component-Builder>\r\n * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.\r\n * @license GNU General Public License version 2 or later; see LICENSE.txt\r\n *\/\r\n",
"head": "",
"composer": ""
}

View File

@ -0,0 +1,70 @@
```
██████╗ ██████╗ ██╗ ██╗███████╗██████╗
██╔══██╗██╔═══██╗██║ ██║██╔════╝██╔══██╗
██████╔╝██║ ██║██║ █╗ ██║█████╗ ██████╔╝
██╔═══╝ ██║ ██║██║███╗██║██╔══╝ ██╔══██╗
██║ ╚██████╔╝╚███╔███╔╝███████╗██║ ██║
╚═╝ ╚═════╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═╝
```
# trait Guid (Details)
> namespace: **VDM\Joomla\Data**
```uml
@startuml
class Guid << (T,Orange) >> #Turquoise {
+ getGuid(string $key) : string
- fallbackGuid(string $key) : string
- checkGuid(string $guid, string $key) : string
}
note right of Guid::getGuid
Returns a GUIDv4 string.
This function uses the best cryptographically secure method
available on the platform with a fallback to an older, less secure version.
since: 5.0.2
return: string
end note
note right of Guid::fallbackGuid
Generates a fallback GUIDv4 using less secure methods.
since: 5.0.2
return: string
end note
note right of Guid::checkGuid
Checks if the GUID value is unique and does not already exist.
since: 5.0.2
return: string
end note
@enduml
```
The Power feature in JCB allows you to write PHP classes and their implementations, making it easy to include them in your Joomla project. JCB handles linking, autoloading, namespacing, and folder structure creation for you.
By using the SPK (Super Power Key) in your custom code (replacing the class name in your code with the SPK), JCB will automatically pull the power from the repository into your project. This makes it available in your JCB instance, allowing you to edit it and include the class in your generated Joomla component.
JCB uses placeholders like [[[`NamespacePrefix`]]] and [[[`ComponentNamespace`]]] in namespacing to prevent collisions and improve reusability across different JCB systems. You can also set the **JCB powers path** globally or per component under the **Dynamic Integration** tab, providing flexibility and easy maintainability.
To add this specific Power to your project in JCB:
> simply use this SPK
```
Super---5acded67_0e3d_4c6b_a6ea_b533b076de0c---Power
```
> remember to replace the `---` with `___` to activate this Power in your code
---
```
██╗ ██████╗██████╗
██║██╔════╝██╔══██╗
██║██║ ██████╔╝
██ ██║██║ ██╔══██╗
╚█████╔╝╚██████╗██████╔╝
╚════╝ ╚═════╝╚═════╝
```
> Build with [Joomla Component Builder](https://git.vdm.dev/joomla/Component-Builder)

View File

@ -0,0 +1,110 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 3rd September, 2020
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Data;
/**
* Globally Unique Identifier
*
* @since 5.0.2
*/
trait Guid
{
/**
* Returns a GUIDv4 string.
*
* This function uses the best cryptographically secure method
* available on the platform with a fallback to an older, less secure version.
*
* @param string $key The key to check and modify values.
*
* @return string A GUIDv4 string.
*
* @since 5.0.2
*/
public function getGuid(string $key): string
{
// Windows: Use com_create_guid if available
if (function_exists('com_create_guid'))
{
$guid = trim(\com_create_guid(), '{}');
return $this->checkGuid($guid, $key);
}
// Unix-based systems: Use openssl_random_pseudo_bytes if available
if (function_exists('random_bytes'))
{
try {
$data = random_bytes(16);
} catch (Exception $e) {
// Handle the error appropriately (logging, throwing, etc.)
return $this->fallbackGuid($key);
}
// Set the version to 0100 and the bits 6-7 to 10 as per RFC 4122
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
$guid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
return $this->checkGuid($guid, $key);
}
// Fallback to older methods if secure methods are not available
return $this->fallbackGuid($key);
}
/**
* Generates a fallback GUIDv4 using less secure methods.
*
* @param string $key The key to check and modify values.
*
* @return string A GUIDv4 string.
*
* @since 5.0.2
*/
private function fallbackGuid(string $key): string
{
$charid = strtolower(md5(uniqid(random_int(0, PHP_INT_MAX), true)));
$guidv4 = sprintf(
'%s-%s-%s-%s-%s',
substr($charid, 0, 8),
substr($charid, 8, 4),
substr($charid, 12, 4),
substr($charid, 16, 4),
substr($charid, 20, 12)
);
return $this->checkGuid($guidv4, $key);
}
/**
* Checks if the GUID value is unique and does not already exist.
*
* @param string $guid The GUID value to check.
* @param string $key The key to check and modify values.
*
* @return string The unique GUID value.
*
* @since 5.0.2
*/
private function checkGuid(string $guid, string $key): string
{
// Check that the GUID does not already exist
if ($this->items->table($this->getTable())->values([$guid], $key))
{
return $this->getGuid($key);
}
return $guid;
}
}

View File

@ -0,0 +1,87 @@
/**
* Returns a GUIDv4 string.
*
* This function uses the best cryptographically secure method
* available on the platform with a fallback to an older, less secure version.
*
* @param string $key The key to check and modify values.
*
* @return string A GUIDv4 string.
*
* @since 5.0.2
*/
public function getGuid(string $key): string
{
// Windows: Use com_create_guid if available
if (function_exists('com_create_guid'))
{
$guid = trim(\com_create_guid(), '{}');
return $this->checkGuid($guid, $key);
}
// Unix-based systems: Use openssl_random_pseudo_bytes if available
if (function_exists('random_bytes'))
{
try {
$data = random_bytes(16);
} catch (Exception $e) {
// Handle the error appropriately (logging, throwing, etc.)
return $this->fallbackGuid($key);
}
// Set the version to 0100 and the bits 6-7 to 10 as per RFC 4122
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
$guid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
return $this->checkGuid($guid, $key);
}
// Fallback to older methods if secure methods are not available
return $this->fallbackGuid($key);
}
/**
* Generates a fallback GUIDv4 using less secure methods.
*
* @param string $key The key to check and modify values.
*
* @return string A GUIDv4 string.
*
* @since 5.0.2
*/
private function fallbackGuid(string $key): string
{
$charid = strtolower(md5(uniqid(random_int(0, PHP_INT_MAX), true)));
$guidv4 = sprintf(
'%s-%s-%s-%s-%s',
substr($charid, 0, 8),
substr($charid, 8, 4),
substr($charid, 12, 4),
substr($charid, 16, 4),
substr($charid, 20, 12)
);
return $this->checkGuid($guidv4, $key);
}
/**
* Checks if the GUID value is unique and does not already exist.
*
* @param string $guid The GUID value to check.
* @param string $key The key to check and modify values.
*
* @return string The unique GUID value.
*
* @since 5.0.2
*/
private function checkGuid(string $guid, string $key): string
{
// Check that the GUID does not already exist
if ($this->items->table($this->getTable())->values([$guid], $key))
{
return $this->getGuid($key);
}
return $guid;
}

View File

@ -0,0 +1,19 @@
{
"add_head": "0",
"add_licensing_template": "2",
"extends": "",
"guid": "5acded67-0e3d-4c6b-a6ea-b533b076de0c",
"implements": null,
"load_selection": null,
"name": "Guid",
"power_version": "1.0.0",
"system_name": "VDM.Data.Guid",
"type": "trait",
"use_selection": null,
"extendsinterfaces": null,
"namespace": "[[[NamespacePrefix]]]\\Joomla\\Data.Guid",
"description": "Globally Unique Identifier\r\n\r\n@since 5.0.2",
"licensing_template": "\/**\r\n * @package Joomla.Component.Builder\r\n *\r\n * @created 3rd September, 2020\r\n * @author Llewellyn van der Merwe <https:\/\/dev.vdm.io>\r\n * @git Joomla Component Builder <https:\/\/git.vdm.dev\/joomla\/Component-Builder>\r\n * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.\r\n * @license GNU General Public License version 2 or later; see LICENSE.txt\r\n *\/\r\n",
"head": "",
"composer": ""
}

View File

@ -12,6 +12,7 @@
```uml ```uml
@startuml @startuml
abstract Helper #Orange { abstract Helper #Orange {
+ {static} setParams(string $target, mixed $value, ...) : mixed
+ {static} getParams(?string $option = null) : Registry + {static} getParams(?string $option = null) : Registry
+ {static} setOption(?string $option) : void + {static} setOption(?string $option) : void
+ {static} getOption(?string $default = 'empty') : ?string + {static} getOption(?string $default = 'empty') : ?string
@ -21,65 +22,82 @@ abstract Helper #Orange {
+ {static} getManifest(?string $option = null) : ?object + {static} getManifest(?string $option = null) : ?object
+ {static} methodExists(string $method, ?string $option = null) : bool + {static} methodExists(string $method, ?string $option = null) : bool
+ {static} _(string $method, array $arguments = [], ...) : mixed + {static} _(string $method, array $arguments = [], ...) : mixed
+ {static} getModel(string $type, string $prefix = 'Administrator', ...) : BaseDatabaseModel
- {static} getPrefixFromModelPath(string $path) : string
} }
note right of Helper::getParams note right of Helper::setParams
Sets a parameter value for the given target in the specified option's params.
If no option is provided, it falls back to the default option.
This method updates the parameters for a given extension in the database,
only if the new value differs from the existing one.
since: 5.0.3
return: mixed
arguments:
string $target
mixed $value
?string $option = null
end note
note left of Helper::getParams
Gets the parameter object for the component Gets the parameter object for the component
since: 3.0.11 since: 3.0.11
return: Registry return: Registry
end note end note
note left of Helper::setOption note right of Helper::setOption
Set the component option Set the component option
since: 3.2.0 since: 3.2.0
return: void return: void
end note end note
note right of Helper::getOption note left of Helper::getOption
Get the component option Get the component option
since: 3.0.11 since: 3.0.11
return: ?string return: ?string
end note end note
note left of Helper::getCode note right of Helper::getCode
Gets the component code name Gets the component code name
since: 3.0.11 since: 3.0.11
return: ?string return: ?string
end note end note
note right of Helper::get note left of Helper::get
Gets the component abstract helper class Gets the component abstract helper class
since: 3.0.11 since: 3.0.11
return: ?string return: ?string
end note end note
note left of Helper::getNamespace note right of Helper::getNamespace
Gets the component namespace if set Gets the component namespace if set
since: 3.0.11 since: 3.0.11
return: ?string return: ?string
end note end note
note right of Helper::getManifest note left of Helper::getManifest
Gets the component abstract helper class Gets the component abstract helper class
since: 3.0.11 since: 3.0.11
return: ?object return: ?object
end note end note
note left of Helper::methodExists note right of Helper::methodExists
Check if the helper class of this component has a method Check if the helper class of this component has a method
since: 3.0.11 since: 3.0.11
return: bool return: bool
end note end note
note right of Helper::_ note left of Helper::_
Check if the helper class of this component has a method, and call it with the arguments Check if the helper class of this component has a method, and call it with the arguments
since: 3.2.0 since: 3.2.0
@ -91,6 +109,26 @@ note right of Helper::_
?string $option = null ?string $option = null
end note end note
note right of Helper::getModel
Returns a Model object based on the specified type, prefix, and configuration.
since: 5.0.3
return: BaseDatabaseModel
arguments:
string $type
string $prefix = 'Administrator'
?string $option = null
array $config = []
end note
note left of Helper::getPrefixFromModelPath
Get the prefix from the model path
since: 5.0.3
return: string
end note
@enduml @enduml
``` ```

View File

@ -14,9 +14,11 @@ namespace VDM\Joomla\Utilities\Component;
use Joomla\CMS\Factory; use Joomla\CMS\Factory;
use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\Input\Input; use Joomla\Input\Input;
use Joomla\Registry\Registry; use Joomla\Registry\Registry;
use VDM\Joomla\Utilities\String\NamespaceHelper; use VDM\Joomla\Utilities\String\NamespaceHelper;
use VDM\Joomla\Utilities\StringHelper;
/** /**
@ -50,6 +52,57 @@ abstract class Helper
*/ */
protected static array $params = []; protected static array $params = [];
/**
* Sets a parameter value for the given target in the specified option's params.
* If no option is provided, it falls back to the default option.
*
* This method updates the parameters for a given extension in the database,
* only if the new value differs from the existing one.
*
* @param string $target The parameter name to be updated.
* @param mixed $value The value to set for the parameter.
* @param string|null $option The optional extension element name. Defaults to null, which will use the default option.
*
* @return mixed The previous value of the parameter before it was updated.
* @since 5.0.3
*/
public static function setParams(string $target, $value, ?string $option = null)
{
// Ensure that an option is specified, defaulting to the system's option if not provided.
if (empty($option))
{
$option = static::getOption();
}
// Retrieve current parameters for the specified option.
$params = static::getParams($option);
// Get the current value of the target parameter.
$was = $params->get($target, null);
// Only proceed if the new value differs from the current value.
if ($was !== $value)
{
// Update the parameter value.
$params->set($target, $value);
// Obtain a database connection instance.
$db = Factory::getDBO();
$query = $db->getQuery(true);
// Build and execute the query to update the parameters in the database.
$query->update('#__extensions AS a')
->set('a.params = ' . $db->quote((string) $params))
->where('a.element = ' . $db->quote((string) $option));
$db->setQuery($query);
$db->execute();
}
// Return the previous value of the parameter.
return $was;
}
/** /**
* Gets the parameter object for the component * Gets the parameter object for the component
* *
@ -64,16 +117,16 @@ abstract class Helper
// check that we have an option // check that we have an option
if (empty($option)) if (empty($option))
{ {
$option = self::getOption(); $option = static::getOption();
} }
// get global value // get global value
if (!isset(self::$params[$option]) || !self::$params[$option] instanceof Registry) if (!isset(static::$params[$option]) || !static::$params[$option] instanceof Registry)
{ {
self::$params[$option] = ComponentHelper::getParams($option); static::$params[$option] = ComponentHelper::getParams($option);
} }
return self::$params[$option]; return static::$params[$option];
} }
/** /**
@ -86,7 +139,7 @@ abstract class Helper
*/ */
public static function setOption(?string $option): void public static function setOption(?string $option): void
{ {
self::$option = $option; static::$option = $option;
} }
/** /**
@ -99,13 +152,13 @@ abstract class Helper
*/ */
public static function getOption(?string $default = 'empty'): ?string public static function getOption(?string $default = 'empty'): ?string
{ {
if (empty(self::$option)) if (empty(static::$option))
{ {
// get the option from the url input // get the option from the url input
self::$option = (new Input)->getString('option', null); static::$option = (new Input)->getString('option', null);
} }
if (empty(self::$option)) if (empty(static::$option))
{ {
$app = Factory::getApplication(); $app = Factory::getApplication();
@ -113,16 +166,16 @@ abstract class Helper
if (method_exists($app, 'getInput')) if (method_exists($app, 'getInput'))
{ {
// get the option from the application // get the option from the application
self::$option = $app->getInput()->getCmd('option', $default); static::$option = $app->getInput()->getCmd('option', $default);
} }
else else
{ {
// Use the default value if getInput method does not exist // Use the default value if getInput method does not exist
self::$option = $default; static::$option = $default;
} }
} }
return self::$option; return static::$option;
} }
/** /**
@ -139,7 +192,7 @@ abstract class Helper
// check that we have an option // check that we have an option
if (empty($option)) if (empty($option))
{ {
$option = self::getOption(); $option = static::getOption();
} }
// option with com_ // option with com_
if (is_string($option) && strpos($option, 'com_') === 0) if (is_string($option) && strpos($option, 'com_') === 0)
@ -164,7 +217,7 @@ abstract class Helper
{ {
// check that we have an option // check that we have an option
// and get the code name from it // and get the code name from it
if (($code_name = self::getCode($option, null)) !== null) if (($code_name = static::getCode($option, null)) !== null)
{ {
// we build the helper class name // we build the helper class name
$helper_name = '\\' . \ucfirst($code_name) . 'Helper'; $helper_name = '\\' . \ucfirst($code_name) . 'Helper';
@ -176,7 +229,7 @@ abstract class Helper
} }
// try loading namespace // try loading namespace
if (($namespace = self::getNamespace($option)) !== null) if (($namespace = static::getNamespace($option)) !== null)
{ {
$name = \ucfirst($code_name) . 'Helper'; $name = \ucfirst($code_name) . 'Helper';
$namespace_helper = '\\' . $namespace . '\Administrator\Helper\\' . NamespaceHelper::safeSegment($name); // TODO target site or admin locations not just admin... $namespace_helper = '\\' . $namespace . '\Administrator\Helper\\' . NamespaceHelper::safeSegment($name); // TODO target site or admin locations not just admin...
@ -202,7 +255,7 @@ abstract class Helper
*/ */
public static function getNamespace(?string $option = null): ?string public static function getNamespace(?string $option = null): ?string
{ {
$manifest = self::getManifest($option); $manifest = static::getManifest($option);
return $manifest->namespace ?? null; return $manifest->namespace ?? null;
} }
@ -220,13 +273,13 @@ abstract class Helper
public static function getManifest(?string $option = null): ?object public static function getManifest(?string $option = null): ?object
{ {
if ($option === null if ($option === null
&& ($option = self::getOption($option)) === null) && ($option = static::getOption($option)) === null)
{ {
return null; return null;
} }
// get global manifest_cache values // get global manifest_cache values
if (!isset(self::$manifest[$option])) if (!isset(static::$manifest[$option]))
{ {
$db = Factory::getDbo(); $db = Factory::getDbo();
$query = $db->getQuery(true); $query = $db->getQuery(true);
@ -240,14 +293,14 @@ abstract class Helper
try { try {
$manifest = $db->loadResult(); $manifest = $db->loadResult();
self::$manifest[$option] = json_decode($manifest); static::$manifest[$option] = json_decode($manifest);
} catch (\Exception $e) { } catch (\Exception $e) {
// Handle the database error appropriately. // Handle the database error appropriately.
self::$manifest[$option] = null; static::$manifest[$option] = null;
} }
} }
return self::$manifest[$option]; return static::$manifest[$option];
} }
/** /**
@ -263,7 +316,7 @@ abstract class Helper
public static function methodExists(string $method, ?string $option = null): bool public static function methodExists(string $method, ?string $option = null): bool
{ {
// get the helper class // get the helper class
return ($helper = self::get($option, null)) !== null && return ($helper = static::get($option, null)) !== null &&
method_exists($helper, $method); method_exists($helper, $method);
} }
@ -280,7 +333,7 @@ abstract class Helper
public static function _(string $method, array $arguments = [], ?string $option = null) public static function _(string $method, array $arguments = [], ?string $option = null)
{ {
// get the helper class // get the helper class
if (($helper = self::get($option, null)) !== null && if (($helper = static::get($option, null)) !== null &&
method_exists($helper, $method)) method_exists($helper, $method))
{ {
// we know this is not ideal... // we know this is not ideal...
@ -292,5 +345,76 @@ abstract class Helper
return null; return null;
} }
/**
* Returns a Model object based on the specified type, prefix, and configuration.
*
* @param string $type The model type to instantiate. Must not be empty.
* @param string $prefix Prefix for the model class name. Optional, defaults to 'Administrator'.
* @param string|null $option The component option. Optional, defaults to the component's option.
* @param array $config Configuration array for the model. Optional, defaults to an empty array.
*
* @return BaseDatabaseModel The instantiated model object.
*
* @throws \InvalidArgumentException If the $type parameter is empty.
* @throws \Exception For other errors that may occur during model creation.
*
* @since 5.0.3
*/
public static function getModel(string $type, string $prefix = 'Administrator',
?string $option = null, array $config = []): BaseDatabaseModel
{
// Ensure the $type parameter is not empty
if (empty($type))
{
throw new \InvalidArgumentException('The $type parameter cannot be empty when calling Component Helper getModel method.');
}
// Ensure the $option parameter is set, defaulting to the component's option if not provided
if (empty($option))
{
$option = static::getOption();
}
// Normalize the model type name if the first character is not uppercase
if (!ctype_upper($type[0]))
{
$type = StringHelper::safe($type, 'F');
}
// Normalize the prefix if it's not 'Site' or 'Administrator'
if ($prefix !== 'Site' && $prefix !== 'Administrator')
{
$prefix = static::getPrefixFromModelPath($prefix);
}
// Instantiate and return the model using the MVCFactory
return Factory::getApplication()
->bootComponent($option)
->getMVCFactory()
->createModel($type, $prefix, $config);
}
/**
* Get the prefix from the model path
*
* @param string $path The model path
*
* @return string The prefix value
* @since 5.0.3
*/
private static function getPrefixFromModelPath(string $path): string
{
// Check if $path starts with JPATH_ADMINISTRATOR path
if (str_starts_with($path, JPATH_ADMINISTRATOR . '/components/'))
{
return 'Administrator';
}
// Check if $path starts with JPATH_SITE path
elseif (str_starts_with($path, JPATH_SITE . '/components/'))
{
return 'Site';
}
return 'Administrator';
}
} }

View File

@ -22,6 +22,57 @@
*/ */
protected static array $params = []; protected static array $params = [];
/**
* Sets a parameter value for the given target in the specified option's params.
* If no option is provided, it falls back to the default option.
*
* This method updates the parameters for a given extension in the database,
* only if the new value differs from the existing one.
*
* @param string $target The parameter name to be updated.
* @param mixed $value The value to set for the parameter.
* @param string|null $option The optional extension element name. Defaults to null, which will use the default option.
*
* @return mixed The previous value of the parameter before it was updated.
* @since 5.0.3
*/
public static function setParams(string $target, $value, ?string $option = null)
{
// Ensure that an option is specified, defaulting to the system's option if not provided.
if (empty($option))
{
$option = static::getOption();
}
// Retrieve current parameters for the specified option.
$params = static::getParams($option);
// Get the current value of the target parameter.
$was = $params->get($target, null);
// Only proceed if the new value differs from the current value.
if ($was !== $value)
{
// Update the parameter value.
$params->set($target, $value);
// Obtain a database connection instance.
$db = Factory::getDBO();
$query = $db->getQuery(true);
// Build and execute the query to update the parameters in the database.
$query->update('#__extensions AS a')
->set('a.params = ' . $db->quote((string) $params))
->where('a.element = ' . $db->quote((string) $option));
$db->setQuery($query);
$db->execute();
}
// Return the previous value of the parameter.
return $was;
}
/** /**
* Gets the parameter object for the component * Gets the parameter object for the component
* *
@ -36,16 +87,16 @@
// check that we have an option // check that we have an option
if (empty($option)) if (empty($option))
{ {
$option = self::getOption(); $option = static::getOption();
} }
// get global value // get global value
if (!isset(self::$params[$option]) || !self::$params[$option] instanceof Registry) if (!isset(static::$params[$option]) || !static::$params[$option] instanceof Registry)
{ {
self::$params[$option] = ComponentHelper::getParams($option); static::$params[$option] = ComponentHelper::getParams($option);
} }
return self::$params[$option]; return static::$params[$option];
} }
/** /**
@ -58,7 +109,7 @@
*/ */
public static function setOption(?string $option): void public static function setOption(?string $option): void
{ {
self::$option = $option; static::$option = $option;
} }
/** /**
@ -71,13 +122,13 @@
*/ */
public static function getOption(?string $default = 'empty'): ?string public static function getOption(?string $default = 'empty'): ?string
{ {
if (empty(self::$option)) if (empty(static::$option))
{ {
// get the option from the url input // get the option from the url input
self::$option = (new Input)->getString('option', null); static::$option = (new Input)->getString('option', null);
} }
if (empty(self::$option)) if (empty(static::$option))
{ {
$app = Factory::getApplication(); $app = Factory::getApplication();
@ -85,16 +136,16 @@
if (method_exists($app, 'getInput')) if (method_exists($app, 'getInput'))
{ {
// get the option from the application // get the option from the application
self::$option = $app->getInput()->getCmd('option', $default); static::$option = $app->getInput()->getCmd('option', $default);
} }
else else
{ {
// Use the default value if getInput method does not exist // Use the default value if getInput method does not exist
self::$option = $default; static::$option = $default;
} }
} }
return self::$option; return static::$option;
} }
/** /**
@ -111,7 +162,7 @@
// check that we have an option // check that we have an option
if (empty($option)) if (empty($option))
{ {
$option = self::getOption(); $option = static::getOption();
} }
// option with com_ // option with com_
if (is_string($option) && strpos($option, 'com_') === 0) if (is_string($option) && strpos($option, 'com_') === 0)
@ -136,7 +187,7 @@
{ {
// check that we have an option // check that we have an option
// and get the code name from it // and get the code name from it
if (($code_name = self::getCode($option, null)) !== null) if (($code_name = static::getCode($option, null)) !== null)
{ {
// we build the helper class name // we build the helper class name
$helper_name = '\\' . \ucfirst($code_name) . 'Helper'; $helper_name = '\\' . \ucfirst($code_name) . 'Helper';
@ -148,7 +199,7 @@
} }
// try loading namespace // try loading namespace
if (($namespace = self::getNamespace($option)) !== null) if (($namespace = static::getNamespace($option)) !== null)
{ {
$name = \ucfirst($code_name) . 'Helper'; $name = \ucfirst($code_name) . 'Helper';
$namespace_helper = '\\' . $namespace . '\Administrator\Helper\\' . NamespaceHelper::safeSegment($name); // TODO target site or admin locations not just admin... $namespace_helper = '\\' . $namespace . '\Administrator\Helper\\' . NamespaceHelper::safeSegment($name); // TODO target site or admin locations not just admin...
@ -174,7 +225,7 @@
*/ */
public static function getNamespace(?string $option = null): ?string public static function getNamespace(?string $option = null): ?string
{ {
$manifest = self::getManifest($option); $manifest = static::getManifest($option);
return $manifest->namespace ?? null; return $manifest->namespace ?? null;
} }
@ -192,13 +243,13 @@
public static function getManifest(?string $option = null): ?object public static function getManifest(?string $option = null): ?object
{ {
if ($option === null if ($option === null
&& ($option = self::getOption($option)) === null) && ($option = static::getOption($option)) === null)
{ {
return null; return null;
} }
// get global manifest_cache values // get global manifest_cache values
if (!isset(self::$manifest[$option])) if (!isset(static::$manifest[$option]))
{ {
$db = Factory::getDbo(); $db = Factory::getDbo();
$query = $db->getQuery(true); $query = $db->getQuery(true);
@ -212,14 +263,14 @@
try { try {
$manifest = $db->loadResult(); $manifest = $db->loadResult();
self::$manifest[$option] = json_decode($manifest); static::$manifest[$option] = json_decode($manifest);
} catch (\Exception $e) { } catch (\Exception $e) {
// Handle the database error appropriately. // Handle the database error appropriately.
self::$manifest[$option] = null; static::$manifest[$option] = null;
} }
} }
return self::$manifest[$option]; return static::$manifest[$option];
} }
/** /**
@ -235,7 +286,7 @@
public static function methodExists(string $method, ?string $option = null): bool public static function methodExists(string $method, ?string $option = null): bool
{ {
// get the helper class // get the helper class
return ($helper = self::get($option, null)) !== null && return ($helper = static::get($option, null)) !== null &&
method_exists($helper, $method); method_exists($helper, $method);
} }
@ -252,7 +303,7 @@
public static function _(string $method, array $arguments = [], ?string $option = null) public static function _(string $method, array $arguments = [], ?string $option = null)
{ {
// get the helper class // get the helper class
if (($helper = self::get($option, null)) !== null && if (($helper = static::get($option, null)) !== null &&
method_exists($helper, $method)) method_exists($helper, $method))
{ {
// we know this is not ideal... // we know this is not ideal...
@ -263,3 +314,75 @@
return null; return null;
} }
/**
* Returns a Model object based on the specified type, prefix, and configuration.
*
* @param string $type The model type to instantiate. Must not be empty.
* @param string $prefix Prefix for the model class name. Optional, defaults to 'Administrator'.
* @param string|null $option The component option. Optional, defaults to the component's option.
* @param array $config Configuration array for the model. Optional, defaults to an empty array.
*
* @return BaseDatabaseModel The instantiated model object.
*
* @throws \InvalidArgumentException If the $type parameter is empty.
* @throws \Exception For other errors that may occur during model creation.
*
* @since 5.0.3
*/
public static function getModel(string $type, string $prefix = 'Administrator',
?string $option = null, array $config = []): BaseDatabaseModel
{
// Ensure the $type parameter is not empty
if (empty($type))
{
throw new \InvalidArgumentException('The $type parameter cannot be empty when calling Component Helper getModel method.');
}
// Ensure the $option parameter is set, defaulting to the component's option if not provided
if (empty($option))
{
$option = static::getOption();
}
// Normalize the model type name if the first character is not uppercase
if (!ctype_upper($type[0]))
{
$type = StringHelper::safe($type, 'F');
}
// Normalize the prefix if it's not 'Site' or 'Administrator'
if ($prefix !== 'Site' && $prefix !== 'Administrator')
{
$prefix = static::getPrefixFromModelPath($prefix);
}
// Instantiate and return the model using the MVCFactory
return Factory::getApplication()
->bootComponent($option)
->getMVCFactory()
->createModel($type, $prefix, $config);
}
/**
* Get the prefix from the model path
*
* @param string $path The model path
*
* @return string The prefix value
* @since 5.0.3
*/
private static function getPrefixFromModelPath(string $path): string
{
// Check if $path starts with JPATH_ADMINISTRATOR path
if (str_starts_with($path, JPATH_ADMINISTRATOR . '/components/'))
{
return 'Administrator';
}
// Check if $path starts with JPATH_SITE path
elseif (str_starts_with($path, JPATH_SITE . '/components/'))
{
return 'Site';
}
return 'Administrator';
}

View File

@ -13,12 +13,16 @@
"use_selection0": { "use_selection0": {
"use": "ce8cf834-6bac-44fb-941c-861f7e046cc0", "use": "ce8cf834-6bac-44fb-941c-861f7e046cc0",
"as": "default" "as": "default"
},
"use_selection1": {
"use": "1f28cb53-60d9-4db1-b517-3c7dc6b429ef",
"as": "default"
} }
}, },
"extendsinterfaces": null, "extendsinterfaces": null,
"namespace": "[[[NamespacePrefix]]]\\Joomla\\Utilities.Component.Helper", "namespace": "[[[NamespacePrefix]]]\\Joomla\\Utilities.Component.Helper",
"description": "Some component helper\r\n\r\n@since 3.0.11", "description": "Some component helper\r\n\r\n@since 3.0.11",
"licensing_template": "\/**\r\n * @package Joomla.Component.Builder\r\n *\r\n * @created 3rd September, 2020\r\n * @author Llewellyn van der Merwe <https:\/\/dev.vdm.io>\r\n * @git Joomla Component Builder <https:\/\/git.vdm.dev\/joomla\/Component-Builder>\r\n * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.\r\n * @license GNU General Public License version 2 or later; see LICENSE.txt\r\n *\/\r\n", "licensing_template": "\/**\r\n * @package Joomla.Component.Builder\r\n *\r\n * @created 3rd September, 2020\r\n * @author Llewellyn van der Merwe <https:\/\/dev.vdm.io>\r\n * @git Joomla Component Builder <https:\/\/git.vdm.dev\/joomla\/Component-Builder>\r\n * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.\r\n * @license GNU General Public License version 2 or later; see LICENSE.txt\r\n *\/\r\n",
"head": "use Joomla\\CMS\\Factory;\r\nuse Joomla\\CMS\\Component\\ComponentHelper;\r\nuse Joomla\\Input\\Input;\r\nuse Joomla\\Registry\\Registry;", "head": "use Joomla\\CMS\\Factory;\r\nuse Joomla\\CMS\\Component\\ComponentHelper;\r\nuse Joomla\\CMS\\MVC\\Model\\BaseDatabaseModel;\r\nuse Joomla\\Input\\Input;\r\nuse Joomla\\Registry\\Registry;",
"composer": "" "composer": ""
} }

View File

@ -0,0 +1,141 @@
```
██████╗ ██████╗ ██╗ ██╗███████╗██████╗
██╔══██╗██╔═══██╗██║ ██║██╔════╝██╔══██╗
██████╔╝██║ ██║██║ █╗ ██║█████╗ ██████╔╝
██╔═══╝ ██║ ██║██║███╗██║██╔══╝ ██╔══██╗
██║ ╚██████╔╝╚███╔███╔╝███████╗██║ ██║
╚═╝ ╚═════╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═╝
```
# abstract class UserHelper (Details)
> namespace: **VDM\Joomla\Componentbuilder\Utilities**
```uml
@startuml
abstract UserHelper #Orange {
+ {static} save(array $credentials, int $autologin, ...) : int
+ {static} create(array $credentials, int $autologin, ...) : int
+ {static} update(array $userDetails) : int
+ {static} getUserById(int $id) : User
+ {static} getUserIdByUsername(string $username) : ?int
+ {static} getUserIdByEmail(string $email) : ?int
# {static} getModelByMode(int $mode) : BaseDatabaseModel
# {static} prepareUserData(array $credentials, int $mode) : array
- {static} adminRegister(BaseDatabaseModel $model, array $data) : int
- {static} handlePostRegistration(int $userId, int $autologin, ...) : int
}
note right of UserHelper::save
Save user details by either creating a new user or updating an existing user.
since: 5.0.3
return: int
arguments:
array $credentials
int $autologin
array $params = ['useractivation' => 0, 'sendpassword' => 1]
int $mode = 1
end note
note left of UserHelper::create
Create a user and update the given table.
since: 5.0.3
return: int
arguments:
array $credentials
int $autologin
array $params = ['useractivation' => 0, 'sendpassword' => 1]
int $mode = 1
end note
note right of UserHelper::update
Update user details.
since: 5.0.3
return: int
end note
note left of UserHelper::getUserById
Method to get an instance of a user for the given id.
since: 5.0.3
return: User
end note
note right of UserHelper::getUserIdByUsername
Retrieve the user ID by username.
since: 5.0.3
return: ?int
end note
note left of UserHelper::getUserIdByEmail
Retrieve the user ID by email.
since: 5.0.3
return: ?int
end note
note right of UserHelper::getModelByMode
Load the correct user model based on the registration mode.
since: 5.0.3
return: BaseDatabaseModel
end note
note left of UserHelper::prepareUserData
Prepare user data array for registration or update.
since: 5.0.3
return: array
end note
note right of UserHelper::adminRegister
Handle the registration process for admin mode.
since: 5.0.3
return: int
end note
note left of UserHelper::handlePostRegistration
Handle post-registration processes like auto-login.
since: 5.0.3
return: int
arguments:
int $userId
int $autologin
array $credentials
end note
@enduml
```
The Power feature in JCB allows you to write PHP classes and their implementations, making it easy to include them in your Joomla project. JCB handles linking, autoloading, namespacing, and folder structure creation for you.
By using the SPK (Super Power Key) in your custom code (replacing the class name in your code with the SPK), JCB will automatically pull the power from the repository into your project. This makes it available in your JCB instance, allowing you to edit it and include the class in your generated Joomla component.
JCB uses placeholders like [[[`NamespacePrefix`]]] and [[[`ComponentNamespace`]]] in namespacing to prevent collisions and improve reusability across different JCB systems. You can also set the **JCB powers path** globally or per component under the **Dynamic Integration** tab, providing flexibility and easy maintainability.
To add this specific Power to your project in JCB:
> simply use this SPK
```
Super---7832a726_87b6_4e95_887e_7b725d3fab8f---Power
```
> remember to replace the `---` with `___` to activate this Power in your code
---
```
██╗ ██████╗██████╗
██║██╔════╝██╔══██╗
██║██║ ██████╔╝
██ ██║██║ ██╔══██╗
╚█████╔╝╚██████╗██████╔╝
╚════╝ ╚═════╝╚═════╝
```
> Build with [Joomla Component Builder](https://git.vdm.dev/joomla/Component-Builder)

View File

@ -0,0 +1,413 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 3rd September, 2020
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Componentbuilder\Utilities;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\User\User;
use Joomla\CMS\User\UserHelper as JoomlaUserHelper;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use VDM\Joomla\Utilities\Component\Helper as Component;
use VDM\Component\Componentbuilder\Administrator\Helper\ComponentbuilderHelper;
/**
* Create & Update User [Save]
*
* @since 5.0.2
*/
abstract class UserHelper
{
/**
* Save user details by either creating a new user or updating an existing user.
*
* @param array $credentials User credentials including 'name', 'username', 'email', 'password', and 'password2'.
* @param int $autologin Flag to determine whether to auto-login the user after registration.
* @param array $params Parameters for user activation, password sending, and user registration allowance.
* @param int $mode Mode of registration: 1 = Site Registration, 0 = Admin Registration, 2 = Custom Helper Method.
*
* @return int User ID on success.
*
* @throws \InvalidArgumentException If required credentials are missing.
* @throws \RuntimeException If the user update or creation fails.
*
* @since 5.0.3
*/
public static function save(array $credentials, int $autologin = 0,
array $params = ['useractivation' => 0, 'sendpassword' => 1], int $mode = 1): int
{
// can not continue without an email
if (empty($credentials['email']))
{
throw new \InvalidArgumentException(Text::_('COM_COMPONENTBUILDER_CAN_NOT_SAVE_USER_WITHOUT_EMAIL_VALUE'));
}
// Ensure the 'username' key exists in the credentials array, set to an empty string if not provided.
$username = $credentials['username'] ?? $credentials['email'];
// Check for an existing user by email or username.
$existingEmailUserId = static::getUserIdByEmail($credentials['email']);
$existingUserId = static::getUserIdByUsername($username);
// If either the email or username already exists, handle the update logic.
if ($existingEmailUserId !== null || $existingUserId !== null || (isset($credentials['id']) && $credentials['id'] > 0))
{
// Prevent updating other users or reusing an email by different users.
if (
(
isset($credentials['id']) &&
(
($existingEmailUserId !== null && $existingEmailUserId != $credentials['id']) ||
($existingUserId !== null && $existingUserId != $credentials['id'])
)
) || ($existingUserId !== null && $existingEmailUserId !== null && $existingEmailUserId != $existingUserId)
)
{
throw new \RuntimeException(Text::sprintf('COM_COMPONENTBUILDER_USER_ID_MISMATCH_DETECTED_WHEN_TRYING_TO_SAVE_S_S_CREDENTIALS', $username, $credentials['email']));
}
// Update the existing user.
$credentials['id'] = $credentials['id'] ?? $existingEmailUserId ?? $existingUserId;
return static::update($credentials);
}
// Create a new user if no existing user is found.
return static::create($credentials, $autologin, $params, $mode);
}
/**
* Create a user and update the given table.
*
* @param array $credentials User credentials including 'name', 'username', 'email', 'password', and 'password2'.
* @param int $autologin Flag to determine whether to auto-login the user after registration.
* @param array $params Parameters for user activation, password sending, and user registration allowance.
* @param int $mode Mode of registration: 1 = Site Registration, 0 = Admin Registration, 2 = Custom Helper Method.
*
* @return int User ID on success.
*
* @throws \RuntimeException If user creation fails.
*
* @since 5.0.3
*/
public static function create(array $credentials, int $autologin = 0,
array $params = ['useractivation' => 0, 'sendpassword' => 1], int $mode = 1): int
{
$lang = Factory::getLanguage();
$lang->load('com_users', JPATH_SITE, 'en-GB', true);
// Handle custom registration mode
if ($mode === 2 && method_exists(ComponentbuilderHelper::class, 'registerUser'))
{
$params['autologin'] = $autologin;
$userId = ComponentbuilderHelper::registerUser($credentials, $params);
if (is_numeric($userId))
{
return $userId;
}
throw new \RuntimeException(Text::_('COM_COMPONENTBUILDER_USER_CREATION_FAILED'));
}
// Check if we have params/config
if (ArrayHelper::check($params))
{
// Make changes to user config
foreach ($params as $param => $set)
{
// If you know of a better path, let me know
$params[$param] = Component::setParams($param, $set, 'com_users');
}
}
// Fallback to Site Registrations if mode is set to 2 but the method doesn't exist
$mode = $mode === 2 ? 1 : $mode;
// Load the appropriate user model
$model = static::getModelByMode($mode);
// Set default values for missing credentials
$credentials['username'] = $credentials['username'] ?? $credentials['email'];
// Prepare user data
$data = static::prepareUserData($credentials, $mode);
// Handle user creation
$userId = $mode === 1 ? $model->register($data) : static::adminRegister($model, $data);
// Check if we have params
if (ArrayHelper::check($params))
{
// Change user params/config back
foreach ($params as $param => $set)
{
// If you know of a better path, let me know
Component::setParams($param, $set, 'com_users');
}
}
if (is_numeric($userId) && $userId > 0)
{
// Handle post-registration processes
return static::handlePostRegistration($userId, $autologin, $credentials);
}
$error_messages = '';
if (method_exists($model, 'getError'))
{
$errors = $model->getError();
if (!empty($errors))
{
$error_messages = '<br>' . implode('<br>', $errors);
}
}
throw new \RuntimeException(
Text::sprintf('COM_COMPONENTBUILDER_USER_S_S_CREATION_FAILEDS',
(string) $credentials['username'],
(string) $credentials['email'],
$error_messages
)
);
}
/**
* Update user details.
*
* @param array $userDetails Array containing user details to be updated.
*
* @return int Updated user ID on success.
*
* @throws \RuntimeException If user update fails.
*
* @since 5.0.3
*/
public static function update(array $userDetails): int
{
$lang = Factory::getLanguage();
$lang->load('com_users', JPATH_ADMINISTRATOR, 'en-GB', true);
$model = Component::getModel('User', 'Administrator', 'com_users');
// Set default values for missing credentials
$userDetails['username'] = $userDetails['username'] ?? $userDetails['email'];
// Prepare user data for update
$data = [
'id' => $userDetails['id'],
'username' => $userDetails['username'],
'name' => $userDetails['name'],
'email' => $userDetails['email'],
'password' => $userDetails['password'] ?? null,
'password2' => $userDetails['password2'] ?? null,
'block' => 0
];
// set groups if found
if (isset($userDetails['groups']) && ArrayHelper::check($userDetails['groups']))
{
$data['groups'] = $userDetails['groups'];
}
// Update the user
if ($model->save($data))
{
return $userDetails['id'];
}
$error_messages = '';
if (method_exists($model, 'getError'))
{
$errors = $model->getError();
if (!empty($errors))
{
$error_messages = '<br>' . implode('<br>', $errors);
}
}
throw new \RuntimeException(
Text::sprintf('COM_COMPONENTBUILDER_UPDATE_OF_USER_S_S_FAILEDS',
(string) $userDetails['username'],
(string) $userDetails['email'],
(string) $error_messages
)
);
}
/**
* Method to get an instance of a user for the given id.
*
* @param int $id The id
*
* @return User
*
* @since 5.0.3
*/
public static function getUserById(int $id): User
{
return new User($id);
}
/**
* Retrieve the user ID by username.
*
* @param string $username The username to check.
*
* @return int|null The user ID if the user exists, null otherwise.
*
* @since 5.0.3
*/
public static function getUserIdByUsername(string $username): ?int
{
$userId = JoomlaUserHelper::getUserId($username);
return $userId ?: null;
}
/**
* Retrieve the user ID by email.
*
* @param string $email The email address to check.
*
* @return int|null The user ID if the user exists, null otherwise.
*
* @since 5.0.3
*/
public static function getUserIdByEmail(string $email): ?int
{
// Initialise some variables
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('id'))
->from($db->quoteName('#__users'))
->where($db->quoteName('email') . ' = :email')
->bind(':email', $email)
->setLimit(1);
$db->setQuery($query);
$userId = $db->loadResult();
return $userId ?: null;
}
/**
* Load the correct user model based on the registration mode.
*
* @param int $mode The registration mode.
*
* @return BaseDatabaseModel The appropriate user model.
*
* @since 5.0.3
*/
protected static function getModelByMode(int $mode): BaseDatabaseModel
{
if ($mode === 1)
{
return Component::getModel('Registration', 'Site', 'com_users');
}
return Component::getModel('User', 'Administrator', 'com_users');
}
/**
* Prepare user data array for registration or update.
*
* @param array $credentials User credentials.
* @param int $mode The registration mode.
*
* @return array The prepared user data array.
*
* @since 5.0.3
*/
protected static function prepareUserData(array $credentials, int $mode)
{
$data = [
'username' => $credentials['username'],
'name' => $credentials['name'],
'block' => 0
];
if ($mode === 1)
{
$data['email1'] = $credentials['email'];
}
else
{
$data['email'] = $credentials['email'];
$data['registerDate'] = Factory::getDate()->toSql();
}
if ($mode === 1 && empty($credentials['password']))
{
$credentials['password'] = StringHelper::random(10);
$credentials['password2'] = $credentials['password'];
}
if (!empty($credentials['password']) && !empty($credentials['password2']))
{
$data['password1'] = $credentials['password'];
$data['password2'] = $credentials['password2'];
}
if ($mode === 0 && isset($credentials['groups']) && ArrayHelper::check($credentials['groups']))
{
$data['groups'] = $credentials['groups'];
}
return $data;
}
/**
* Handle the registration process for admin mode.
*
* @param BaseDatabaseModel $model The user model.
* @param array $data The user data.
*
* @return int The ID of the created user.
*
* @since 5.0.3
*/
private static function adminRegister(BaseDatabaseModel $model, array $data): int
{
$model->save($data);
return $model->getState('user.id', 0);
}
/**
* Handle post-registration processes like auto-login.
*
* @param int $userId The ID of the created user.
* @param int $autologin Flag to determine whether to auto-login the user.
* @param array $credentials The user credentials.
*
* @return int The user ID on success.
*
* @since 5.0.3
*/
private static function handlePostRegistration(int $userId, int $autologin, array $credentials): int
{
if ($autologin && isset($credentials['password']))
{
try
{
Factory::getApplication()->login($credentials);
}
catch (\Exception $e)
{
// we might need to redirect here?
}
}
return $userId;
}
}

View File

@ -0,0 +1,381 @@
/**
* Save user details by either creating a new user or updating an existing user.
*
* @param array $credentials User credentials including 'name', 'username', 'email', 'password', and 'password2'.
* @param int $autologin Flag to determine whether to auto-login the user after registration.
* @param array $params Parameters for user activation, password sending, and user registration allowance.
* @param int $mode Mode of registration: 1 = Site Registration, 0 = Admin Registration, 2 = Custom Helper Method.
*
* @return int User ID on success.
*
* @throws \InvalidArgumentException If required credentials are missing.
* @throws \RuntimeException If the user update or creation fails.
*
* @since 5.0.3
*/
public static function save(array $credentials, int $autologin = 0,
array $params = ['useractivation' => 0, 'sendpassword' => 1], int $mode = 1): int
{
// can not continue without an email
if (empty($credentials['email']))
{
throw new \InvalidArgumentException(Text::_('Can not save user without email value.'));
}
// Ensure the 'username' key exists in the credentials array, set to an empty string if not provided.
$username = $credentials['username'] ?? $credentials['email'];
// Check for an existing user by email or username.
$existingEmailUserId = static::getUserIdByEmail($credentials['email']);
$existingUserId = static::getUserIdByUsername($username);
// If either the email or username already exists, handle the update logic.
if ($existingEmailUserId !== null || $existingUserId !== null || (isset($credentials['id']) && $credentials['id'] > 0))
{
// Prevent updating other users or reusing an email by different users.
if (
(
isset($credentials['id']) &&
(
($existingEmailUserId !== null && $existingEmailUserId != $credentials['id']) ||
($existingUserId !== null && $existingUserId != $credentials['id'])
)
) || ($existingUserId !== null && $existingEmailUserId !== null && $existingEmailUserId != $existingUserId)
)
{
throw new \RuntimeException(Text::sprintf('User ID mismatch detected when trying to save %s (%s) credentials.', $username, $credentials['email']));
}
// Update the existing user.
$credentials['id'] = $credentials['id'] ?? $existingEmailUserId ?? $existingUserId;
return static::update($credentials);
}
// Create a new user if no existing user is found.
return static::create($credentials, $autologin, $params, $mode);
}
/**
* Create a user and update the given table.
*
* @param array $credentials User credentials including 'name', 'username', 'email', 'password', and 'password2'.
* @param int $autologin Flag to determine whether to auto-login the user after registration.
* @param array $params Parameters for user activation, password sending, and user registration allowance.
* @param int $mode Mode of registration: 1 = Site Registration, 0 = Admin Registration, 2 = Custom Helper Method.
*
* @return int User ID on success.
*
* @throws \RuntimeException If user creation fails.
*
* @since 5.0.3
*/
public static function create(array $credentials, int $autologin = 0,
array $params = ['useractivation' => 0, 'sendpassword' => 1], int $mode = 1): int
{
$lang = Factory::getLanguage();
$lang->load('com_users', JPATH_SITE, 'en-GB', true);
// Handle custom registration mode
if ($mode === 2 && method_exists(ComponentbuilderHelper::class, 'registerUser'))
{
$params['autologin'] = $autologin;
$userId = ComponentbuilderHelper::registerUser($credentials, $params);
if (is_numeric($userId))
{
return $userId;
}
throw new \RuntimeException(Text::_('User creation failed!'));
}
// Check if we have params/config
if (ArrayHelper::check($params))
{
// Make changes to user config
foreach ($params as $param => $set)
{
// If you know of a better path, let me know
$params[$param] = Component::setParams($param, $set, 'com_users');
}
}
// Fallback to Site Registrations if mode is set to 2 but the method doesn't exist
$mode = $mode === 2 ? 1 : $mode;
// Load the appropriate user model
$model = static::getModelByMode($mode);
// Set default values for missing credentials
$credentials['username'] = $credentials['username'] ?? $credentials['email'];
// Prepare user data
$data = static::prepareUserData($credentials, $mode);
// Handle user creation
$userId = $mode === 1 ? $model->register($data) : static::adminRegister($model, $data);
// Check if we have params
if (ArrayHelper::check($params))
{
// Change user params/config back
foreach ($params as $param => $set)
{
// If you know of a better path, let me know
Component::setParams($param, $set, 'com_users');
}
}
if (is_numeric($userId) && $userId > 0)
{
// Handle post-registration processes
return static::handlePostRegistration($userId, $autologin, $credentials);
}
$error_messages = '';
if (method_exists($model, 'getError'))
{
$errors = $model->getError();
if (!empty($errors))
{
$error_messages = '<br>' . implode('<br>', $errors);
}
}
throw new \RuntimeException(
Text::sprintf('User %s (%s) creation failed!%s',
(string) $credentials['username'],
(string) $credentials['email'],
$error_messages
)
);
}
/**
* Update user details.
*
* @param array $userDetails Array containing user details to be updated.
*
* @return int Updated user ID on success.
*
* @throws \RuntimeException If user update fails.
*
* @since 5.0.3
*/
public static function update(array $userDetails): int
{
$lang = Factory::getLanguage();
$lang->load('com_users', JPATH_ADMINISTRATOR, 'en-GB', true);
$model = Component::getModel('User', 'Administrator', 'com_users');
// Set default values for missing credentials
$userDetails['username'] = $userDetails['username'] ?? $userDetails['email'];
// Prepare user data for update
$data = [
'id' => $userDetails['id'],
'username' => $userDetails['username'],
'name' => $userDetails['name'],
'email' => $userDetails['email'],
'password' => $userDetails['password'] ?? null,
'password2' => $userDetails['password2'] ?? null,
'block' => 0
];
// set groups if found
if (isset($userDetails['groups']) && ArrayHelper::check($userDetails['groups']))
{
$data['groups'] = $userDetails['groups'];
}
// Update the user
if ($model->save($data))
{
return $userDetails['id'];
}
$error_messages = '';
if (method_exists($model, 'getError'))
{
$errors = $model->getError();
if (!empty($errors))
{
$error_messages = '<br>' . implode('<br>', $errors);
}
}
throw new \RuntimeException(
Text::sprintf('Update of user %s (%s) failed!%s',
(string) $userDetails['username'],
(string) $userDetails['email'],
(string) $error_messages
)
);
}
/**
* Method to get an instance of a user for the given id.
*
* @param int $id The id
*
* @return User
*
* @since 5.0.3
*/
public static function getUserById(int $id): User
{
return new User($id);
}
/**
* Retrieve the user ID by username.
*
* @param string $username The username to check.
*
* @return int|null The user ID if the user exists, null otherwise.
*
* @since 5.0.3
*/
public static function getUserIdByUsername(string $username): ?int
{
$userId = JoomlaUserHelper::getUserId($username);
return $userId ?: null;
}
/**
* Retrieve the user ID by email.
*
* @param string $email The email address to check.
*
* @return int|null The user ID if the user exists, null otherwise.
*
* @since 5.0.3
*/
public static function getUserIdByEmail(string $email): ?int
{
// Initialise some variables
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('id'))
->from($db->quoteName('#__users'))
->where($db->quoteName('email') . ' = :email')
->bind(':email', $email)
->setLimit(1);
$db->setQuery($query);
$userId = $db->loadResult();
return $userId ?: null;
}
/**
* Load the correct user model based on the registration mode.
*
* @param int $mode The registration mode.
*
* @return BaseDatabaseModel The appropriate user model.
*
* @since 5.0.3
*/
protected static function getModelByMode(int $mode): BaseDatabaseModel
{
if ($mode === 1)
{
return Component::getModel('Registration', 'Site', 'com_users');
}
return Component::getModel('User', 'Administrator', 'com_users');
}
/**
* Prepare user data array for registration or update.
*
* @param array $credentials User credentials.
* @param int $mode The registration mode.
*
* @return array The prepared user data array.
*
* @since 5.0.3
*/
protected static function prepareUserData(array $credentials, int $mode)
{
$data = [
'username' => $credentials['username'],
'name' => $credentials['name'],
'block' => 0
];
if ($mode === 1)
{
$data['email1'] = $credentials['email'];
}
else
{
$data['email'] = $credentials['email'];
$data['registerDate'] = Factory::getDate()->toSql();
}
if ($mode === 1 && empty($credentials['password']))
{
$credentials['password'] = StringHelper::random(10);
$credentials['password2'] = $credentials['password'];
}
if (!empty($credentials['password']) && !empty($credentials['password2']))
{
$data['password1'] = $credentials['password'];
$data['password2'] = $credentials['password2'];
}
if ($mode === 0 && isset($credentials['groups']) && ArrayHelper::check($credentials['groups']))
{
$data['groups'] = $credentials['groups'];
}
return $data;
}
/**
* Handle the registration process for admin mode.
*
* @param BaseDatabaseModel $model The user model.
* @param array $data The user data.
*
* @return int The ID of the created user.
*
* @since 5.0.3
*/
private static function adminRegister(BaseDatabaseModel $model, array $data): int
{
$model->save($data);
return $model->getState('user.id', 0);
}
/**
* Handle post-registration processes like auto-login.
*
* @param int $userId The ID of the created user.
* @param int $autologin Flag to determine whether to auto-login the user.
* @param array $credentials The user credentials.
*
* @return int The user ID on success.
*
* @since 5.0.3
*/
private static function handlePostRegistration(int $userId, int $autologin, array $credentials): int
{
if ($autologin && isset($credentials['password']))
{
try
{
Factory::getApplication()->login($credentials);
}
catch (\Exception $e)
{
// we might need to redirect here?
}
}
return $userId;
}

View File

@ -0,0 +1,31 @@
{
"add_head": "1",
"add_licensing_template": "2",
"extends": "",
"guid": "7832a726-87b6-4e95-887e-7b725d3fab8f",
"implements": null,
"load_selection": {
"load_selection0": {
"load": "0a59c65c-9daf-4bc9-baf4-e063ff9e6a8a"
},
"load_selection1": {
"load": "1f28cb53-60d9-4db1-b517-3c7dc6b429ef"
}
},
"name": "UserHelper",
"power_version": "1.0.0",
"system_name": "Joomla.Utilities.UserHelper",
"type": "abstract class",
"use_selection": {
"use_selection0": {
"use": "640b5352-fb09-425f-a26e-cd44eda03f15",
"as": "Component"
}
},
"extendsinterfaces": null,
"namespace": "[[[NamespacePrefix]]]\\Joomla\\[[[ComponentNamespace]]].Utilities.UserHelper",
"description": "Create & Update User [Save]\r\n\r\n@since 5.0.2",
"licensing_template": "\/**\r\n * @package Joomla.Component.Builder\r\n *\r\n * @created 3rd September, 2020\r\n * @author Llewellyn van der Merwe <https:\/\/dev.vdm.io>\r\n * @git Joomla Component Builder <https:\/\/git.vdm.dev\/joomla\/Component-Builder>\r\n * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.\r\n * @license GNU General Public License version 2 or later; see LICENSE.txt\r\n *\/\r\n",
"head": "use Joomla\\CMS\\Factory;\r\nuse Joomla\\CMS\\Language\\Text;\r\nuse Joomla\\CMS\\User\\User;\r\nuse Joomla\\CMS\\User\\UserHelper as JoomlaUserHelper;\r\nuse Joomla\\CMS\\MVC\\Model\\BaseDatabaseModel;",
"composer": ""
}

View File

@ -22,7 +22,6 @@ class Subform << (F,LightGreen) >> #RoyalBlue {
- purge(array $items, string $indexKey, ...) : void - purge(array $items, string $indexKey, ...) : void
- converter(array $items, array $keySet, ...) : array - converter(array $items, array $keySet, ...) : array
- process(mixed $items, string $indexKey, ...) : array - process(mixed $items, string $indexKey, ...) : array
- setGuid(string $key, bool $trim = true) : string
} }
note right of Subform::__construct note right of Subform::__construct
@ -110,18 +109,6 @@ note left of Subform::process
string $linkValue string $linkValue
end note end note
note right of Subform::setGuid
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.
since: 3.0.9
return: string
end note
@enduml @enduml
``` ```

View File

@ -13,6 +13,8 @@ namespace VDM\Joomla\Data;
use VDM\Joomla\Interfaces\Data\ItemsInterface as Items; use VDM\Joomla\Interfaces\Data\ItemsInterface as Items;
use VDM\Joomla\Data\Guid;
use VDM\Joomla\Interfaces\Data\GuidInterface;
use VDM\Joomla\Interfaces\Data\SubformInterface; use VDM\Joomla\Interfaces\Data\SubformInterface;
@ -21,10 +23,17 @@ use VDM\Joomla\Interfaces\Data\SubformInterface;
* *
* @since 3.2.2 * @since 3.2.2
*/ */
final class Subform implements SubformInterface final class Subform implements GuidInterface, SubformInterface
{ {
/** /**
* The ItemsInterface Class. * The Globally Unique Identifier.
*
* @since 5.0.2
*/
use Guid;
/**
* The Items Class.
* *
* @var Items * @var Items
* @since 3.2.2 * @since 3.2.2
@ -42,7 +51,7 @@ final class Subform implements SubformInterface
/** /**
* Constructor. * Constructor.
* *
* @param Items $items The ItemsInterface Class. * @param Items $items The Items Class.
* @param string|null $table The table name. * @param string|null $table The table name.
* *
* @since 3.2.2 * @since 3.2.2
@ -238,7 +247,7 @@ final class Subform implements SubformInterface
if (empty($value)) if (empty($value))
{ {
// set INDEX // set INDEX
$item[$indexKey] = $this->setGuid($indexKey); $item[$indexKey] = $this->getGuid($indexKey);
} }
break; break;
case 'id': case 'id':
@ -257,69 +266,5 @@ final class Subform implements SubformInterface
return array_values($items); 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;
}
} }

View File

@ -1,5 +1,12 @@
/** /**
* The ItemsInterface Class. * The Globally Unique Identifier.
*
* @since 5.0.2
*/
use Guid;
/**
* The Items Class.
* *
* @var Items * @var Items
* @since 3.2.2 * @since 3.2.2
@ -17,7 +24,7 @@
/** /**
* Constructor. * Constructor.
* *
* @param Items $items The ItemsInterface Class. * @param Items $items The Items Class.
* @param string|null $table The table name. * @param string|null $table The table name.
* *
* @since 3.2.2 * @since 3.2.2
@ -213,7 +220,7 @@
if (empty($value)) if (empty($value))
{ {
// set INDEX // set INDEX
$item[$indexKey] = $this->setGuid($indexKey); $item[$indexKey] = $this->getGuid($indexKey);
} }
break; break;
case 'id': case 'id':
@ -232,67 +239,3 @@
return array_values($items); 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;
}

View File

@ -4,6 +4,7 @@
"extends": "", "extends": "",
"guid": "85785701-07b2-4f81-bc1e-0f423700c254", "guid": "85785701-07b2-4f81-bc1e-0f423700c254",
"implements": [ "implements": [
"576685fd-263c-46bb-9fdc-1f5eb234cbb6",
"34959721-415b-4b5e-8002-3d1fc84b3b2b" "34959721-415b-4b5e-8002-3d1fc84b3b2b"
], ],
"load_selection": null, "load_selection": null,
@ -15,6 +16,10 @@
"use_selection0": { "use_selection0": {
"use": "7212e4db-371f-4cfd-8122-32e9bb100d83", "use": "7212e4db-371f-4cfd-8122-32e9bb100d83",
"as": "Items" "as": "Items"
},
"use_selection1": {
"use": "5acded67-0e3d-4c6b-a6ea-b533b076de0c",
"as": "default"
} }
}, },
"extendsinterfaces": null, "extendsinterfaces": null,

View File

@ -20,6 +20,7 @@ class Data #Gold {
+ getItem(Container $container) : Item + getItem(Container $container) : Item
+ getItems(Container $container) : Items + getItems(Container $container) : Items
+ getSubform(Container $container) : Subform + getSubform(Container $container) : Subform
+ getUsersSubform(Container $container) : UsersSubform
+ getMultiSubform(Container $container) : MultiSubform + getMultiSubform(Container $container) : MultiSubform
} }
@ -79,7 +80,14 @@ note left of Data::getSubform
return: Subform return: Subform
end note end note
note right of Data::getMultiSubform note right of Data::getUsersSubform
Get The Users Subform Class.
since: 5.0.2
return: UsersSubform
end note
note left of Data::getMultiSubform
Get The MultiSubform Class. Get The MultiSubform Class.
since: 3.2.0 since: 3.2.0

View File

@ -21,6 +21,7 @@ use VDM\Joomla\Data\Action\Delete;
use VDM\Joomla\Data\Item; use VDM\Joomla\Data\Item;
use VDM\Joomla\Data\Items; use VDM\Joomla\Data\Items;
use VDM\Joomla\Data\Subform; use VDM\Joomla\Data\Subform;
use VDM\Joomla\Data\UsersSubform;
use VDM\Joomla\Data\MultiSubform; use VDM\Joomla\Data\MultiSubform;
@ -62,6 +63,9 @@ class Data implements ServiceProviderInterface
$container->alias(Subform::class, 'Data.Subform') $container->alias(Subform::class, 'Data.Subform')
->share('Data.Subform', [$this, 'getSubform'], true); ->share('Data.Subform', [$this, 'getSubform'], true);
$container->alias(UsersSubform::class, 'Data.UsersSubform')
->share('Data.UsersSubform', [$this, 'getUsersSubform'], true);
$container->alias(MultiSubform::class, 'Data.MultiSubform') $container->alias(MultiSubform::class, 'Data.MultiSubform')
->share('Data.MultiSubform', [$this, 'getMultiSubform'], true); ->share('Data.MultiSubform', [$this, 'getMultiSubform'], true);
} }
@ -182,6 +186,21 @@ class Data implements ServiceProviderInterface
); );
} }
/**
* Get The Users Subform Class.
*
* @param Container $container The DI container.
*
* @return UsersSubform
* @since 5.0.2
*/
public function getUsersSubform(Container $container): UsersSubform
{
return new UsersSubform(
$container->get('Data.Items')
);
}
/** /**
* Get The MultiSubform Class. * Get The MultiSubform Class.
* *

View File

@ -29,6 +29,9 @@
$container->alias(Subform::class, 'Data.Subform') $container->alias(Subform::class, 'Data.Subform')
->share('Data.Subform', [$this, 'getSubform'], true); ->share('Data.Subform', [$this, 'getSubform'], true);
$container->alias(UsersSubform::class, 'Data.UsersSubform')
->share('Data.UsersSubform', [$this, 'getUsersSubform'], true);
$container->alias(MultiSubform::class, 'Data.MultiSubform') $container->alias(MultiSubform::class, 'Data.MultiSubform')
->share('Data.MultiSubform', [$this, 'getMultiSubform'], true); ->share('Data.MultiSubform', [$this, 'getMultiSubform'], true);
} }
@ -149,6 +152,21 @@
); );
} }
/**
* Get The Users Subform Class.
*
* @param Container $container The DI container.
*
* @return UsersSubform
* @since 5.0.2
*/
public function getUsersSubform(Container $container): UsersSubform
{
return new UsersSubform(
$container->get('Data.Items')
);
}
/** /**
* Get The MultiSubform Class. * Get The MultiSubform Class.
* *

View File

@ -40,6 +40,10 @@
"use": "85785701-07b2-4f81-bc1e-0f423700c254", "use": "85785701-07b2-4f81-bc1e-0f423700c254",
"as": "default" "as": "default"
}, },
"use_selection8": {
"use": "46b98346-ec98-42b3-a393-96c7d1282b1c",
"as": "default"
},
"use_selection7": { "use_selection7": {
"use": "e0198c3f-777a-4a0b-87b7-e6a198afc8f9", "use": "e0198c3f-777a-4a0b-87b7-e6a198afc8f9",
"as": "default" "as": "default"

View File

@ -7019,7 +7019,7 @@ final class Table extends BaseTable implements Tableinterface
'db' => [ 'db' => [
'type' => 'TINYINT(1)', 'type' => 'TINYINT(1)',
'default' => '0', 'default' => '0',
'null_switch' => 'NOT NULL', 'null_switch' => 'NULL',
'unique_key' => false, 'unique_key' => false,
'key' => true, 'key' => true,
], ],

View File

@ -445,7 +445,10 @@ final class MultiSubform implements MultiSubformInterface
{ {
if ($this->validSetMap($map)) if ($this->validSetMap($map))
{ {
return $this->setSubformData($subform[$key], $map, [$table => $subform]); // will delete all existing linked items [IF EMPTY] :( not ideal, but real
$data = (empty($subform[$key]) || !is_array($subform[$key])) ? [] : $subform[$key];
return $this->setSubformData($data, $map, [$table => $subform]);
} }
return false; return false;

View File

@ -420,7 +420,10 @@
{ {
if ($this->validSetMap($map)) if ($this->validSetMap($map))
{ {
return $this->setSubformData($subform[$key], $map, [$table => $subform]); // will delete all existing linked items [IF EMPTY] :( not ideal, but real
$data = (empty($subform[$key]) || !is_array($subform[$key])) ? [] : $subform[$key];
return $this->setSubformData($data, $map, [$table => $subform]);
} }
return false; return false;

View File

@ -274,6 +274,17 @@
"spk": "Super---43134867_5cb8_4280_9be8_309fd2fd135f---Power", "spk": "Super---43134867_5cb8_4280_9be8_309fd2fd135f---Power",
"guid": "43134867-5cb8-4280-9be8-309fd2fd135f" "guid": "43134867-5cb8-4280-9be8-309fd2fd135f"
}, },
"46b98346-ec98-42b3-a393-96c7d1282b1c": {
"name": "UsersSubform",
"type": "final class",
"namespace": "VDM\\Joomla\\Data",
"code": "src\/46b98346-ec98-42b3-a393-96c7d1282b1c\/code.php",
"power": "src\/46b98346-ec98-42b3-a393-96c7d1282b1c\/code.power",
"settings": "src\/46b98346-ec98-42b3-a393-96c7d1282b1c\/settings.json",
"path": "src\/46b98346-ec98-42b3-a393-96c7d1282b1c",
"spk": "Super---46b98346_ec98_42b3_a393_96c7d1282b1c---Power",
"guid": "46b98346-ec98-42b3-a393-96c7d1282b1c"
},
"4815e1c7-a433-443d-a112-d1e03d7df84b": { "4815e1c7-a433-443d-a112-d1e03d7df84b": {
"name": "Database", "name": "Database",
"type": "class", "type": "class",
@ -362,6 +373,17 @@
"spk": "Super---52a1d14f_304a_431c_8fa4_411179942db5---Power", "spk": "Super---52a1d14f_304a_431c_8fa4_411179942db5---Power",
"guid": "52a1d14f-304a-431c-8fa4-411179942db5" "guid": "52a1d14f-304a-431c-8fa4-411179942db5"
}, },
"576685fd-263c-46bb-9fdc-1f5eb234cbb6": {
"name": "GuidInterface",
"type": "interface",
"namespace": "VDM\\Joomla\\Interfaces\\Data",
"code": "src\/576685fd-263c-46bb-9fdc-1f5eb234cbb6\/code.php",
"power": "src\/576685fd-263c-46bb-9fdc-1f5eb234cbb6\/code.power",
"settings": "src\/576685fd-263c-46bb-9fdc-1f5eb234cbb6\/settings.json",
"path": "src\/576685fd-263c-46bb-9fdc-1f5eb234cbb6",
"spk": "Super---576685fd_263c_46bb_9fdc_1f5eb234cbb6---Power",
"guid": "576685fd-263c-46bb-9fdc-1f5eb234cbb6"
},
"584747d1-3a86-453d-b7a3-a2219de8d777": { "584747d1-3a86-453d-b7a3-a2219de8d777": {
"name": "Model", "name": "Model",
"type": "abstract class", "type": "abstract class",
@ -384,6 +406,17 @@
"spk": "Super---59b1a2ea_d77e_4040_ac8c_e65cd8743e9b---Power", "spk": "Super---59b1a2ea_d77e_4040_ac8c_e65cd8743e9b---Power",
"guid": "59b1a2ea-d77e-4040-ac8c-e65cd8743e9b" "guid": "59b1a2ea-d77e-4040-ac8c-e65cd8743e9b"
}, },
"5acded67-0e3d-4c6b-a6ea-b533b076de0c": {
"name": "Guid",
"type": "trait",
"namespace": "VDM\\Joomla\\Data",
"code": "src\/5acded67-0e3d-4c6b-a6ea-b533b076de0c\/code.php",
"power": "src\/5acded67-0e3d-4c6b-a6ea-b533b076de0c\/code.power",
"settings": "src\/5acded67-0e3d-4c6b-a6ea-b533b076de0c\/settings.json",
"path": "src\/5acded67-0e3d-4c6b-a6ea-b533b076de0c",
"spk": "Super---5acded67_0e3d_4c6b_a6ea_b533b076de0c---Power",
"guid": "5acded67-0e3d-4c6b-a6ea-b533b076de0c"
},
"5f0205fa-5c43-424a-af7d-abc943c17c8c": { "5f0205fa-5c43-424a-af7d-abc943c17c8c": {
"name": "SchemaChecker", "name": "SchemaChecker",
"type": "abstract class", "type": "abstract class",
@ -505,6 +538,17 @@
"spk": "Super---728ee726_3f0f_4762_899d_f8c9430cee58---Power", "spk": "Super---728ee726_3f0f_4762_899d_f8c9430cee58---Power",
"guid": "728ee726-3f0f-4762-899d-f8c9430cee58" "guid": "728ee726-3f0f-4762-899d-f8c9430cee58"
}, },
"7832a726-87b6-4e95-887e-7b725d3fab8f": {
"name": "UserHelper",
"type": "abstract class",
"namespace": "VDM\\Joomla\\Componentbuilder\\Utilities",
"code": "src\/7832a726-87b6-4e95-887e-7b725d3fab8f\/code.php",
"power": "src\/7832a726-87b6-4e95-887e-7b725d3fab8f\/code.power",
"settings": "src\/7832a726-87b6-4e95-887e-7b725d3fab8f\/settings.json",
"path": "src\/7832a726-87b6-4e95-887e-7b725d3fab8f",
"spk": "Super---7832a726_87b6_4e95_887e_7b725d3fab8f---Power",
"guid": "7832a726-87b6-4e95-887e-7b725d3fab8f"
},
"7c1fb50f-8fb1-4627-8705-6fedf7182ca5": { "7c1fb50f-8fb1-4627-8705-6fedf7182ca5": {
"name": "Upsert", "name": "Upsert",
"type": "final class", "type": "final class",