forked from joomla/Component-Builder
Release of v5.0.4-beta1
Add first classes for the new import engine.
This commit is contained in:
parent
147f2f4cb7
commit
703e8e0133
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,9 +1,6 @@
|
||||
# v5.0.4-alpha3
|
||||
# v5.0.4-beta1
|
||||
|
||||
- Fix Save failed issue in dynamicGet. #1148
|
||||
- Move all [TEXT, EDITOR, TEXTAREA] fields from [NOT NULL] to [NULL]
|
||||
- Add the DateHelper class and improve the date methods.
|
||||
- Add simple SessionHelper class.
|
||||
- Add first classes for the new import engine.
|
||||
|
||||
# v5.0.4-alpha
|
||||
|
||||
@ -14,6 +11,10 @@
|
||||
- Move the setDocument and _prepareDocument above the display in the site view and custom admin view.
|
||||
- Update the trashhelper layout to work in Joomla 5.
|
||||
- Add AllowDynamicProperties (Joomla 4+5) to view class to allow Custom Dynamic Get methods to work without issues.
|
||||
- Fix Save failed issue in dynamicGet. #1148
|
||||
- Move all [TEXT, EDITOR, TEXTAREA] fields from [NOT NULL] to [NULL]
|
||||
- Add the DateHelper class and improve the date methods.
|
||||
- Add simple SessionHelper class.
|
||||
|
||||
# v5.0.3
|
||||
|
||||
|
@ -3270,7 +3270,7 @@ class Com_ComponentbuilderInstallerScript implements InstallerScriptInterface
|
||||
echo '<div style="background-color: #fff;" class="alert alert-info"><a target="_blank" href="https://dev.vdm.io" title="Component Builder">
|
||||
<img src="components/com_componentbuilder/assets/images/vdm-component.jpg"/>
|
||||
</a>
|
||||
<h3>Upgrade to Version 5.0.4-alpha3 Was Successful! Let us know if anything is not working as expected.</h3></div>';
|
||||
<h3>Upgrade to Version 5.0.4-beta1 Was Successful! Let us know if anything is not working as expected.</h3></div>';
|
||||
|
||||
// Add/Update component in the action logs extensions table.
|
||||
$this->setActionLogsExtensions();
|
||||
|
12
README.md
12
README.md
@ -9,7 +9,7 @@ The Component Builder for [Joomla](https://extensions.joomla.org/extension/compo
|
||||
|
||||
Whether you're a seasoned [Joomla](https://extensions.joomla.org/extension/component-builder/) developer, or have just started, Component Builder will save you lots of time and money. A real must have!
|
||||
|
||||
You can install it quite easily and with no limitations. On [gitea](https://git.vdm.dev/joomla/Component-Builder/tags) is the latest release (5.0.4-alpha3) with **ALL** its features and **ALL** concepts totally open-source and free!
|
||||
You can install it quite easily and with no limitations. On [gitea](https://git.vdm.dev/joomla/Component-Builder/tags) is the latest release (5.0.4-beta1) with **ALL** its features and **ALL** concepts totally open-source and free!
|
||||
|
||||
> Watch Quick Build of a Hello World component in [JCB on Youtube](https://www.youtube.com/watch?v=IQfsLYIeblk&list=PLQRGFI8XZ_wtGvPQZWBfDzzlERLQgpMRE&index=45)
|
||||
|
||||
@ -144,14 +144,14 @@ TODO
|
||||
+ *Author*: [Llewellyn van der Merwe](mailto:joomla@vdm.io)
|
||||
+ *Name*: [Component Builder](https://git.vdm.dev/joomla/Component-Builder)
|
||||
+ *First Build*: 30th April, 2015
|
||||
+ *Last Build*: 13th October, 2024
|
||||
+ *Version*: 5.0.4-alpha3
|
||||
+ *Last Build*: 7th November, 2024
|
||||
+ *Version*: 5.0.4-beta1
|
||||
+ *Copyright*: Copyright (C) 2015 Vast Development Method. All rights reserved.
|
||||
+ *License*: GNU General Public License version 2 or later; see LICENSE.txt
|
||||
+ *Line count*: **871857**
|
||||
+ *Line count*: **890506**
|
||||
+ *Field count*: **2104**
|
||||
+ *File count*: **6028**
|
||||
+ *Folder count*: **633**
|
||||
+ *File count*: **6228**
|
||||
+ *Folder count*: **636**
|
||||
|
||||
> This **component** was build with a [Joomla](https://extensions.joomla.org/extension/component-builder/) [Automated Component Builder](https://www.joomlacomponentbuilder.com).
|
||||
> Developed by [Llewellyn van der Merwe](mailto:joomla@vdm.io)
|
||||
|
@ -9,7 +9,7 @@ The Component Builder for [Joomla](https://extensions.joomla.org/extension/compo
|
||||
|
||||
Whether you're a seasoned [Joomla](https://extensions.joomla.org/extension/component-builder/) developer, or have just started, Component Builder will save you lots of time and money. A real must have!
|
||||
|
||||
You can install it quite easily and with no limitations. On [gitea](https://git.vdm.dev/joomla/Component-Builder/tags) is the latest release (5.0.4-alpha3) with **ALL** its features and **ALL** concepts totally open-source and free!
|
||||
You can install it quite easily and with no limitations. On [gitea](https://git.vdm.dev/joomla/Component-Builder/tags) is the latest release (5.0.4-beta1) with **ALL** its features and **ALL** concepts totally open-source and free!
|
||||
|
||||
> Watch Quick Build of a Hello World component in [JCB on Youtube](https://www.youtube.com/watch?v=IQfsLYIeblk&list=PLQRGFI8XZ_wtGvPQZWBfDzzlERLQgpMRE&index=45)
|
||||
|
||||
@ -144,14 +144,14 @@ TODO
|
||||
+ *Author*: [Llewellyn van der Merwe](mailto:joomla@vdm.io)
|
||||
+ *Name*: [Component Builder](https://git.vdm.dev/joomla/Component-Builder)
|
||||
+ *First Build*: 30th April, 2015
|
||||
+ *Last Build*: 13th October, 2024
|
||||
+ *Version*: 5.0.4-alpha3
|
||||
+ *Last Build*: 7th November, 2024
|
||||
+ *Version*: 5.0.4-beta1
|
||||
+ *Copyright*: Copyright (C) 2015 Vast Development Method. All rights reserved.
|
||||
+ *License*: GNU General Public License version 2 or later; see LICENSE.txt
|
||||
+ *Line count*: **871857**
|
||||
+ *Line count*: **890506**
|
||||
+ *Field count*: **2104**
|
||||
+ *File count*: **6028**
|
||||
+ *Folder count*: **633**
|
||||
+ *File count*: **6228**
|
||||
+ *Folder count*: **636**
|
||||
|
||||
> This **component** was build with a [Joomla](https://extensions.joomla.org/extension/component-builder/) [Automated Component Builder](https://www.joomlacomponentbuilder.com).
|
||||
> Developed by [Llewellyn van der Merwe](mailto:joomla@vdm.io)
|
||||
|
@ -4570,6 +4570,7 @@ COM_COMPONENTBUILDER_DYNAMIC_GET_YES="Yes"
|
||||
COM_COMPONENTBUILDER_DYNAMIC_GET_YY="yy"
|
||||
COM_COMPONENTBUILDER_DYNAMIC_GET_Z="z"
|
||||
COM_COMPONENTBUILDER_DYNAMIC_GET_ZZ="zz"
|
||||
COM_COMPONENTBUILDER_D_ROWS_PROCESSED_SUCCESS_RATE_TWOF_IMPORT_SUCCESSFUL="%d rows processed. Success rate: %.2f%%. Import successful!"
|
||||
COM_COMPONENTBUILDER_EDIT="Edit"
|
||||
COM_COMPONENTBUILDER_EDITCREATE_SITE_VIEW="Edit/Create Site View"
|
||||
COM_COMPONENTBUILDER_EDITING="Editing"
|
||||
@ -5440,6 +5441,7 @@ COM_COMPONENTBUILDER_ID="id"
|
||||
COM_COMPONENTBUILDER_ID_MISMATCH_WAS_DETECTED_WITH_THE_SSSS_GUI_CODE_FIELD_SO_THE_PLACEHOLDER_WAS_NOT_SET="ID mismatch was detected with the %s.%s.%s.%s GUI code field. So the placeholder was not set."
|
||||
COM_COMPONENTBUILDER_IEMAILI_BSB="<i>Email:</i> <b>%s</b>"
|
||||
COM_COMPONENTBUILDER_IMPORT_BY_GUID_ONLY="Import by GUID only!"
|
||||
COM_COMPONENTBUILDER_IMPORT_FAILED_D_ROWS_PROCESSED_WITH_ONLY_D_SUCCESSES_ERROR_RATE_TWOF="Import failed. %d rows processed with only %d successes. Error rate: %.2f%%."
|
||||
COM_COMPONENTBUILDER_IMPORT_S="Import %s"
|
||||
COM_COMPONENTBUILDER_IMPORT_SELECT_FILE_FOR_JOOMLA_COMPONENTS="Select the file to import data to joomla_components."
|
||||
COM_COMPONENTBUILDER_IMPORT_SELECT_FILE_FOR_LANGUAGE_TRANSLATIONS="Select the file to import data to language_translations."
|
||||
@ -7598,6 +7600,7 @@ COM_COMPONENTBUILDER_NO_NAMESPACE_FOUND="No Namespace Found"
|
||||
COM_COMPONENTBUILDER_NO_NEED_TO_GET_IT_SINCE_IT_IS_ALREADY_IN_SYNC_WITH_YOUR_LOCAL_VERSION="No need to get it since it is already in sync with your local version"
|
||||
COM_COMPONENTBUILDER_NO_PATHS_FOUND="No Paths Found"
|
||||
COM_COMPONENTBUILDER_NO_RESULTS_MATCH="No results match"
|
||||
COM_COMPONENTBUILDER_NO_ROWS_WERE_PROCESSED="No rows were processed."
|
||||
COM_COMPONENTBUILDER_NO_SELECTION_DETECTED="No selection detected"
|
||||
COM_COMPONENTBUILDER_NO_SNIPPETS_WERE_SELECTED_PLEASE_MAKE_A_SELECTION_AND_TRY_AGAIN="No snippets were selected, please make a selection and try again!"
|
||||
COM_COMPONENTBUILDER_NO_S_FOUND="No %s Found"
|
||||
|
@ -21,6 +21,7 @@ use VDM\Joomla\Componentbuilder\Compiler\Factory as CFactory;
|
||||
use Joomla\CMS\Version;
|
||||
use VDM\Joomla\Componentbuilder\File\Factory as FileFactory;
|
||||
use VDM\Joomla\Componentbuilder\Import\Factory as ImportFactory;
|
||||
use VDM\Joomla\Abstraction\Console\Import;
|
||||
use VDM\Joomla\Utilities\ArrayHelper as UtilitiesArrayHelper;
|
||||
use VDM\Joomla\Utilities\StringHelper;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
@ -76,6 +77,8 @@ class CompilerController extends AdminController
|
||||
* FileFactory
|
||||
* Adding this so that the import factory gets build for Super Powers
|
||||
* ImportFactory
|
||||
* Adding this so that the import cli gets build for Super Powers
|
||||
* Import
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -1,15 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<extension type="component" version="5.0" method="upgrade">
|
||||
<name>COM_COMPONENTBUILDER</name>
|
||||
<creationDate>13th October, 2024</creationDate>
|
||||
<creationDate>7th November, 2024</creationDate>
|
||||
<author>Llewellyn van der Merwe</author>
|
||||
<authorEmail>joomla@vdm.io</authorEmail>
|
||||
<authorUrl>https://dev.vdm.io</authorUrl>
|
||||
<copyright>Copyright (C) 2015 Vast Development Method. All rights reserved.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<version>5.0.4-alpha3</version>
|
||||
<version>5.0.4-beta1</version>
|
||||
<description><![CDATA[
|
||||
<h1>Component Builder (v.5.0.4-alpha3)</h1>
|
||||
<h1>Component Builder (v.5.0.4-beta1)</h1>
|
||||
<div style="clear: both;"></div>
|
||||
<p>The Component Builder for [Joomla](https://extensions.joomla.org/extension/component-builder/) is highly advanced tool that is truly able to build extremely complex components in a fraction of the time.
|
||||
|
||||
|
@ -131,13 +131,13 @@
|
||||
<element>pkg_component_builder</element>
|
||||
<type>package</type>
|
||||
<client>site</client>
|
||||
<version>5.0.4-alpha3</version>
|
||||
<version>5.0.4-beta1</version>
|
||||
<infourl title="Component Builder!">https://dev.vdm.io</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.vdm.dev/api/v1/repos/joomla/pkg-component-builder/archive/v5.0.4-alpha3.zip</downloadurl>
|
||||
<downloadurl type="full" format="zip">https://git.vdm.dev/api/v1/repos/joomla/pkg-component-builder/archive/v5.0.4-beta1.zip</downloadurl>
|
||||
</downloads>
|
||||
<tags>
|
||||
<tag>alpha</tag>
|
||||
<tag>beta</tag>
|
||||
</tags>
|
||||
<maintainer>Llewellyn van der Merwe</maintainer>
|
||||
<maintainerurl>https://dev.vdm.io</maintainerurl>
|
||||
|
@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Abstraction;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Tableinterface;
|
||||
use VDM\Joomla\Interfaces\TableInterface;
|
||||
|
||||
|
||||
/**
|
||||
@ -20,7 +20,7 @@ use VDM\Joomla\Interfaces\Tableinterface;
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
abstract class BaseTable implements Tableinterface
|
||||
abstract class BaseTable implements TableInterface
|
||||
{
|
||||
/**
|
||||
* All areas/views/tables with their field details
|
||||
|
@ -0,0 +1,258 @@
|
||||
<?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\Abstraction\Console;
|
||||
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Console\Command\AbstractCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use VDM\Joomla\Componentbuilder\Import\Factory as ImportFactory;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\Spreadsheet\ImportCliInterface as ImportEngine;
|
||||
use VDM\Joomla\Data\Items;
|
||||
use VDM\Joomla\Utilities\Component\Helper;
|
||||
|
||||
|
||||
/**
|
||||
* Console Import
|
||||
*
|
||||
* @since 5.0.2
|
||||
*/
|
||||
abstract class Import extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* The Items Class.
|
||||
*
|
||||
* @var Items
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected Items $items;
|
||||
|
||||
/**
|
||||
* The Import Class.
|
||||
*
|
||||
* @var ImportEngine
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected ImportEngine $import;
|
||||
|
||||
/**
|
||||
* The queue table name.
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $queueTable;
|
||||
|
||||
/**
|
||||
* The queue status field
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $queueStatusField;
|
||||
|
||||
/**
|
||||
* The queue awaiting status
|
||||
*
|
||||
* @var int
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected int $queueWaitState;
|
||||
|
||||
/**
|
||||
* The queue processing status
|
||||
*
|
||||
* @var int
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected int $queueProcessingState;
|
||||
|
||||
/**
|
||||
* The main import target name.
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $targetName;
|
||||
|
||||
/**
|
||||
* The target import class.
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $targetImportClass;
|
||||
|
||||
/**
|
||||
* The default command name.
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected static $defaultName;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string|null $name The name of the command; if the name is empty and no default is set, a name must be set in the configure() method
|
||||
*
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function __construct(?string $name = null)
|
||||
{
|
||||
// make sure we know what component we are working with
|
||||
Helper::setOption('com_componentbuilder');
|
||||
|
||||
// Load administrator language file for backend
|
||||
$lang = Factory::getLanguage();
|
||||
$lang->load('com_componentbuilder', JPATH_ADMINISTRATOR);
|
||||
|
||||
$this->items = ImportFactory::_('Data.Items');
|
||||
$this->import = ImportFactory::_($this->targetImportClass);
|
||||
|
||||
parent::__construct($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the CLI command, setting up the description and help text.
|
||||
*
|
||||
* This command parses the import queue and imports items that are still in the queue.
|
||||
* It is useful for automatically processing pending item imports in the virtual warehouse.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setDescription("Processes the import queue and {$this->targetName} imports all spreadsheets that are still in the queue.");
|
||||
$this->setHelp(
|
||||
<<<EOF
|
||||
The <info>%command.name%</info> command parses the import queue and processes all {$this->targetName} spreadsheets that are still pending import.
|
||||
This is useful for keeping the system up-to-date with incoming data.
|
||||
|
||||
Usage:
|
||||
<info>php joomla.php %command.name%</info>
|
||||
EOF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the CLI command, processing each spreadsheet in the import queue.
|
||||
*
|
||||
* @param InputInterface $input The input to inject into the command.
|
||||
* @param OutputInterface $output The output to inject into the command.
|
||||
*
|
||||
* @return int The command exit code (0 for success).
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected function doExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
// Output the title for the task
|
||||
$io->title("Component Builder: {$this->targetName} import status");
|
||||
|
||||
// Get all imports in the queue that are in waiting state
|
||||
if (($queue = $this->items->table($this->queueTable)->get([$this->queueWaitState], $this->queueStatusField)) === null)
|
||||
{
|
||||
// Get the current date and time
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
|
||||
// Output the notice of no imports to be done
|
||||
$io->info("No {$this->targetName} imports found in the queue. Idle at {$timestamp}.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// take spreadsheets out of queue
|
||||
$this->items->table($this->queueTable)->set(array_map(function($item) {
|
||||
return [
|
||||
'guid' => $item->guid,
|
||||
$this->queueStatusField => $this->queueProcessingState
|
||||
];
|
||||
}, $queue));
|
||||
|
||||
// size of the queue
|
||||
$numberSteps = count((array) $queue);
|
||||
|
||||
// Output initial task information
|
||||
$io->info("Initiating import for {$numberSteps} {$this->targetName} spreadsheet(s) in the queue.");
|
||||
$io->newLine(2);
|
||||
|
||||
// Create a progress bar for the overall import process
|
||||
$progressBar = $io->createProgressBar($numberSteps);
|
||||
$progressBar->start();
|
||||
|
||||
// Track success and failure counts
|
||||
$successCount = 0;
|
||||
$failureCount = 0;
|
||||
|
||||
// Import one spreadsheet at a time
|
||||
foreach ($queue as $spreadsheet)
|
||||
{
|
||||
$io->newLine(2);
|
||||
|
||||
// Output the current spreadsheet being processed
|
||||
$io->section("Processing spreadsheet #{$spreadsheet->guid}...");
|
||||
|
||||
// Import the data found in the spreadsheet
|
||||
$this->import->data($spreadsheet);
|
||||
|
||||
// Get the completion message
|
||||
$completion = $this->import->message();
|
||||
|
||||
// Track success based on completion message
|
||||
if ($completion->message_success)
|
||||
{
|
||||
$successCount++;
|
||||
|
||||
// Output the success message for this spreadsheet
|
||||
$io->success($completion->message_success);
|
||||
}
|
||||
|
||||
// Track failure based on completion message
|
||||
if ($completion->message_error)
|
||||
{
|
||||
$failureCount++;
|
||||
|
||||
// Output the error message for this spreadsheet
|
||||
$io->error($completion->message_error);
|
||||
}
|
||||
|
||||
// Advance the main progress bar by one step
|
||||
sleep(1);
|
||||
$progressBar->advance();
|
||||
$io->newLine(1);
|
||||
}
|
||||
|
||||
// Finish the main progress bar
|
||||
$progressBar->finish();
|
||||
$io->newLine(2);
|
||||
|
||||
// Calculate the success and failure percentages
|
||||
$totalProcessed = $successCount + $failureCount;
|
||||
$successRate = ($totalProcessed > 0) ? round(($successCount / $totalProcessed) * 100) : 0;
|
||||
$failureRate = ($totalProcessed > 0) ? round(($failureCount / $totalProcessed) * 100) : 0;
|
||||
|
||||
// Get the current date and time
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
|
||||
// Output the success and failure summary with the timestamp
|
||||
$io->info("The {$this->targetName} import finished: {$successRate}% success, {$failureRate}% failure. Completed at {$timestamp}.");
|
||||
|
||||
$io->newLine(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
@ -38,6 +38,14 @@ abstract class Database
|
||||
*/
|
||||
protected string $table;
|
||||
|
||||
/**
|
||||
* Date format to return
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $dateFormat = 'Y-m-d H:i:s';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -62,23 +70,32 @@ abstract class Database
|
||||
**/
|
||||
protected function quote($value)
|
||||
{
|
||||
if ($value === null) // hmm the null does pose an issue (will keep an eye on this)
|
||||
if ($value === null)
|
||||
{
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if (is_numeric($value))
|
||||
{
|
||||
// If the value is a numeric string (e.g., "0123"), treat it as a string to preserve the format
|
||||
if (is_string($value) && ltrim($value, '0') !== $value)
|
||||
{
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
if (filter_var($value, FILTER_VALIDATE_INT))
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
elseif (filter_var($value, FILTER_VALIDATE_FLOAT))
|
||||
|
||||
if (filter_var($value, FILTER_VALIDATE_FLOAT))
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
}
|
||||
elseif (is_bool($value)) // not sure if this will work well (but its correct)
|
||||
|
||||
// Handle boolean values
|
||||
if (is_bool($value))
|
||||
{
|
||||
return $value ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
@ -86,10 +103,10 @@ abstract class Database
|
||||
// For date and datetime values
|
||||
if ($value instanceof \DateTime)
|
||||
{
|
||||
return $this->db->quote($value->format('Y-m-d H:i:s'));
|
||||
return $this->db->quote($value->format($this->getDateFormat()));
|
||||
}
|
||||
|
||||
// For other data types, just escape it
|
||||
// For other types of values, quote as string
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
@ -111,5 +128,16 @@ abstract class Database
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date format to return in the quote
|
||||
*
|
||||
* @return string
|
||||
* @since 5.0.2
|
||||
**/
|
||||
protected function getDateFormat(): string
|
||||
{
|
||||
return $this->dateFormat;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace VDM\Joomla\Abstraction;
|
||||
|
||||
use VDM\Joomla\Utilities\StringHelper;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Interfaces\Tableinterface as Table;
|
||||
use VDM\Joomla\Interfaces\TableInterface as Table;
|
||||
use VDM\Joomla\Interfaces\ModelInterface;
|
||||
|
||||
|
||||
@ -109,7 +109,7 @@ abstract class Model implements ModelInterface
|
||||
|
||||
/**
|
||||
* Model a value of multiple items
|
||||
* Example: $this->items(Array, 'value_key', 'table_name');
|
||||
* Example: $this->values(Array, 'value_key', 'table_name');
|
||||
*
|
||||
* @param array|null $items The array of values
|
||||
* @param string $field The field key
|
||||
|
@ -14,7 +14,7 @@ namespace VDM\Joomla\Abstraction;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Version;
|
||||
use VDM\Joomla\Interfaces\Tableinterface as Table;
|
||||
use VDM\Joomla\Interfaces\TableInterface as Table;
|
||||
use VDM\Joomla\Interfaces\SchemaInterface;
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace VDM\Joomla\Abstraction;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use VDM\Joomla\Interfaces\SchemaInterface as Schema;
|
||||
use VDM\Joomla\Interfaces\Tableinterface as Table;
|
||||
use VDM\Joomla\Interfaces\TableInterface as Table;
|
||||
use VDM\Joomla\Utilities\ClassHelper;
|
||||
use VDM\Joomla\Interfaces\SchemaCheckerInterface;
|
||||
|
||||
|
@ -0,0 +1,130 @@
|
||||
<?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\Componentbuilder\Import;
|
||||
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use VDM\Joomla\Componentbuilder\Import\Data;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\ImportStatusInterface as Status;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\ImportMessageInterface as Message;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\ImportAssessorInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Import Assessor Class
|
||||
*
|
||||
* @since 4.0.3
|
||||
*/
|
||||
final class Assessor implements ImportAssessorInterface
|
||||
{
|
||||
/**
|
||||
* The Data Class.
|
||||
*
|
||||
* @var Data
|
||||
* @since 4.0.3
|
||||
*/
|
||||
protected Data $data;
|
||||
|
||||
/**
|
||||
* The Import Status Class.
|
||||
*
|
||||
* @var Status
|
||||
* @since 4.0.3
|
||||
*/
|
||||
protected Status $status;
|
||||
|
||||
/**
|
||||
* The Import Message Class.
|
||||
*
|
||||
* @var Message
|
||||
* @since 4.0.3
|
||||
*/
|
||||
protected Message $message;
|
||||
|
||||
/**
|
||||
* Constants for defining the success threshold
|
||||
* Minimum success rate to consider the import successful
|
||||
*
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private const SUCCESS_THRESHOLD = 0.80;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Data $data The Data Class.
|
||||
* @param Status $status The Import Status Class.
|
||||
* @param Message $message The Import Message Class.
|
||||
*
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function __construct(Data $data, Status $status, Message $message)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->status = $status;
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the import process and sets the success/error message based on the success rate.
|
||||
*
|
||||
* @param int $rowCounter Total number of rows processed.
|
||||
* @param int $successCounter Number of successfully processed rows.
|
||||
* @param int $errorCounter Number of rows that failed to process.
|
||||
*
|
||||
* @return void
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function evaluate(int $rowCounter, int $successCounter, int $errorCounter): void
|
||||
{
|
||||
// No rows processed case
|
||||
if ($rowCounter === 0)
|
||||
{
|
||||
$this->message->addError(Text::_('COM_COMPONENTBUILDER_NO_ROWS_WERE_PROCESSED'));
|
||||
|
||||
if (($guid = $this->data->get('import.guid')) !== null)
|
||||
{
|
||||
$this->status->set(4, $guid); // Status 4 => completed with errors
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$successRate = $successCounter / $rowCounter;
|
||||
$errorRate = (1 - $successRate) * 100;
|
||||
$successPercentage = $successRate * 100;
|
||||
|
||||
// Determine appropriate message based on success rate
|
||||
if ($successRate >= self::SUCCESS_THRESHOLD)
|
||||
{
|
||||
$this->message->addSuccess(Text::sprintf('COM_COMPONENTBUILDER_D_ROWS_PROCESSED_SUCCESS_RATE_TWOF_IMPORT_SUCCESSFUL',
|
||||
$rowCounter,
|
||||
$successPercentage
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->message->addError(Text::sprintf('COM_COMPONENTBUILDER_IMPORT_FAILED_D_ROWS_PROCESSED_WITH_ONLY_D_SUCCESSES_ERROR_RATE_TWOF',
|
||||
$rowCounter,
|
||||
$successCounter,
|
||||
$errorRate
|
||||
));
|
||||
}
|
||||
|
||||
if (($guid = $this->data->get('import.guid')) !== null)
|
||||
{
|
||||
// Update import status based on success rate
|
||||
$importStatus = ($successPercentage == 100) ? 3 : 4; // 3 => completed, 4 => completed with errors
|
||||
$this->status->set($importStatus, $guid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
<?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\Componentbuilder\Import;
|
||||
|
||||
|
||||
use VDM\Joomla\Abstraction\Registry;
|
||||
|
||||
|
||||
/**
|
||||
* Import Data Registry
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
class Data extends Registry
|
||||
{
|
||||
}
|
||||
|
@ -12,10 +12,12 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Import;
|
||||
|
||||
|
||||
use Joomla\DI\Container;
|
||||
use VDM\Joomla\Service\Table;
|
||||
use VDM\Joomla\Service\Database;
|
||||
use VDM\Joomla\Service\Model;
|
||||
use VDM\Joomla\Service\Data;
|
||||
use VDM\Joomla\Componentbuilder\Import\Service\Import;
|
||||
use VDM\Joomla\Componentbuilder\File\Service\File;
|
||||
use VDM\Joomla\Componentbuilder\Service\Spreadsheet;
|
||||
use VDM\Joomla\Interfaces\FactoryInterface;
|
||||
@ -50,6 +52,7 @@ abstract class Factory extends ExtendingFactory implements FactoryInterface
|
||||
->registerServiceProvider(new Database())
|
||||
->registerServiceProvider(new Model())
|
||||
->registerServiceProvider(new Data())
|
||||
->registerServiceProvider(new Import())
|
||||
->registerServiceProvider(new File())
|
||||
->registerServiceProvider(new Spreadsheet());
|
||||
}
|
||||
|
@ -0,0 +1,270 @@
|
||||
<?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\Componentbuilder\Import;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\TableValidatorInterface as Validator;
|
||||
use VDM\Joomla\Interfaces\Data\ItemInterface as DataItem;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\ImportRowInterface as Row;
|
||||
use VDM\Joomla\Utilities\GuidHelper;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\ImportItemInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Import Item Class
|
||||
*
|
||||
* @since 4.0.3
|
||||
*/
|
||||
final class Item implements ImportItemInterface
|
||||
{
|
||||
/**
|
||||
* The Table Validator Class.
|
||||
*
|
||||
* @var Validator
|
||||
* @since 4.0.3
|
||||
*/
|
||||
protected Validator $validator;
|
||||
|
||||
/**
|
||||
* The Item Class.
|
||||
*
|
||||
* @var Item
|
||||
* @since 4.0.3
|
||||
*/
|
||||
protected DataItem $item;
|
||||
|
||||
/**
|
||||
* The Import Row Class.
|
||||
*
|
||||
* @var Row
|
||||
* @since 4.0.3
|
||||
*/
|
||||
protected Row $row;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Validator $validator The Table ValidatorI Class.
|
||||
* @param DataItem $item The Item Class.
|
||||
* @param Row $row The Import Row Class.
|
||||
*
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function __construct(Validator $validator, DataItem $item, Row $row)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
$this->item = $item;
|
||||
$this->row = $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item from the import row values and ensure it is valid
|
||||
*
|
||||
* @param string $table The table these columns belongs to.
|
||||
* @param array $columns The columns to extract.
|
||||
*
|
||||
* @return array|null
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function get(string $table, array $columns): ?array
|
||||
{
|
||||
$item = [];
|
||||
foreach ($columns as $column => $map)
|
||||
{
|
||||
if (($value = $this->row->getValue($column)) !== null && !isset($item[$map['name']]))
|
||||
{
|
||||
// get the valid importable value
|
||||
$item[$map['name']] = $this->getImportValue($value, $map['name'], $table, $map['link'] ?? null);
|
||||
|
||||
// remove value from global row values set
|
||||
$this->row->unsetValue($column);
|
||||
}
|
||||
}
|
||||
|
||||
return $item ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct value needed for the import of the related row (item).
|
||||
*
|
||||
* @param mixed $value The value from the row.
|
||||
* @param string $field The field name where the value is being stored.
|
||||
* @param string $table The table this field belongs to.
|
||||
* @param array $link The field link values.
|
||||
*
|
||||
* @return mixed
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private function getImportValue($value, string $field, string $table, ?array $link)
|
||||
{
|
||||
// Validate the link array and return the original value if invalid
|
||||
if (empty($link) || $link['type'] !== 1 || empty($link['table']) || empty($link['key']) || empty($link['value']))
|
||||
{
|
||||
return $this->validImportValue($value, $field, $table);
|
||||
}
|
||||
|
||||
// Handle GUID key with validation via GuidHelper
|
||||
if ($link['key'] === 'guid' && GuidHelper::item($value, $link['table']))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Handle numeric ID with validation
|
||||
if ($link['key'] === 'id' && is_numeric($value) && $this->isValueExists($value, $link))
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
// Attempt to retrieve the local value
|
||||
$local_value = $this->getLocalValue($value, $link);
|
||||
|
||||
// If no local value exists, create it if necessary
|
||||
if ($local_value === null)
|
||||
{
|
||||
$local_value = $this->setLocalValue($value, $link);
|
||||
}
|
||||
|
||||
return $this->validImportValue($local_value, $field, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure we have a valid import value
|
||||
*
|
||||
* @param mixed $value The value.
|
||||
* @param string $field The field name where the value is being stored.
|
||||
* @param string $table The table this field belongs to.
|
||||
*
|
||||
* @return mixed
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private function validImportValue($value, string $field, string $table)
|
||||
{
|
||||
// make sure our value will fit in the database table datatype
|
||||
return $this->validator->getValid($value, $field, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the local value from the database table.
|
||||
*
|
||||
* @param mixed $value The value to search for.
|
||||
* @param array $link The field link details.
|
||||
*
|
||||
* @return mixed|null The local value or null if not found.
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private function getLocalValue($value, array $link)
|
||||
{
|
||||
// Attempt to retrieve the value based on the link['value'] and link['key']
|
||||
$local_value = $this->item->table($link['table'])->value($value, $link['value'], $link['key']);
|
||||
|
||||
// If not found, try retrieving by link['key'] and link['key']
|
||||
if ($local_value === null && $this->isValueExists($value, $link))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $local_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value exists in the table for the given link.
|
||||
*
|
||||
* @param mixed $value The value to check.
|
||||
* @param array $link The field link details.
|
||||
*
|
||||
* @return bool True if the value exists, false otherwise.
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private function isValueExists($value, array $link): bool
|
||||
{
|
||||
return $this->item->table($link['table'])->value($value, $link['key'], $link['key']) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new value in the database table if it doesn't already exist.
|
||||
*
|
||||
* @param mixed $value The value to create.
|
||||
* @param array $link The field link details.
|
||||
*
|
||||
* @return mixed|null The newly created value or null if creation failed.
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private function setLocalValue($value, array $link)
|
||||
{
|
||||
// Handle GUID creation if the provided value is not valid
|
||||
if ($link['key'] === 'guid')
|
||||
{
|
||||
if (!GuidHelper::valid($value))
|
||||
{
|
||||
return $this->insertItemWithGuid($value, $link);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Handle ID creation
|
||||
if ($link['key'] === 'id')
|
||||
{
|
||||
if (!is_numeric($value))
|
||||
{
|
||||
return $this->insertItemWithId($value, $link);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// could not create local item (we don't have enough details)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new item with a GUID.
|
||||
*
|
||||
* @param mixed $value The value to insert.
|
||||
* @param array $link The field link details.
|
||||
*
|
||||
* @return string|null The new GUID or null if insertion failed.
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private function insertItemWithGuid($value, array $link): ?string
|
||||
{
|
||||
$guid = GuidHelper::get();
|
||||
$item = (object) [$link['value'] => $value, $link['key'] => $guid];
|
||||
|
||||
if ($this->item->table($link['table'])->set($item, $link['key'], 'insert'))
|
||||
{
|
||||
return $guid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new item with a non-numeric ID.
|
||||
*
|
||||
* @param mixed $value The value to insert.
|
||||
* @param array $link The field link details.
|
||||
*
|
||||
* @return mixed|null The new ID or null if insertion failed.
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private function insertItemWithId($value, array $link)
|
||||
{
|
||||
$item = (object) [$link['key'] => 0, $link['value'] => $value];
|
||||
|
||||
if ($this->item->table($link['table'])->set($item, $link['key'], 'insert'))
|
||||
{
|
||||
return $this->item->table($link['table'])->value($value, $link['value'], $link['key']);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,154 @@
|
||||
<?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\Componentbuilder\Import;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\TableInterface as Table;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\ImportMapperInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Import Mapper Class
|
||||
*
|
||||
* @since 4.0.3
|
||||
*/
|
||||
final class Mapper implements ImportMapperInterface
|
||||
{
|
||||
/**
|
||||
* The Table Class.
|
||||
*
|
||||
* @var Table
|
||||
* @since 4.0.3
|
||||
*/
|
||||
protected Table $table;
|
||||
|
||||
/**
|
||||
* The current parent table map.
|
||||
*
|
||||
* @var array
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private array $parent = [];
|
||||
|
||||
/**
|
||||
* The current join tables map.
|
||||
*
|
||||
* @var array
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private array $join = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Table $table The Table Class.
|
||||
*
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function __construct(Table $table)
|
||||
{
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tables mapper
|
||||
*
|
||||
* @param object $map The import file map.
|
||||
* @param string $parentTable The parent table name.
|
||||
*
|
||||
* @return void
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function set(object $map, string $parentTable): void
|
||||
{
|
||||
// always reset these
|
||||
$this->parent = [];
|
||||
$this->join = [];
|
||||
|
||||
foreach ($map as $row)
|
||||
{
|
||||
$target = $row->target ?? null;
|
||||
|
||||
if (empty($target))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($tm = $this->getTableField($target)) !== null)
|
||||
{
|
||||
$field = $this->table->get($tm->table, $tm->field);
|
||||
if ($tm->table === $parentTable)
|
||||
{
|
||||
$this->parent[$row->column] = $field;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->join[$tm->table][$row->column] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent table keys
|
||||
*
|
||||
* @return array
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function getParent(): array
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the join tables keys
|
||||
*
|
||||
* @return array
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function getJoin(): array
|
||||
{
|
||||
return $this->join;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table and field name
|
||||
*
|
||||
* @param string $key The import file key.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 4.0.3
|
||||
*/
|
||||
private function getTableField(string $key): ?object
|
||||
{
|
||||
// Find the position of the first dot
|
||||
$dotPosition = strpos($key, '.');
|
||||
|
||||
// If no dot is found, return the whole string
|
||||
if ($dotPosition === false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extract the table (before the dot) and the field (after the dot)
|
||||
$table = substr($key, 0, $dotPosition);
|
||||
$field = substr($key, $dotPosition + 1);
|
||||
|
||||
if ($this->table->exist($table ?? '_error', $field))
|
||||
{
|
||||
return (object) ['table' => $table, 'field' => $field];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,307 @@
|
||||
<?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\Componentbuilder\Import;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Data\UpdateInterface as Update;
|
||||
use VDM\Joomla\Interfaces\Data\InsertInterface as Insert;
|
||||
use VDM\Joomla\Utilities\GuidHelper;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\ImportMessageInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Import Messages Class
|
||||
*
|
||||
* @since 5.0.2
|
||||
*/
|
||||
final class Message implements ImportMessageInterface
|
||||
{
|
||||
/**
|
||||
* The Update Class.
|
||||
*
|
||||
* @var Update
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected Update $update;
|
||||
|
||||
/**
|
||||
* The Insert Class.
|
||||
*
|
||||
* @var Insert
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected Insert $insert;
|
||||
|
||||
/**
|
||||
* The success message bus.
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.2
|
||||
*/
|
||||
private array $success = [];
|
||||
|
||||
/**
|
||||
* The info message bus.
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.2
|
||||
*/
|
||||
private array $info = [];
|
||||
|
||||
/**
|
||||
* The error message bus.
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.2
|
||||
*/
|
||||
private array $error = [];
|
||||
|
||||
/**
|
||||
* The entity GUID value.
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
private ?string $guid = null;
|
||||
|
||||
/**
|
||||
* The entity type value.
|
||||
*
|
||||
* @var string|null
|
||||
* @since 5.0.2
|
||||
*/
|
||||
private ?string $entity = null;
|
||||
|
||||
/**
|
||||
* The entity table value.
|
||||
*
|
||||
* @var string|null
|
||||
* @since 5.0.2
|
||||
*/
|
||||
private ?string $table = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Update $update The Update Class.
|
||||
* @param Insert $insert The Insert Class.
|
||||
*
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function __construct(Update $update, Insert $insert)
|
||||
{
|
||||
$this->update = $update;
|
||||
$this->insert = $insert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an entity that these message belong to
|
||||
*
|
||||
* @param string $guid The entity guid these messages must be linked to.
|
||||
* @param string $entity The entity type these messages must be linked to.
|
||||
* @param string $table The messages table where these message must be stored.
|
||||
*
|
||||
* @return self
|
||||
* @throws \InvalidArgumentException if any of the parameters are null or empty.
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function load(string $guid, string $entity, string $table): self
|
||||
{
|
||||
if (empty($guid) || empty($entity) || empty($table))
|
||||
{
|
||||
throw new \InvalidArgumentException('GUID, entity, and table must not be null or empty.');
|
||||
}
|
||||
|
||||
// set entity details
|
||||
$this->guid = $guid;
|
||||
$this->entity = $entity;
|
||||
$this->table = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the messages of the last import event
|
||||
*
|
||||
* @return object
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function get(): object
|
||||
{
|
||||
return (object) [
|
||||
'message_success' => $this->success ?? null,
|
||||
'message_info' => $this->info ?? null,
|
||||
'message_error' => $this->error ?? null
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the messages of the last import event
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function reset(): void
|
||||
{
|
||||
// clear the message bus
|
||||
$this->success = [];
|
||||
$this->info = [];
|
||||
$this->error = [];
|
||||
|
||||
$this->guid = null;
|
||||
$this->entity = null;
|
||||
$this->table = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Archive the messages in the DB of the last import event
|
||||
*
|
||||
* @return self
|
||||
* @throws \InvalidArgumentException if GUID, entity, or table is null.
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function archive(): self
|
||||
{
|
||||
if (empty($this->guid) || empty($this->entity) || empty($this->table))
|
||||
{
|
||||
throw new \InvalidArgumentException('GUID, entity, and table must not be null or empty.');
|
||||
}
|
||||
|
||||
// trash all messages from the past
|
||||
$this->update->table($this->table)->rows([['entity' => $this->guid, 'published' => -2]], 'entity');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the messages in the DB of the last import event
|
||||
*
|
||||
* @return self
|
||||
* @throws \InvalidArgumentException if GUID, entity, or table is null.
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function set(): self
|
||||
{
|
||||
if (empty($this->guid) || empty($this->entity) || empty($this->table))
|
||||
{
|
||||
throw new \InvalidArgumentException('GUID, entity, and table must not be null or empty.');
|
||||
}
|
||||
|
||||
// start message bucket
|
||||
$messages = [];
|
||||
|
||||
// set the success messages
|
||||
if (!empty($this->success))
|
||||
{
|
||||
foreach ($this->success as $message)
|
||||
{
|
||||
$messages[] = [
|
||||
'guid' => GuidHelper::get(),
|
||||
'entity' => $this->guid,
|
||||
'entity_type' => $this->entity,
|
||||
'message' => $message,
|
||||
'message_status' => 1
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// set the info messages
|
||||
if (!empty($this->info))
|
||||
{
|
||||
foreach ($this->info as $message)
|
||||
{
|
||||
$messages[] = [
|
||||
'guid' => GuidHelper::get(),
|
||||
'entity' => $this->guid,
|
||||
'entity_type' => $this->entity,
|
||||
'message' => $message,
|
||||
'message_status' => 2
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// set the error messages
|
||||
if (!empty($this->error))
|
||||
{
|
||||
foreach ($this->error as $message)
|
||||
{
|
||||
$messages[] = [
|
||||
'guid' => GuidHelper::get(),
|
||||
'entity' => $this->guid,
|
||||
'entity_type' => $this->entity,
|
||||
'message' => $message,
|
||||
'message_status' => 3
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->insert->table($this->table)->rows($messages);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a success message to the log.
|
||||
*
|
||||
* This method records a success message for the import process. The message provides
|
||||
* relevant information, such as the number of rows processed and the success rate.
|
||||
*
|
||||
* @param string $message The success message to log.
|
||||
*
|
||||
* @return self
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function addSuccess(string $message): self
|
||||
{
|
||||
$this->success[] = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a info message to the log.
|
||||
*
|
||||
* This method records a info message for the import process. The message provides
|
||||
* relevant information, such as the number of rows processed and the info rate.
|
||||
*
|
||||
* @param string $message The info message to log.
|
||||
*
|
||||
* @return self
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function addInfo(string $message): self
|
||||
{
|
||||
$this->info[] = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error message to the log.
|
||||
*
|
||||
* This method records an error message when the import process encounters issues.
|
||||
* The message includes details about the failures, such as the number of failed rows
|
||||
* and the corresponding error rate.
|
||||
*
|
||||
* @param string $message The error message to log.
|
||||
*
|
||||
* @return self
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function addError(string $message): self
|
||||
{
|
||||
$this->error[] = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,131 @@
|
||||
<?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\Componentbuilder\Import;
|
||||
|
||||
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\ImportRowInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Import Row Class
|
||||
*
|
||||
* @since 4.0.3
|
||||
*/
|
||||
final class Row implements ImportRowInterface
|
||||
{
|
||||
/**
|
||||
* The row array of values.
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.2
|
||||
*/
|
||||
private array $values;
|
||||
|
||||
/**
|
||||
* The row index.
|
||||
*
|
||||
* @var int
|
||||
* @since 5.0.2
|
||||
*/
|
||||
private int $index;
|
||||
|
||||
/**
|
||||
* A flag to track if values and index are set.
|
||||
*
|
||||
* @var bool
|
||||
* @since 5.0.2
|
||||
*/
|
||||
private bool $isSet = false;
|
||||
|
||||
/**
|
||||
* Set the row details
|
||||
*
|
||||
* @param int $index The row index
|
||||
* @param array $values The values
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function set(int $index, array $values): void
|
||||
{
|
||||
$this->index = $index;
|
||||
$this->values = $values;
|
||||
$this->isSet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the row details
|
||||
*
|
||||
* @return self
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function clear(): self
|
||||
{
|
||||
$this->index = 0;
|
||||
$this->values = [];
|
||||
$this->isSet = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Index
|
||||
*
|
||||
* @return int
|
||||
* @throws \InvalidArgumentException if any of the parameters are null or empty.
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function getIndex(): int
|
||||
{
|
||||
if (!$this->isSet)
|
||||
{
|
||||
throw new \InvalidArgumentException('Index must not be null or empty. Use the set method to first set the index.');
|
||||
}
|
||||
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Value
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \InvalidArgumentException if any of the parameters are null or empty.
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function getValue(string $key)
|
||||
{
|
||||
if (!$this->isSet)
|
||||
{
|
||||
throw new \InvalidArgumentException('Values must be set before accessing. Use the set method to first set the values.');
|
||||
}
|
||||
|
||||
return $this->values[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset Value
|
||||
*
|
||||
* @return void
|
||||
* @throws \InvalidArgumentException if any of the parameters are null or empty.
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function unsetValue(string $key): void
|
||||
{
|
||||
if (!$this->isSet)
|
||||
{
|
||||
throw new \InvalidArgumentException('Values must be set before accessing. Use the set method to first set the values.');
|
||||
}
|
||||
|
||||
unset($this->values[$key]);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,171 @@
|
||||
<?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\Componentbuilder\Import\Service;
|
||||
|
||||
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use VDM\Joomla\Componentbuilder\Import\Data;
|
||||
use VDM\Joomla\Componentbuilder\Import\Mapper;
|
||||
use VDM\Joomla\Componentbuilder\Import\Row;
|
||||
use VDM\Joomla\Componentbuilder\Import\Item;
|
||||
use VDM\Joomla\Componentbuilder\Import\Message;
|
||||
use VDM\Joomla\Componentbuilder\Import\Status;
|
||||
use VDM\Joomla\Componentbuilder\Import\Assessor;
|
||||
|
||||
|
||||
/**
|
||||
* Import Service Provider
|
||||
*
|
||||
* @since 5.0.3
|
||||
*/
|
||||
class Import implements ServiceProviderInterface
|
||||
{
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->alias(Data::class, 'Import.Data')
|
||||
->share('Import.Data', [$this, 'getData'], true);
|
||||
|
||||
$container->alias(Mapper::class, 'Import.Mapper')
|
||||
->share('Import.Mapper', [$this, 'getMapper'], true);
|
||||
|
||||
$container->alias(Row::class, 'Import.Row')
|
||||
->share('Import.Row', [$this, 'getRow'], true);
|
||||
|
||||
$container->alias(Item::class, 'Import.Item')
|
||||
->share('Import.Item', [$this, 'getItem'], true);
|
||||
|
||||
$container->alias(Message::class, 'Import.Message')
|
||||
->share('Import.Message', [$this, 'getMessage'], true);
|
||||
|
||||
$container->alias(Status::class, 'Import.Status')
|
||||
->share('Import.Status', [$this, 'getStatus'], true);
|
||||
|
||||
$container->alias(Assessor::class, 'Import.Assessor')
|
||||
->share('Import.Assessor', [$this, 'getAssessor'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Data Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Data
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function getData(Container $container): Data
|
||||
{
|
||||
return new Data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Mapper Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Mapper
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function getMapper(Container $container): Mapper
|
||||
{
|
||||
return new Mapper(
|
||||
$container->get('Table')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Row Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Row
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function getRow(Container $container): Row
|
||||
{
|
||||
return new Row();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Item Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Item
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function getItem(Container $container): Item
|
||||
{
|
||||
return new Item(
|
||||
$container->get('Table.Validator'),
|
||||
$container->get('Data.Item'),
|
||||
$container->get('Import.Row')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Message Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Message
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function getMessage(Container $container): Message
|
||||
{
|
||||
return new Message(
|
||||
$container->get('Data.Update'),
|
||||
$container->get('Data.Insert')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Status Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Status
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function getStatus(Container $container): Status
|
||||
{
|
||||
return new Status(
|
||||
$container->get('Data.Item')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Assessor Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Assessor
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function getAssessor(Container $container): Assessor
|
||||
{
|
||||
return new Assessor(
|
||||
$container->get('Import.Data'),
|
||||
$container->get('Import.Status'),
|
||||
$container->get('Import.Message')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
@ -0,0 +1,149 @@
|
||||
<?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\Componentbuilder\Import;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Data\ItemInterface as Item;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\ImportStatusInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Import Status Class
|
||||
*
|
||||
* @since 5.0.2
|
||||
*/
|
||||
final class Status implements ImportStatusInterface
|
||||
{
|
||||
/**
|
||||
* The Item Class.
|
||||
*
|
||||
* @var Item
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected Item $item;
|
||||
|
||||
/**
|
||||
* Table Name
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $table;
|
||||
|
||||
/**
|
||||
* Status Field Name
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $fieldName;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Item $item The Item Class.
|
||||
* @param string|null $table The table name
|
||||
* @param string|null $field The field name.
|
||||
*
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function __construct(Item $item, ?string $table = null, ?string $field = null)
|
||||
{
|
||||
$this->item = $item;
|
||||
|
||||
if ($table !== null)
|
||||
{
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
if ($field !== null)
|
||||
{
|
||||
$this->field = $field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the status in the database.
|
||||
*
|
||||
* This method updates the import status in the database based on the result of the import process.
|
||||
* Status codes:
|
||||
* - 2: Being Processed.
|
||||
* - 3: Import completed successfully.
|
||||
* - 4: Import completed with errors.
|
||||
*
|
||||
* @param int $status The status code to set for the import (2 => processing, 3 => success, 4 => errors).
|
||||
* @param string $guid The target import GUID
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function set(int $status, string $guid): void
|
||||
{
|
||||
$this->item->table($this->getTable())->set((object) [
|
||||
'guid' => $guid,
|
||||
$this->getField() => $status
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current target status field name
|
||||
*
|
||||
* @param string $fieldName The field name where the status is set
|
||||
*
|
||||
* @return self
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function field(string $fieldName): self
|
||||
{
|
||||
$this->fieldName = $fieldName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current active table
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getTable(): string
|
||||
{
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current target status field name
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getField(): string
|
||||
{
|
||||
return $this->fieldName;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?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\Componentbuilder\Interfaces;
|
||||
|
||||
|
||||
/**
|
||||
* Import Assessor Interface
|
||||
*
|
||||
* @since 3.0.3
|
||||
*/
|
||||
interface ImportAssessorInterface
|
||||
{
|
||||
/**
|
||||
* Evaluates the import process and sets the success/error message based on the success rate.
|
||||
*
|
||||
* @param int $rowCounter Total number of rows processed.
|
||||
* @param int $successCounter Number of successfully processed rows.
|
||||
* @param int $errorCounter Number of rows that failed to process.
|
||||
*
|
||||
* @return void
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function evaluate(int $rowCounter, int $successCounter, int $errorCounter): void;
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
<?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\Componentbuilder\Interfaces;
|
||||
|
||||
|
||||
/**
|
||||
* Import Item Interface
|
||||
*
|
||||
* @since 3.0.3
|
||||
*/
|
||||
interface ImportItemInterface
|
||||
{
|
||||
/**
|
||||
* Get the item from the import row values and ensure it is valid
|
||||
*
|
||||
* @param string $table The table these columns belongs to.
|
||||
* @param array $columns The columns to extract.
|
||||
*
|
||||
* @return array|null
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function get(string $table, array $columns): ?array;
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
<?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\Componentbuilder\Interfaces;
|
||||
|
||||
|
||||
/**
|
||||
* Import Mapper Interface
|
||||
*
|
||||
* @since 3.0.3
|
||||
*/
|
||||
interface ImportMapperInterface
|
||||
{
|
||||
/**
|
||||
* Set the tables mapper
|
||||
*
|
||||
* @param object $map The import file map.
|
||||
* @param string $parentTable The parent table name.
|
||||
*
|
||||
* @return void
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function set(object $map, string $parentTable): void;
|
||||
|
||||
/**
|
||||
* Get the parent table keys
|
||||
*
|
||||
* @return array
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function getParent(): array;
|
||||
|
||||
/**
|
||||
* Get the join tables keys
|
||||
*
|
||||
* @return array
|
||||
* @since 4.0.3
|
||||
*/
|
||||
public function getJoin(): array;
|
||||
}
|
||||
|
@ -0,0 +1,109 @@
|
||||
<?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\Componentbuilder\Interfaces;
|
||||
|
||||
|
||||
/**
|
||||
* Import Message Interface
|
||||
*
|
||||
* @since 3.0.2
|
||||
*/
|
||||
interface ImportMessageInterface
|
||||
{
|
||||
/**
|
||||
* Load an entity that these message belong to
|
||||
*
|
||||
* @param string $guid The entity guid these messages must be linked to.
|
||||
* @param string $entity The entity type these messages must be linked to.
|
||||
* @param string $table The messages table where these message must be stored.
|
||||
*
|
||||
* @return self
|
||||
* @throws \InvalidArgumentException if any of the parameters are null or empty.
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public function load(string $guid, string $entity, string $table): self;
|
||||
|
||||
/**
|
||||
* Get the messages of the last import event
|
||||
*
|
||||
* @return object
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public function get(): object;
|
||||
|
||||
/**
|
||||
* Reset the messages of the last import event
|
||||
*
|
||||
* @return void
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public function reset(): void;
|
||||
|
||||
/**
|
||||
* Archive the messages in the DB of the last import event
|
||||
*
|
||||
* @return self
|
||||
* @throws \InvalidArgumentException if GUID, entity, or table is null.
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public function archive(): self;
|
||||
|
||||
/**
|
||||
* Set the messages in the DB of the last import event
|
||||
*
|
||||
* @return self
|
||||
* @throws \InvalidArgumentException if GUID, entity, or table is null.
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public function set(): self;
|
||||
|
||||
/**
|
||||
* Adds a success message to the log.
|
||||
*
|
||||
* This method records a success message for the import process. The message provides
|
||||
* relevant information, such as the number of rows processed and the success rate.
|
||||
*
|
||||
* @param string $message The success message to log.
|
||||
*
|
||||
* @return self
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public function addSuccess(string $message): self;
|
||||
|
||||
/**
|
||||
* Adds a info message to the log.
|
||||
*
|
||||
* This method records a info message for the import process. The message provides
|
||||
* relevant information, such as the number of rows processed and the info rate.
|
||||
*
|
||||
* @param string $message The info message to log.
|
||||
*
|
||||
* @return self
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public function addInfo(string $message): self;
|
||||
|
||||
/**
|
||||
* Adds an error message to the log.
|
||||
*
|
||||
* This method records an error message when the import process encounters issues.
|
||||
* The message includes details about the failures, such as the number of failed rows
|
||||
* and the corresponding error rate.
|
||||
*
|
||||
* @param string $message The error message to log.
|
||||
*
|
||||
* @return self
|
||||
* @since 3.0.2
|
||||
*/
|
||||
public function addError(string $message): self;
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
<?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\Componentbuilder\Interfaces;
|
||||
|
||||
|
||||
/**
|
||||
* Import Row Interface
|
||||
*
|
||||
* @since 3.0.3
|
||||
*/
|
||||
interface ImportRowInterface
|
||||
{
|
||||
/**
|
||||
* Set the row details
|
||||
*
|
||||
* @param int $index The row index
|
||||
* @param array $values The values
|
||||
*
|
||||
* @return void
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public function set(int $index, array $values): void;
|
||||
|
||||
/**
|
||||
* Clear the row details
|
||||
*
|
||||
* @return self
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public function clear(): self;
|
||||
|
||||
/**
|
||||
* Get Index
|
||||
*
|
||||
* @return int
|
||||
* @throws \InvalidArgumentException if any of the parameters are null or empty.
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public function getIndex(): int;
|
||||
|
||||
/**
|
||||
* Get Value
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \InvalidArgumentException if any of the parameters are null or empty.
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public function getValue(string $key);
|
||||
|
||||
/**
|
||||
* Unset Value
|
||||
*
|
||||
* @return void
|
||||
* @throws \InvalidArgumentException if any of the parameters are null or empty.
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public function unsetValue(string $key): void;
|
||||
}
|
||||
|
@ -0,0 +1,75 @@
|
||||
<?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\Componentbuilder\Interfaces;
|
||||
|
||||
|
||||
/**
|
||||
* Import Status Interface
|
||||
*
|
||||
* @since 3.2.2
|
||||
*/
|
||||
interface ImportStatusInterface
|
||||
{
|
||||
/**
|
||||
* Updates the status in the database.
|
||||
*
|
||||
* This method updates the import status in the database based on the result of the import process.
|
||||
* Status codes:
|
||||
* - 2: Being Processed.
|
||||
* - 3: Import completed successfully.
|
||||
* - 4: Import completed with errors.
|
||||
*
|
||||
* @param int $status The status code to set for the import (2 => processing, 3 => success, 4 => errors).
|
||||
* @param string $guid The target import GUID
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function set(int $status, string $guid): void;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Set the current target status field name
|
||||
*
|
||||
* @param string $fieldName The field name where the status is set
|
||||
*
|
||||
* @return self
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function field(string $fieldName): self;
|
||||
|
||||
/**
|
||||
* Get the current active table
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getTable(): string;
|
||||
|
||||
/**
|
||||
* Get the current target status field name
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getField(): string;
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?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\Componentbuilder\Interfaces\Spreadsheet;
|
||||
|
||||
|
||||
/**
|
||||
* Spreadsheet File Reader Interface
|
||||
*
|
||||
* @since 3.2.2
|
||||
*/
|
||||
interface FileReaderInterface
|
||||
{
|
||||
/**
|
||||
* Stream rows from a CSV or Excel file one by one using yield.
|
||||
*
|
||||
* @param string $filePath The path to the file.
|
||||
* @param int $startRow The starting row index.
|
||||
* @param int $chunkSize The number of rows to read per chunk.
|
||||
*
|
||||
* @return \Generator A generator that yields each row as an array.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function read(string $filePath, int $startRow, int $chunkSize): \Generator;
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
<?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\Componentbuilder\Interfaces\Spreadsheet;
|
||||
|
||||
|
||||
/**
|
||||
* Spreadsheet Import Cli Interface
|
||||
*
|
||||
* @since 3.2.2
|
||||
*/
|
||||
interface ImportCliInterface
|
||||
{
|
||||
/**
|
||||
* The trigger function called from the CLI to start the import on a spreadsheet
|
||||
*
|
||||
* @param object $import The spreadsheet data to import.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function data(object $import): void;
|
||||
|
||||
/**
|
||||
* The message of the last import event
|
||||
*
|
||||
* @return object
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function message(): object;
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?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\Componentbuilder\Interfaces\Spreadsheet;
|
||||
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Row;
|
||||
|
||||
|
||||
/**
|
||||
* Spreadsheet Row Data Processor Interface
|
||||
*
|
||||
* @since 3.2.2
|
||||
*/
|
||||
interface RowDataProcessorInterface
|
||||
{
|
||||
/**
|
||||
* Processes the given spreadsheet row and returns it in a specific format.
|
||||
*
|
||||
* @param Row $row The row object from the spreadsheet to be processed.
|
||||
*
|
||||
* @return mixed Processed row data, could be an array, cell object, or other structures.
|
||||
*/
|
||||
public function process(Row $row): mixed;
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
@ -17,6 +17,7 @@ use Joomla\DI\ServiceProviderInterface;
|
||||
use VDM\Joomla\Componentbuilder\Spreadsheet\Header;
|
||||
use VDM\Joomla\Componentbuilder\Spreadsheet\Exporter;
|
||||
use VDM\Joomla\Componentbuilder\Spreadsheet\Importer;
|
||||
use VDM\Joomla\Componentbuilder\Spreadsheet\FileReader;
|
||||
|
||||
|
||||
/**
|
||||
@ -44,6 +45,9 @@ class Spreadsheet implements ServiceProviderInterface
|
||||
|
||||
$container->alias(Importer::class, 'Spreadsheet.Importer')
|
||||
->share('Spreadsheet.Importer', [$this, 'getImporter'], true);
|
||||
|
||||
$container->alias(FileReader::class, 'Spreadsheet.FileReader')
|
||||
->share('Spreadsheet.FileReader', [$this, 'getFileReader'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,7 +86,22 @@ class Spreadsheet implements ServiceProviderInterface
|
||||
*/
|
||||
public function getImporter(Container $container): Importer
|
||||
{
|
||||
return new Importer();
|
||||
return new Importer(
|
||||
$container->get('Spreadsheet.FileReader')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The FileReader Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return FileReader
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function getFileReader(Container $container): FileReader
|
||||
{
|
||||
return new FileReader();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,109 @@
|
||||
<?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\Componentbuilder\Spreadsheet;
|
||||
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use VDM\Joomla\Componentbuilder\Spreadsheet\ChunkReadFilter;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\Spreadsheet\FileReaderInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Spreadsheet File Reader Class
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
final class FileReader implements FileReaderInterface
|
||||
{
|
||||
/**
|
||||
* Stream rows from a CSV or Excel file one by one using yield.
|
||||
*
|
||||
* @param string $filePath The path to the file.
|
||||
* @param int $startRow The starting row index.
|
||||
* @param int $chunkSize The number of rows to read per chunk.
|
||||
*
|
||||
* @return \Generator A generator that yields each row as an array.
|
||||
* @throws \InvalidArgumentException If the file does not exist.
|
||||
* @throws \OutOfRangeException If the start row is beyond the highest row, no rows can be processed.
|
||||
* @throws ReaderException If there is an error identifying or reading the file.
|
||||
* @throws SpreadsheetException If there is an error working with the spreadsheet.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function read(string $filePath, int $startRow, int $chunkSize): \Generator
|
||||
{
|
||||
// Check if the file exists
|
||||
if (!is_file($filePath))
|
||||
{
|
||||
throw new \InvalidArgumentException("File not found: $filePath");
|
||||
}
|
||||
|
||||
try {
|
||||
// Identify file type and create reader
|
||||
$inputFileType = IOFactory::identify($filePath);
|
||||
$reader = IOFactory::createReader($inputFileType);
|
||||
$reader->setReadDataOnly(true);
|
||||
|
||||
// Load the entire spreadsheet to determine the highest row
|
||||
$spreadsheet = $reader->load($filePath);
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
$highestRow = $worksheet->getHighestRow(); // Get the highest row number in the sheet
|
||||
|
||||
// Disconnect and free memory after fetching the highest row
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
unset($spreadsheet);
|
||||
|
||||
// If the start row is beyond the highest row, no rows can be processed
|
||||
if ($startRow > $highestRow)
|
||||
{
|
||||
throw new \OutOfRangeException("Start row ($startRow) is beyond highest row ($highestRow)");
|
||||
}
|
||||
|
||||
// Initialize variables for row processing
|
||||
$totalRows = $startRow;
|
||||
|
||||
do {
|
||||
// Calculate the last row in the current chunk
|
||||
$endRow = min($totalRows + $chunkSize - 1, $highestRow);
|
||||
|
||||
// Set up a new chunk filter for the current chunk
|
||||
$chunkFilter = new ChunkReadFilter($totalRows, $endRow);
|
||||
$reader->setReadFilter($chunkFilter);
|
||||
|
||||
// Reload the chunk into the spreadsheet
|
||||
$spreadsheet = $reader->load($filePath);
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
// Iterate through the rows in the current chunk
|
||||
foreach ($worksheet->getRowIterator($totalRows, $endRow) as $row)
|
||||
{
|
||||
yield $row;
|
||||
|
||||
// Update the row index for the next chunk
|
||||
$totalRows = $row->getRowIndex() + 1;
|
||||
}
|
||||
|
||||
// Disconnect the spreadsheet to free memory
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
unset($spreadsheet);
|
||||
|
||||
} while ($totalRows <= $highestRow); // Continue reading while within the row limit
|
||||
|
||||
} catch (ReaderException $e) {
|
||||
throw new ReaderException("Error reading the file: " . $e->getMessage(), $e->getCode(), $e);
|
||||
} catch (SpreadsheetException $e) {
|
||||
throw new SpreadsheetException("Error with the spreadsheet: " . $e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ final class Header
|
||||
if ($row->getRowIndex() === $targetRow)
|
||||
{
|
||||
$cellIterator = $row->getCellIterator();
|
||||
$cellIterator->setIterateOnlyExistingCells(false);
|
||||
$cellIterator->setIterateOnlyExistingCells(true);
|
||||
foreach ($cellIterator as $cell)
|
||||
{
|
||||
$headers[$cell->getColumn()] = $cell->getValue();
|
||||
|
@ -12,10 +12,8 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Spreadsheet;
|
||||
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use VDM\Joomla\Componentbuilder\Spreadsheet\ChunkReadFilter;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\Spreadsheet\FileReaderInterface as FileReader;
|
||||
use VDM\Joomla\Componentbuilder\Interfaces\Spreadsheet\RowDataProcessorInterface as RowDataProcessor;
|
||||
|
||||
|
||||
/**
|
||||
@ -25,12 +23,33 @@ use VDM\Joomla\Componentbuilder\Spreadsheet\ChunkReadFilter;
|
||||
*/
|
||||
final class Importer
|
||||
{
|
||||
/**
|
||||
* The FileReader Class.
|
||||
*
|
||||
* @var FileReader
|
||||
* @since 3.0.8
|
||||
*/
|
||||
protected FileReader $filereader;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param FileReader $filereader The FileReader Class.
|
||||
*
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public function __construct(FileReader $filereader)
|
||||
{
|
||||
$this->filereader = $filereader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream rows from a CSV or Excel file one by one using yield.
|
||||
*
|
||||
* @param string $filePath The path to the file.
|
||||
* @param int $startRow The starting row index (default is 1).
|
||||
* @param int $chunkSize The number of rows to read per chunk (default is 100).
|
||||
* @param int $startRow The starting row index.
|
||||
* @param int $chunkSize The number of rows to read per chunk.
|
||||
* @param RowDataProcessor $processor The processor used to transform the row data into the desired format.
|
||||
*
|
||||
* @return \Generator A generator that yields each row as an array.
|
||||
* @throws \InvalidArgumentException If the file does not exist.
|
||||
@ -39,81 +58,11 @@ final class Importer
|
||||
* @throws SpreadsheetException If there is an error working with the spreadsheet.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function get(string $filePath, int $startRow = 1, int $chunkSize = 100): \Generator
|
||||
public function read(string $filePath, int $startRow, int $chunkSize, RowDataProcessor $processor): \Generator
|
||||
{
|
||||
// Check if the file exists
|
||||
if (!is_file($filePath))
|
||||
foreach ($this->filereader->read($filePath, $startRow, $chunkSize) as $row)
|
||||
{
|
||||
throw new \InvalidArgumentException("File not found: $filePath");
|
||||
}
|
||||
|
||||
try {
|
||||
// Identify file type and create reader
|
||||
$inputFileType = IOFactory::identify($filePath);
|
||||
$reader = IOFactory::createReader($inputFileType);
|
||||
$reader->setReadDataOnly(true);
|
||||
|
||||
// Load the entire spreadsheet to determine the highest row
|
||||
$spreadsheet = $reader->load($filePath);
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
$highestRow = $worksheet->getHighestRow(); // Get the highest row number in the sheet
|
||||
|
||||
// Disconnect and free memory after fetching the highest row
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
unset($spreadsheet);
|
||||
|
||||
// If the start row is beyond the highest row, no rows can be processed
|
||||
if ($startRow > $highestRow)
|
||||
{
|
||||
throw new \OutOfRangeException("Start row ($startRow) is beyond highest row ($highestRow)");
|
||||
}
|
||||
|
||||
// Initialize variables for row processing
|
||||
$totalRows = $startRow;
|
||||
|
||||
do {
|
||||
// Calculate the last row in the current chunk
|
||||
$endRow = min($totalRows + $chunkSize - 1, $highestRow);
|
||||
|
||||
// Set up a new chunk filter for the current chunk
|
||||
$chunkFilter = new ChunkReadFilter($totalRows, $endRow);
|
||||
$reader->setReadFilter($chunkFilter);
|
||||
|
||||
// Reload the chunk into the spreadsheet
|
||||
$spreadsheet = $reader->load($filePath);
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
// Iterate through the rows in the current chunk
|
||||
foreach ($worksheet->getRowIterator($totalRows, $endRow) as $row)
|
||||
{
|
||||
$rowIndex = $row->getRowIndex();
|
||||
$rowData = [];
|
||||
|
||||
$cellIterator = $row->getCellIterator();
|
||||
$cellIterator->setIterateOnlyExistingCells(false); // Include empty cells
|
||||
|
||||
// Collect all cell data in the row
|
||||
foreach ($cellIterator as $cell)
|
||||
{
|
||||
$rowData[$cell->getColumn()] = $cell->getValue();
|
||||
}
|
||||
|
||||
yield $rowData;
|
||||
|
||||
// Update the row index for the next chunk
|
||||
$totalRows = $rowIndex + 1;
|
||||
}
|
||||
|
||||
// Disconnect the spreadsheet to free memory
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
unset($spreadsheet);
|
||||
|
||||
} while ($totalRows <= $highestRow); // Continue reading while within the row limit
|
||||
|
||||
} catch (ReaderException $e) {
|
||||
throw new ReaderException("Error reading the file: " . $e->getMessage(), $e->getCode(), $e);
|
||||
} catch (SpreadsheetException $e) {
|
||||
throw new SpreadsheetException("Error with the spreadsheet: " . $e->getMessage(), $e->getCode(), $e);
|
||||
yield $processor->process($row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Tableinterface;
|
||||
use VDM\Joomla\Interfaces\TableInterface;
|
||||
use VDM\Joomla\Abstraction\BaseTable;
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ use VDM\Joomla\Abstraction\BaseTable;
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
final class Table extends BaseTable implements Tableinterface
|
||||
final class Table extends BaseTable implements TableInterface
|
||||
{
|
||||
/**
|
||||
* All areas/views/tables with their field details
|
||||
|
@ -0,0 +1,426 @@
|
||||
<?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\Componentbuilder\Table;
|
||||
|
||||
|
||||
use VDM\Joomla\Componentbuilder\Table;
|
||||
use VDM\Joomla\Interfaces\TableValidatorInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Table Value Validator
|
||||
*
|
||||
* @since 5.3.0
|
||||
*/
|
||||
final class Validator implements TableValidatorInterface
|
||||
{
|
||||
/**
|
||||
* The Table Class.
|
||||
*
|
||||
* @var Table
|
||||
* @since 5.3.0
|
||||
*/
|
||||
protected Table $table;
|
||||
|
||||
/**
|
||||
* A map of MySQL base types to their respective validation methods.
|
||||
*
|
||||
* @var array
|
||||
* @since 5.3.0
|
||||
*/
|
||||
protected array $validators = [];
|
||||
|
||||
/**
|
||||
* A map of defaults for the respective datatypes.
|
||||
*
|
||||
* @var array
|
||||
* @since 5.3.0
|
||||
*/
|
||||
protected array $defaults = [];
|
||||
|
||||
/**
|
||||
* Cache of the parsed datatype details
|
||||
*
|
||||
* @var array
|
||||
* @since 5.3.0
|
||||
*/
|
||||
protected array $datatypes = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Table $table The Table Class.
|
||||
*
|
||||
* @since 5.3.0
|
||||
*/
|
||||
public function __construct(Table $table)
|
||||
{
|
||||
$this->table = $table;
|
||||
|
||||
// Register datatype validators (mapping MySQL types to handlers)
|
||||
$this->registerValidators();
|
||||
|
||||
// Register datatype defaults
|
||||
$this->registerDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the valid value based on datatype definition.
|
||||
* If the value is valid, return it. If not, return the default value,
|
||||
* NULL (if allowed), or an empty string if 'EMPTY' is set.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param string $field The field name.
|
||||
* @param string $table The table name.
|
||||
*
|
||||
* @return mixed Returns the valid value, or the default, NULL, or empty string based on validation.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
public function getValid($value, string $field, string $table)
|
||||
{
|
||||
// Get the database field definition
|
||||
if (($dbField = $this->getDatabaseField($field, $table)) === null)
|
||||
{
|
||||
return null; // not legal field or table
|
||||
}
|
||||
|
||||
// Check if the value is valid for the field
|
||||
if ($this->validate($value, $dbField))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
// If invalid, return default, NULL (if allowed), or empty string
|
||||
return $this->getDefault($dbField, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if the given value is valid for the provided database field.
|
||||
* This is a private method as `getValid()` will handle the actual logic.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $dbField The database field details (type, default, null_switch, etc.).
|
||||
*
|
||||
* @return bool Returns true if the value is valid, false otherwise.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function validate($value, array $dbField): bool
|
||||
{
|
||||
// Extract datatype and handle the validation
|
||||
$typeInfo = $this->parseDataType($dbField['type']);
|
||||
$baseType = $typeInfo['type'];
|
||||
|
||||
// Use the appropriate validator if it exists
|
||||
if (isset($this->validators[$baseType]))
|
||||
{
|
||||
return call_user_func($this->validators[$baseType], $value, $typeInfo);
|
||||
}
|
||||
|
||||
// If no validator exists, assume invalid
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle returning the default value, null, or empty string if validation fails.
|
||||
*
|
||||
* @param array $dbField The database field details.
|
||||
* @param mixed $value The value to validate.
|
||||
*
|
||||
* @return mixed The default value, null, or empty string based on field settings.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function getDefault(array $dbField, $value)
|
||||
{
|
||||
// get default value from field db
|
||||
$db_default = isset($dbField['default']) ? $dbField['default'] : null;
|
||||
|
||||
// If a default value is provided, return it
|
||||
if ($db_default !== null)
|
||||
{
|
||||
return strtoupper($db_default) === 'EMPTY' ? '' : $db_default;
|
||||
}
|
||||
|
||||
// Check if NULL is allowed
|
||||
if (isset($dbField['null_switch']) && strtoupper($dbField['null_switch']) === 'NULL')
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fallback to datatype default
|
||||
$typeInfo = $this->parseDataType($dbField['type']);
|
||||
return $this->defaults[$typeInfo['type']] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the data type from the database field and extract details like type, size, and precision.
|
||||
*
|
||||
* @param string $datatype The full MySQL datatype (e.g., VARCHAR(255)).
|
||||
*
|
||||
* @return array An array containing 'type', 'size', and other relevant info.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function parseDataType(string $datatype): array
|
||||
{
|
||||
if (isset($this->datatypes[$datatype]))
|
||||
{
|
||||
return $this->datatypes[$datatype];
|
||||
}
|
||||
|
||||
$pattern = '/(?<type>\w+)(\((?<size>\d+)(,\s*(?<precision>\d+))?\))?/i';
|
||||
preg_match($pattern, $datatype, $matches);
|
||||
|
||||
$result = [
|
||||
'type' => isset($matches['type']) ? strtolower($matches['type']) : strtolower($datatype),
|
||||
'size' => $matches['size'] ?? null,
|
||||
'precision' => $matches['precision'] ?? null,
|
||||
];
|
||||
|
||||
return $this->datatypes[$datatype] = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the database field structure for the specified field and table.
|
||||
* In your case, you use `$db = $this->table->get($table, $field, 'db')`.
|
||||
*
|
||||
* @param string $field The field name.
|
||||
* @param string $table The table name.
|
||||
*
|
||||
* @return array The database field details, including type, default, null_switch, etc.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function getDatabaseField(string $field, string $table): array
|
||||
{
|
||||
// Simulated retrieval of field details. Replace with actual logic.
|
||||
return $this->table->get($table, $field, 'db');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register validators for MySQL data types.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function registerValidators(): void
|
||||
{
|
||||
$this->validators = [
|
||||
'int' => [$this, 'validateInteger'],
|
||||
'tinyint' => [$this, 'validateInteger'],
|
||||
'smallint' => [$this, 'validateInteger'],
|
||||
'mediumint' => [$this, 'validateInteger'],
|
||||
'bigint' => [$this, 'validateInteger'],
|
||||
'varchar' => [$this, 'validateString'],
|
||||
'char' => [$this, 'validateString'],
|
||||
'text' => [$this, 'validateText'],
|
||||
'tinytext' => [$this, 'validateText'],
|
||||
'mediumtext' => [$this, 'validateText'],
|
||||
'longtext' => [$this, 'validateText'],
|
||||
'decimal' => [$this, 'validateDecimal'],
|
||||
'float' => [$this, 'validateFloat'],
|
||||
'double' => [$this, 'validateFloat'],
|
||||
'date' => [$this, 'validateDate'],
|
||||
'datetime' => [$this, 'validateDate'],
|
||||
'timestamp' => [$this, 'validateDate'],
|
||||
'time' => [$this, 'validateDate'],
|
||||
'json' => [$this, 'validateJson'],
|
||||
'blob' => [$this, 'validateBlob'],
|
||||
'tinyblob' => [$this, 'validateBlob'],
|
||||
'mediumblob' => [$this, 'validateBlob'],
|
||||
'longblob' => [$this, 'validateBlob'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register default values for MySQL data types.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function registerDefaults(): void
|
||||
{
|
||||
$this->defaults = [
|
||||
'int' => 0,
|
||||
'tinyint' => 0,
|
||||
'smallint' => 0,
|
||||
'mediumint' => 0,
|
||||
'bigint' => 0,
|
||||
'varchar' => '',
|
||||
'char' => '',
|
||||
'text' => '',
|
||||
'tinytext' => '',
|
||||
'mediumtext' => '',
|
||||
'longtext' => '',
|
||||
'decimal' => 0.0,
|
||||
'float' => 0.0,
|
||||
'double' => 0.0,
|
||||
'date' => '0000-00-00',
|
||||
'datetime' => '0000-00-00 00:00:00',
|
||||
'timestamp' => '0000-00-00 00:00:00',
|
||||
'time' => '00:00:00',
|
||||
'json' => '{}',
|
||||
'blob' => '',
|
||||
'tinyblob' => '',
|
||||
'mediumblob' => '',
|
||||
'longblob' => '',
|
||||
];
|
||||
}
|
||||
|
||||
// ----------------- Validation Methods -----------------
|
||||
|
||||
/**
|
||||
* Validate integer types (including tinyint, smallint, mediumint, etc.).
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $typeInfo The parsed data type information.
|
||||
*
|
||||
* @return bool True if valid, false otherwise.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function validateInteger($value, array $typeInfo): bool
|
||||
{
|
||||
if (!is_numeric($value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = (int)$value;
|
||||
if (isset($typeInfo['unsigned']) && $typeInfo['unsigned'] && $value < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate string types like VARCHAR and CHAR.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $typeInfo The parsed data type information.
|
||||
*
|
||||
* @return bool True if valid, false otherwise.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function validateString($value, array $typeInfo): bool
|
||||
{
|
||||
if (!is_string($value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the length exceeds the allowed size
|
||||
if ($typeInfo['size'] !== null && strlen($value) > (int)$typeInfo['size'])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate text types like TEXT, TINYTEXT, MEDIUMTEXT, LONGTEXT.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $typeInfo The parsed data type information.
|
||||
*
|
||||
* @return bool True if valid, false otherwise.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function validateText($value, array $typeInfo): bool
|
||||
{
|
||||
return is_string($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate float, double, and decimal types.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $typeInfo The parsed data type information.
|
||||
*
|
||||
* @return bool True if valid, false otherwise.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function validateFloat($value, array $typeInfo): bool
|
||||
{
|
||||
return is_numeric($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate decimal types (numeric precision and scale).
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $typeInfo The parsed data type information.
|
||||
*
|
||||
* @return bool True if valid, false otherwise.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function validateDecimal($value, array $typeInfo): bool
|
||||
{
|
||||
return is_numeric($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate date, datetime, timestamp, and time types.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $typeInfo The parsed data type information.
|
||||
*
|
||||
* @return bool True if valid, false otherwise.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function validateDate($value, array $typeInfo): bool
|
||||
{
|
||||
$formats = [
|
||||
'date' => 'Y-m-d',
|
||||
'datetime' => 'Y-m-d H:i:s',
|
||||
'timestamp' => 'Y-m-d H:i:s',
|
||||
'time' => 'H:i:s',
|
||||
];
|
||||
|
||||
if (!isset($formats[$typeInfo['type']]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$dateTime = \DateTime::createFromFormat($formats[$typeInfo['type']], $value);
|
||||
return $dateTime && $dateTime->format($formats[$typeInfo['type']]) === $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate JSON types.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $typeInfo The parsed data type information.
|
||||
*
|
||||
* @return bool True if valid, false otherwise.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function validateJson($value, array $typeInfo): bool
|
||||
{
|
||||
json_decode($value);
|
||||
return json_last_error() === JSON_ERROR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate BLOB types (including TINYBLOB, MEDIUMBLOB, LONGBLOB).
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param array $typeInfo The parsed data type information.
|
||||
*
|
||||
* @return bool True if valid, false otherwise.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private function validateBlob($value, array $typeInfo): bool
|
||||
{
|
||||
return is_string($value) || is_resource($value);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace VDM\Joomla\Interfaces;
|
||||
/**
|
||||
* The VDM Core Table Interface
|
||||
*/
|
||||
interface Tableinterface
|
||||
interface TableInterface
|
||||
{
|
||||
/**
|
||||
* Get any value from a item/field/column of an area/view/table
|
@ -0,0 +1,34 @@
|
||||
<?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;
|
||||
|
||||
|
||||
/**
|
||||
* The VDM Core Table Validator Interface
|
||||
*/
|
||||
interface TableValidatorInterface
|
||||
{
|
||||
/**
|
||||
* Returns the valid value based on datatype definition.
|
||||
* If the value is valid, return it. If not, return the default value,
|
||||
* NULL (if allowed), or an empty string if 'EMPTY' is set.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param string $field The field name.
|
||||
* @param string $table The table name.
|
||||
*
|
||||
* @return mixed Returns the valid value, or the default, NULL, or empty string based on validation.
|
||||
* @since 5.3.0
|
||||
*/
|
||||
public function getValid($value, string $field, string $table);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use VDM\Joomla\Componentbuilder\Table as DataTable;
|
||||
use VDM\Joomla\Componentbuilder\Table\Schema;
|
||||
use VDM\Joomla\Componentbuilder\Table\Validator;
|
||||
|
||||
|
||||
/**
|
||||
@ -40,6 +41,9 @@ class Table implements ServiceProviderInterface
|
||||
|
||||
$container->alias(Schema::class, 'Table.Schema')
|
||||
->share('Table.Schema', [$this, 'getSchema'], true);
|
||||
|
||||
$container->alias(Validator::class, 'Table.Validator')
|
||||
->share('Table.Validator', [$this, 'getValidator'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,5 +73,20 @@ class Table implements ServiceProviderInterface
|
||||
$container->get('Table')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Validator Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Validator
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getValidator(Container $container): Validator
|
||||
{
|
||||
return new Validator(
|
||||
$container->get('Table')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user