#10782: implement form manager for task specific form elements

This commit is contained in:
Daniel Seifert 2021-06-25 15:33:57 +02:00
parent 34cc327896
commit 5367dfdb5a
Signed by: DanielS
GPG Key ID: 8A7C4C6ED1915C6F
15 changed files with 253 additions and 23 deletions

View File

@ -28,7 +28,8 @@
"php": ">=7.1", "php": ">=7.1",
"oxid-esales/oxideshop-ce": "6.3 - 6.8", "oxid-esales/oxideshop-ce": "6.3 - 6.8",
"league/csv": "^9.0", "league/csv": "^9.0",
"mathieuviossat/arraytotexttable": "^1.0" "mathieuviossat/arraytotexttable": "^1.0",
"form-manager/form-manager": "^6.1"
}, },
"extra": { "extra": {
"oxideshop": { "oxideshop": {

View File

@ -15,6 +15,10 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Model; namespace D3\DataWizard\Application\Model;
use D3\DataWizard\Application\Model\Exceptions\InputUnvalidException;
use FormManager\Inputs\Checkbox;
use FormManager\Inputs\Input;
use FormManager\Inputs\Radio;
use OxidEsales\Eshop\Core\DatabaseProvider; use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
@ -22,6 +26,8 @@ use OxidEsales\Eshop\Core\Registry;
abstract class ActionBase implements QueryBase abstract class ActionBase implements QueryBase
{ {
protected $formElements = [];
/** /**
* @return string * @return string
*/ */
@ -36,6 +42,15 @@ abstract class ActionBase implements QueryBase
*/ */
public function run() public function run()
{ {
if ($this->hasFormElements()) {
/** @var Input $element */
foreach ($this->getFormElements() as $element) {
if (false === $element->isValid()) {
throw oxNew(InputUnvalidException::class, $this, $element);
}
}
}
$this->executeAction( $this->getQuery() ); $this->executeAction( $this->getQuery() );
} }
@ -81,4 +96,38 @@ abstract class ActionBase implements QueryBase
{ {
return "D3_DATAWIZARD_ACTION_SUBMIT"; return "D3_DATAWIZARD_ACTION_SUBMIT";
} }
/**
* @param Input $input
*/
public function registerFormElement(Input $input)
{
switch (get_class($input)) {
case Radio::class:
case Checkbox::class:
$input->setTemplate('<p class="form-check">{{ input }} {{ label }}</p>');
$input->setAttribute('class', 'form-check-input');
break;
default:
$input->setTemplate('<p class="formElements">{{ label }} {{ input }}</p>');
$input->setAttribute('class', 'form-control');
}
$this->formElements[] = $input;
}
/**
* @return bool
*/
public function hasFormElements(): bool
{
return (bool) count($this->formElements);
}
/**
* @return array
*/
public function getFormElements(): array
{
return $this->formElements;
}
} }

View File

@ -16,6 +16,7 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Model; namespace D3\DataWizard\Application\Model;
use D3\DataWizard\Application\Model\Actions\FixArtextendsItems; use D3\DataWizard\Application\Model\Actions\FixArtextendsItems;
use D3\DataWizard\Application\Model\Exceptions\DataWizardException;
use D3\DataWizard\Application\Model\Exports\InactiveCategories; use D3\DataWizard\Application\Model\Exports\InactiveCategories;
use D3\DataWizard\Application\Model\Exports\KeyFigures; use D3\DataWizard\Application\Model\Exports\KeyFigures;
use OxidEsales\Eshop\Core\Registry; use OxidEsales\Eshop\Core\Registry;
@ -53,7 +54,7 @@ class Configuration
*/ */
public function registerAction($group, ActionBase $action) public function registerAction($group, ActionBase $action)
{ {
$this->actions[$group][md5(serialize($action))] = $action; $this->actions[$group][md5(get_class($action))] = $action;
} }
/** /**
@ -62,7 +63,7 @@ class Configuration
*/ */
public function registerExport($group, ExportBase $export) public function registerExport($group, ExportBase $export)
{ {
$this->exports[$group][md5(serialize($export))] = $export; $this->exports[$group][md5(get_class($export))] = $export;
} }
/** /**
@ -162,6 +163,12 @@ class Configuration
*/ */
public function getExportById($id) : ExportBase public function getExportById($id) : ExportBase
{ {
return $this->getAllExports()[$id]; $allExports = $this->getAllExports();
if (false == $allExports[$id]) {
throw oxNew(DataWizardException::class, 'no export with id '.$id);
}
return $allExports[$id];
} }
} }

View File

@ -15,6 +15,8 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Model\Exceptions; namespace D3\DataWizard\Application\Model\Exceptions;
interface DataWizardException use OxidEsales\Eshop\Core\Exception\StandardException;
class DataWizardException extends StandardException
{ {
} }

View File

@ -19,7 +19,7 @@ use Exception;
use OxidEsales\Eshop\Core\Exception\StandardException; use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry; use OxidEsales\Eshop\Core\Registry;
class DebugException extends StandardException implements DataWizardException class DebugException extends DataWizardException
{ {
public function __construct($sMessage = "not set", $iCode = 0, Exception $previous = null ) public function __construct($sMessage = "not set", $iCode = 0, Exception $previous = null )
{ {

View File

@ -0,0 +1,46 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\DataWizard\Application\Model\Exceptions;
use D3\DataWizard\Application\Model\QueryBase;
use Exception;
use FormManager\Inputs\Input;
use OxidEsales\Eshop\Core\Exception\StandardException;
class InputUnvalidException extends DataWizardException
{
/** @var QueryBase */
public $task;
public function __construct( QueryBase $task, Input $inputElement, $iCode = 0, Exception $previous = null )
{
$messages = [];
foreach ($inputElement->getError()->getIterator() as $item) {
$messages[] = $inputElement->label->innerHTML.' -> '.$item->getMessage();
}
$sMessage = implode(
' - ',
[
$task->getTitle(),
implode(', ', $messages)
]
);
parent::__construct( $sMessage, $iCode, $previous );
$this->task = $task;
}
}

View File

@ -19,7 +19,7 @@ use Exception;
use OxidEsales\Eshop\Core\Exception\StandardException; use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry; use OxidEsales\Eshop\Core\Registry;
class NoSuitableRendererException extends StandardException implements DataWizardException class NoSuitableRendererException extends DataWizardException
{ {
public function __construct($sMessage = "not set", $iCode = 0, Exception $previous = null ) public function __construct($sMessage = "not set", $iCode = 0, Exception $previous = null )
{ {

View File

@ -15,8 +15,6 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Model\Exceptions; namespace D3\DataWizard\Application\Model\Exceptions;
use OxidEsales\Eshop\Core\Exception\StandardException; class RenderException extends DataWizardException
class RenderException extends StandardException implements DataWizardException
{ {
} }

View File

@ -17,9 +17,8 @@ namespace D3\DataWizard\Application\Model\Exceptions;
use D3\DataWizard\Application\Model\QueryBase; use D3\DataWizard\Application\Model\QueryBase;
use Exception; use Exception;
use OxidEsales\Eshop\Core\Exception\StandardException;
class TaskException extends StandardException implements DataWizardException class TaskException extends DataWizardException
{ {
/** @var QueryBase */ /** @var QueryBase */
public $task; public $task;

View File

@ -15,11 +15,15 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Model; namespace D3\DataWizard\Application\Model;
use D3\DataWizard\Application\Model\Exceptions\InputUnvalidException;
use D3\DataWizard\Application\Model\ExportRenderer\RendererBridge; use D3\DataWizard\Application\Model\ExportRenderer\RendererBridge;
use D3\ModCfg\Application\Model\d3filesystem; use D3\ModCfg\Application\Model\d3filesystem;
use D3\ModCfg\Application\Model\Exception\d3_cfg_mod_exception; use D3\ModCfg\Application\Model\Exception\d3_cfg_mod_exception;
use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException; use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use FormManager\Inputs\Checkbox;
use FormManager\Inputs\Input;
use FormManager\Inputs\Radio;
use OxidEsales\Eshop\Core\DatabaseProvider; use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
@ -28,6 +32,8 @@ use OxidEsales\Eshop\Core\Registry;
abstract class ExportBase implements QueryBase abstract class ExportBase implements QueryBase
{ {
protected $formElements = [];
/** /**
* @return string * @return string
*/ */
@ -50,6 +56,15 @@ abstract class ExportBase implements QueryBase
*/ */
public function run($format = RendererBridge::FORMAT_CSV) public function run($format = RendererBridge::FORMAT_CSV)
{ {
if ($this->hasFormElements()) {
/** @var Input $element */
foreach ($this->getFormElements() as $element) {
if (false === $element->isValid()) {
throw oxNew(InputUnvalidException::class, $this, $element);
}
}
}
[ $rows, $fieldNames ] = $this->getExportData( $this->getQuery() ); [ $rows, $fieldNames ] = $this->getExportData( $this->getQuery() );
$content = $this->renderContent($rows, $fieldNames, $format); $content = $this->renderContent($rows, $fieldNames, $format);
@ -160,4 +175,38 @@ abstract class ExportBase implements QueryBase
return [ $rows, $fieldNames ]; return [ $rows, $fieldNames ];
} }
/**
* @param Input $input
*/
public function registerFormElement(Input $input)
{
switch (get_class($input)) {
case Radio::class:
case Checkbox::class:
$input->setTemplate('<p class="form-check">{{ input }} {{ label }}</p>');
$input->setAttribute('class', 'form-check-input');
break;
default:
$input->setTemplate('<p class="formElements">{{ label }} {{ input }}</p>');
$input->setAttribute('class', 'form-control');
}
$this->formElements[] = $input;
}
/**
* @return bool
*/
public function hasFormElements(): bool
{
return (bool) count($this->formElements);
}
/**
* @return array
*/
public function getFormElements(): array
{
return $this->formElements;
}
} }

View File

@ -16,15 +16,45 @@
namespace D3\DataWizard\Application\Model\Exports; namespace D3\DataWizard\Application\Model\Exports;
use D3\DataWizard\Application\Model\ExportBase; use D3\DataWizard\Application\Model\ExportBase;
use FormManager\Inputs\Date;
use OxidEsales\Eshop\Application\Model\Order; use OxidEsales\Eshop\Application\Model\Order;
use OxidEsales\Eshop\Core\Registry; use OxidEsales\Eshop\Core\Registry;
use FormManager\Factory as FormFactory;
class KeyFigures extends ExportBase class KeyFigures extends ExportBase
{ {
const STARTDATE_NAME = 'startdate';
const ENDDATE_NAME = 'enddate';
/** /**
* Shopkennzahlen * Shopkennzahlen
*/ */
public function __construct()
{
/** @var Date $startDate */
$startDateValue = Registry::getRequest()->getRequestEscapedParameter(self::STARTDATE_NAME);
$startDate = FormFactory::date(
Registry::getLang()->translateString('D3_DATAWIZARD_EXPORTS_KEYFIGURES_FIELD_STARTDATE'),
[
'name' => self::STARTDATE_NAME,
'value' => $startDateValue
]
);
$this->registerFormElement($startDate);
/** @var Date $endDate */
$endDateValue = Registry::getRequest()->getRequestEscapedParameter(self::ENDDATE_NAME);
$endDate = FormFactory::date(
Registry::getLang()->translateString('D3_DATAWIZARD_EXPORTS_KEYFIGURES_FIELD_ENDDATE'),
[
'name' => self::ENDDATE_NAME,
'value' => $endDateValue
]
);
$this->registerFormElement($endDate);
}
/** /**
* @return string * @return string
*/ */
@ -43,19 +73,25 @@ class KeyFigures extends ExportBase
$basketsTitle = Registry::getLang()->translateString('D3_DATAWIZARD_EXPORTS_KEYFIGURES_BASKETSIZE'); $basketsTitle = Registry::getLang()->translateString('D3_DATAWIZARD_EXPORTS_KEYFIGURES_BASKETSIZE');
$monthTitle = Registry::getLang()->translateString('D3_DATAWIZARD_EXPORTS_KEYFIGURES_MONTH'); $monthTitle = Registry::getLang()->translateString('D3_DATAWIZARD_EXPORTS_KEYFIGURES_MONTH');
$startDateValue = Registry::getRequest()->getRequestEscapedParameter(self::STARTDATE_NAME) ?: '1970-01-01';
$endDateValue = Registry::getRequest()->getRequestEscapedParameter(self::ENDDATE_NAME) ?: date('Y-m-d');
return [ return [
'SELECT 'SELECT
DATE_FORMAT(oo.oxorderdate, "%Y-%m") as :monthTitle, DATE_FORMAT(oo.oxorderdate, "%Y-%m") as :monthTitle,
FORMAT(COUNT(oo.oxid), 0) AS :ordersTitle, FORMAT(COUNT(oo.oxid), 0) AS :ordersTitle,
FORMAT(SUM(oo.OXTOTALBRUTSUM / oo.oxcurrate) / COUNT(oo.oxid), 2) as :basketsTitle FORMAT(SUM(oo.OXTOTALBRUTSUM / oo.oxcurrate) / COUNT(oo.oxid), 2) as :basketsTitle
FROM '.$orderTable.' AS oo FROM '.$orderTable.' AS oo
GROUP BY :monthTitle WHERE oo.oxorderdate >= :startDate AND oo.oxorderdate <= :endDate
ORDER BY :monthTitle DESC GROUP BY DATE_FORMAT(oo.oxorderdate, "%Y-%m")
ORDER BY DATE_FORMAT(oo.oxorderdate, "%Y-%m") DESC
LIMIT 30', LIMIT 30',
[ [
'monthTitle' => $monthTitle, 'startDate' => $startDateValue,
'ordersTitle' => $ordersTitle, 'endDate' => $endDateValue,
'basketsTitle' => $basketsTitle 'monthTitle' => $monthTitle,
'ordersTitle' => $ordersTitle,
'basketsTitle' => $basketsTitle
] ]
]; ];
} }

View File

@ -15,6 +15,8 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Model; namespace D3\DataWizard\Application\Model;
use FormManager\Inputs\Input;
interface QueryBase interface QueryBase
{ {
public function run(); public function run();
@ -38,4 +40,19 @@ interface QueryBase
* @return array [string $query, array $parameters] * @return array [string $query, array $parameters]
*/ */
public function getQuery() : array; public function getQuery() : array;
/**
* @param Input $input
*/
public function registerFormElement(Input $input);
/**
* @return bool
*/
public function hasFormElements(): bool;
/**
* @return array
*/
public function getFormElements(): array;
} }

View File

@ -55,6 +55,8 @@ $aLang = array(
'D3_DATAWIZARD_EXPORTS_INACTIVECATEGORIES_COUNT' => 'Anzahl', 'D3_DATAWIZARD_EXPORTS_INACTIVECATEGORIES_COUNT' => 'Anzahl',
'D3_DATAWIZARD_EXPORTS_KEYFIGURES' => 'Bestellungskennzahlen nach Monat', 'D3_DATAWIZARD_EXPORTS_KEYFIGURES' => 'Bestellungskennzahlen nach Monat',
'D3_DATAWIZARD_EXPORTS_KEYFIGURES_FIELD_STARTDATE'=> 'Startdatum (optional)',
'D3_DATAWIZARD_EXPORTS_KEYFIGURES_FIELD_ENDDATE' => 'Enddatum (optional)',
'D3_DATAWIZARD_EXPORTS_KEYFIGURES_ORDERSPERMONTH' => 'Bestellungen pro Monat', 'D3_DATAWIZARD_EXPORTS_KEYFIGURES_ORDERSPERMONTH' => 'Bestellungen pro Monat',
'D3_DATAWIZARD_EXPORTS_KEYFIGURES_BASKETSIZE' => 'Warenkorbhöhe', 'D3_DATAWIZARD_EXPORTS_KEYFIGURES_BASKETSIZE' => 'Warenkorbhöhe',
'D3_DATAWIZARD_EXPORTS_KEYFIGURES_MONTH' => 'Monat', 'D3_DATAWIZARD_EXPORTS_KEYFIGURES_MONTH' => 'Monat',

View File

@ -23,6 +23,10 @@
h5.card-header { h5.card-header {
font-size: 1.1rem; font-size: 1.1rem;
} }
.formElements label {
display: inline-block;
margin: .5rem 0;
}
</style> </style>
[{capture name="d3script"}][{strip}] [{capture name="d3script"}][{strip}]
@ -72,9 +76,17 @@
[{$action->getTitle()}] [{$action->getTitle()}]
</h5> </h5>
<div class="card-body"> <div class="card-body">
<p class="card-text"> [{if $action->getDescription()}]
[{$action->getDescription()}] <p class="card-text">
</p> [{$action->getDescription()}]
</p>
[{/if}]
[{if $action->hasFormElements()}]
[{foreach from=$action->getFormElements() item="formElement"}]
[{$formElement}]
[{/foreach}]
[{/if}]
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-primary" onclick="startAction('[{$id}]')"> <button type="button" class="btn btn-primary" onclick="startAction('[{$id}]')">

View File

@ -23,6 +23,10 @@
h5.card-header { h5.card-header {
font-size: 1.1rem; font-size: 1.1rem;
} }
.formElements label {
display: inline-block;
margin: .5rem 0;
}
</style> </style>
[{capture name="d3script"}][{strip}] [{capture name="d3script"}][{strip}]
@ -74,9 +78,17 @@
[{$export->getTitle()}] [{$export->getTitle()}]
</h5> </h5>
<div class="card-body"> <div class="card-body">
<p class="card-text"> [{if $export->getDescription()}]
[{$export->getDescription()}] <p class="card-text">
</p> [{$export->getDescription()}]
</p>
[{/if}]
[{if $export->hasFormElements()}]
[{foreach from=$export->getFormElements() item="formElement"}]
[{$formElement}]
[{/foreach}]
[{/if}]
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-primary" onclick="startExport('[{$id}]', 'CSV')"> <button type="button" class="btn btn-primary" onclick="startExport('[{$id}]', 'CSV')">