From 2c8ef877ccb61222ecf8bece669b4b36a65a67b2 Mon Sep 17 00:00:00 2001 From: Daniel Seifert Date: Tue, 20 Apr 2021 09:50:49 +0200 Subject: [PATCH] add exceptions for better error handling --- composer.json | 1 + .../Controller/Admin/d3ExportWizard.php | 19 +++-- src/Application/Model/Configuration.php | 14 +++- .../Model/Exceptions/DataWizardException.php | 20 +++++ .../Model/Exceptions/DebugException.php | 33 +++++++++ .../NoSuitableRendererException.php | 33 +++++++++ .../Model/Exceptions/RenderException.php | 22 ++++++ .../Model/Exceptions/TaskException.php | 40 ++++++++++ src/Application/Model/ExportBase.php | 73 +++++++++++++------ src/Application/Model/ExportRenderer/Csv.php | 17 +++-- .../Model/ExportRenderer/Pretty.php | 9 +++ .../Model/ExportRenderer/RendererBridge.php | 12 ++- .../ExportRenderer/RendererInterface.php | 9 +++ .../views/admin/de/d3DataWizard_lang.php | 6 +- 14 files changed, 265 insertions(+), 43 deletions(-) create mode 100644 src/Application/Model/Exceptions/DataWizardException.php create mode 100644 src/Application/Model/Exceptions/DebugException.php create mode 100644 src/Application/Model/Exceptions/NoSuitableRendererException.php create mode 100644 src/Application/Model/Exceptions/RenderException.php create mode 100644 src/Application/Model/Exceptions/TaskException.php diff --git a/composer.json b/composer.json index df92b9f..1378e10 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "proprietary" ], "require": { + "php": ">=7.1", "oxid-esales/oxideshop-ce": "~6.3", "league/csv": "^9.0", "mathieuviossat/arraytotexttable": "^1.0" diff --git a/src/Application/Controller/Admin/d3ExportWizard.php b/src/Application/Controller/Admin/d3ExportWizard.php index 3422d24..889293d 100644 --- a/src/Application/Controller/Admin/d3ExportWizard.php +++ b/src/Application/Controller/Admin/d3ExportWizard.php @@ -16,10 +16,14 @@ namespace D3\DataWizard\Application\Controller\Admin; use D3\DataWizard\Application\Model\Configuration; +use D3\DataWizard\Application\Model\Exceptions\DataWizardException; +use D3\DataWizard\Application\Model\Exceptions\DebugException; +use D3\ModCfg\Application\Model\Exception\d3_cfg_mod_exception; +use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException; use Doctrine\DBAL\DBALException; -use League\Csv\CannotInsertRecord; -use League\Csv\Exception; use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController; +use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; +use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; use OxidEsales\Eshop\Core\Exception\StandardException; use OxidEsales\Eshop\Core\Registry; @@ -48,9 +52,10 @@ class d3ExportWizard extends AdminDetailsController } /** - * @throws CannotInsertRecord - * @throws DBALException - * @throws Exception + * @throws DatabaseConnectionException + * @throws StandardException + * @throws d3ShopCompatibilityAdapterException + * @throws d3_cfg_mod_exception */ public function doExport() { @@ -60,13 +65,13 @@ class d3ExportWizard extends AdminDetailsController if (Registry::getConfig()->getConfigParam('d3datawizard_debug')) { throw oxNew( - StandardException::class, + DebugException::class, $export->getQuery() ); } $export->run(Registry::getRequest()->getRequestEscapedParameter('exportformat')); - } catch (StandardException $e) { + } catch (DataWizardException|DBALException|DatabaseErrorException $e) { Registry::getUtilsView()->addErrorToDisplay($e); } } diff --git a/src/Application/Model/Configuration.php b/src/Application/Model/Configuration.php index 8de250d..6cca460 100644 --- a/src/Application/Model/Configuration.php +++ b/src/Application/Model/Configuration.php @@ -35,17 +35,27 @@ class Configuration } + /** + * @param $group + * @param ExportBase $export + */ public function registerExport($group, ExportBase $export) { $this->exports[$group][md5(serialize($export))] = $export; } - public function getGroupedExports() + /** + * @return array + */ + public function getGroupedExports(): array { return $this->exports; } - public function getGroups() + /** + * @return array + */ + public function getGroups(): array { return array_keys($this->exports); } diff --git a/src/Application/Model/Exceptions/DataWizardException.php b/src/Application/Model/Exceptions/DataWizardException.php new file mode 100644 index 0000000..4de082e --- /dev/null +++ b/src/Application/Model/Exceptions/DataWizardException.php @@ -0,0 +1,20 @@ + + * @link http://www.oxidmodule.com + */ + +namespace D3\DataWizard\Application\Model\Exceptions; + +interface DataWizardException +{ +} \ No newline at end of file diff --git a/src/Application/Model/Exceptions/DebugException.php b/src/Application/Model/Exceptions/DebugException.php new file mode 100644 index 0000000..2dad06b --- /dev/null +++ b/src/Application/Model/Exceptions/DebugException.php @@ -0,0 +1,33 @@ + + * @link http://www.oxidmodule.com + */ + +namespace D3\DataWizard\Application\Model\Exceptions; + +use Exception; +use OxidEsales\Eshop\Core\Exception\StandardException; +use OxidEsales\Eshop\Core\Registry; + +class DebugException extends StandardException implements DataWizardException +{ + public function __construct($sMessage = "not set", $iCode = 0, Exception $previous = null ) + { + $sMessage = sprintf( + Registry::getLang()->translateString('D3_DATAWIZARD_DEBUG'), + $sMessage + ); + + parent::__construct($sMessage, $iCode, $previous ); + } +} \ No newline at end of file diff --git a/src/Application/Model/Exceptions/NoSuitableRendererException.php b/src/Application/Model/Exceptions/NoSuitableRendererException.php new file mode 100644 index 0000000..c2fe840 --- /dev/null +++ b/src/Application/Model/Exceptions/NoSuitableRendererException.php @@ -0,0 +1,33 @@ + + * @link http://www.oxidmodule.com + */ + +namespace D3\DataWizard\Application\Model\Exceptions; + +use Exception; +use OxidEsales\Eshop\Core\Exception\StandardException; +use OxidEsales\Eshop\Core\Registry; + +class NoSuitableRendererException extends StandardException implements DataWizardException +{ + public function __construct($sMessage = "not set", $iCode = 0, Exception $previous = null ) + { + $sMessage = sprintf( + Registry::getLang()->translateString('D3_DATAWIZARD_ERR_NOSUITABLERENDERER'), + $sMessage + ); + + parent::__construct($sMessage, $iCode, $previous ); + } +} \ No newline at end of file diff --git a/src/Application/Model/Exceptions/RenderException.php b/src/Application/Model/Exceptions/RenderException.php new file mode 100644 index 0000000..2b380a8 --- /dev/null +++ b/src/Application/Model/Exceptions/RenderException.php @@ -0,0 +1,22 @@ + + * @link http://www.oxidmodule.com + */ + +namespace D3\DataWizard\Application\Model\Exceptions; + +use OxidEsales\Eshop\Core\Exception\StandardException; + +class RenderException extends StandardException implements DataWizardException +{ +} \ No newline at end of file diff --git a/src/Application/Model/Exceptions/TaskException.php b/src/Application/Model/Exceptions/TaskException.php new file mode 100644 index 0000000..0b2929d --- /dev/null +++ b/src/Application/Model/Exceptions/TaskException.php @@ -0,0 +1,40 @@ + + * @link http://www.oxidmodule.com + */ + +namespace D3\DataWizard\Application\Model\Exceptions; + +use D3\DataWizard\Application\Model\QueryBase; +use Exception; +use OxidEsales\Eshop\Core\Exception\StandardException; + +class TaskException extends StandardException implements DataWizardException +{ + /** @var QueryBase */ + public $task; + + public function __construct( QueryBase $task, $sMessage = "not set", $iCode = 0, Exception $previous = null ) + { + $sMessage = implode( + ' - ', + [ + $task->getTitle(), + $sMessage + ] + ); + parent::__construct( $sMessage, $iCode, $previous ); + + $this->task = $task; + } +} \ No newline at end of file diff --git a/src/Application/Model/ExportBase.php b/src/Application/Model/ExportBase.php index 25eb6b7..113087d 100644 --- a/src/Application/Model/ExportBase.php +++ b/src/Application/Model/ExportBase.php @@ -20,8 +20,6 @@ use D3\ModCfg\Application\Model\d3filesystem; use D3\ModCfg\Application\Model\Exception\d3_cfg_mod_exception; use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException; use Doctrine\DBAL\DBALException; -use League\Csv\CannotInsertRecord; -use League\Csv\Exception; use OxidEsales\Eshop\Core\DatabaseProvider; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; @@ -36,6 +34,8 @@ abstract class ExportBase implements QueryBase * @throws DBALException * @throws DatabaseConnectionException * @throws DatabaseErrorException + * @throws Exceptions\NoSuitableRendererException + * @throws Exceptions\TaskException * @throws StandardException * @throws d3ShopCompatibilityAdapterException * @throws d3_cfg_mod_exception @@ -44,24 +44,7 @@ abstract class ExportBase implements QueryBase { $query = trim($this->getQuery()); - if (strtolower(substr($query, 0, 6)) !== 'select') { - /** @var StandardException $e */ - throw oxNew( - StandardException::class, - $this->getTitle().' - '.Registry::getLang()->translateString('D3_DATAWIZARD_EXPORT_NOSELECT') - ); - } - - $rows = DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC)->getAll($query); - - if (count($rows) <= 0) { - throw oxNew( - StandardException::class, - Registry::getLang()->translateString('D3_DATAWIZARD_ERR_NOEXPORTCONTENT') - ); - } - - $fieldNames = array_keys($rows[0]); + list( $rows, $fieldNames ) = $this->executeQuery( $query ); $content = $this->renderContent($rows, $fieldNames, $format); @@ -73,6 +56,9 @@ abstract class ExportBase implements QueryBase ); } + /** + * @return string + */ public function getButtonText() : string { return "D3_DATAWIZARD_EXPORT_SUBMIT"; @@ -82,13 +68,20 @@ abstract class ExportBase implements QueryBase * @param $format * * @return ExportRenderer\RendererInterface + * @throws Exceptions\NoSuitableRendererException */ public function getRenderer($format): ExportRenderer\RendererInterface { return oxNew(RendererBridge::class)->getRenderer($format); } - public function getFileExtension($format) + /** + * @param $format + * + * @return string + * @throws Exceptions\NoSuitableRendererException + */ + public function getFileExtension($format): string { return $this->getRenderer($format)->getFileExtension(); } @@ -98,9 +91,10 @@ abstract class ExportBase implements QueryBase * @param $fieldnames * @param $format * - * @return mixed + * @return string + * @throws Exceptions\NoSuitableRendererException */ - public function renderContent($rows, $fieldnames, $format) + public function renderContent($rows, $fieldnames, $format): string { $renderer = $this->getRenderer($format); return $renderer->getContent($rows, $fieldnames); @@ -110,9 +104,42 @@ abstract class ExportBase implements QueryBase * @param $format * * @return string + * @throws Exceptions\NoSuitableRendererException */ public function getExportFileName($format) : string { return $this->getExportFilenameBase().'_'.date('Y-m-d_H-i-s').'.'.$this->getFileExtension($format); } + + /** + * @param string $query + * + * @return array + * @throws DatabaseConnectionException + * @throws DatabaseErrorException + */ + protected function executeQuery( string $query ): array + { + if ( strtolower( substr( $query, 0, 6 ) ) !== 'select' ) { + throw oxNew( + Exceptions\TaskException::class, + $this, + Registry::getLang()->translateString( 'D3_DATAWIZARD_ERR_NOEXPORTSELECT' ) + ); + } + + $rows = DatabaseProvider::getDb( DatabaseProvider::FETCH_MODE_ASSOC )->getAll( $query ); + + if ( count( $rows ) <= 0 ) { + throw oxNew( + Exceptions\TaskException::class, + $this, + Registry::getLang()->translateString( 'D3_DATAWIZARD_ERR_NOEXPORTCONTENT' ) + ); + } + + $fieldNames = array_keys( $rows[0] ); + + return [ $rows, $fieldNames ]; + } } \ No newline at end of file diff --git a/src/Application/Model/ExportRenderer/Csv.php b/src/Application/Model/ExportRenderer/Csv.php index b638404..2327790 100644 --- a/src/Application/Model/ExportRenderer/Csv.php +++ b/src/Application/Model/ExportRenderer/Csv.php @@ -15,7 +15,7 @@ namespace D3\DataWizard\Application\Model\ExportRenderer; -use League\Csv\CannotInsertRecord; +use D3\DataWizard\Application\Model\Exceptions\RenderException; use League\Csv\EncloseField; use League\Csv\Exception; use League\Csv\Writer; @@ -28,14 +28,19 @@ class Csv implements RendererInterface * @param $fieldNames * * @return string - * @throws Exception - * @throws CannotInsertRecord + * @throws RenderException */ public function getContent($rows, $fieldNames): string { - $csv = $this->getCsv(); - $csv->insertOne($fieldNames); - $csv->insertAll($rows); + try { + $csv = $this->getCsv(); + $csv->insertOne( $fieldNames ); + $csv->insertAll( $rows ); + } catch (Exception $e) { + /** @var RenderException $newException */ + $newException = oxNew(RenderException::class, $e->getMessage(), $e->getCode(), $e ); + throw $newException; + } return $csv->getContent(); } diff --git a/src/Application/Model/ExportRenderer/Pretty.php b/src/Application/Model/ExportRenderer/Pretty.php index 7f7e211..16aa8eb 100644 --- a/src/Application/Model/ExportRenderer/Pretty.php +++ b/src/Application/Model/ExportRenderer/Pretty.php @@ -19,12 +19,21 @@ use MathieuViossat\Util\ArrayToTextTable; class Pretty implements RendererInterface { + /** + * @param $rows + * @param $fieldNames + * + * @return string + */ public function getContent($rows, $fieldNames) : string { $renderer = oxNew(ArrayToTextTable::class, $rows); return $renderer->getTable(); } + /** + * @return string + */ public function getFileExtension(): string { return 'txt'; diff --git a/src/Application/Model/ExportRenderer/RendererBridge.php b/src/Application/Model/ExportRenderer/RendererBridge.php index d87fbe3..53dde27 100644 --- a/src/Application/Model/ExportRenderer/RendererBridge.php +++ b/src/Application/Model/ExportRenderer/RendererBridge.php @@ -15,6 +15,8 @@ namespace D3\DataWizard\Application\Model\ExportRenderer; +use D3\DataWizard\Application\Model\Exceptions\NoSuitableRendererException; + class RendererBridge { const FORMAT_CSV = 'CSV'; @@ -23,16 +25,20 @@ class RendererBridge /** * @param string $format * + * @throws NoSuitableRendererException * @return RendererInterface */ public function getRenderer($format = self::FORMAT_CSV): RendererInterface { switch ($format) { + case self::FORMAT_CSV: + return oxNew(Csv::class); case self::FORMAT_PRETTY: return oxNew(Pretty::class); - case self::FORMAT_CSV: - default: - return oxNew(Csv::class); } + + /** @var NoSuitableRendererException $e */ + $e = oxNew(NoSuitableRendererException::class, $format); + throw $e; } } \ No newline at end of file diff --git a/src/Application/Model/ExportRenderer/RendererInterface.php b/src/Application/Model/ExportRenderer/RendererInterface.php index 704fd75..271711e 100644 --- a/src/Application/Model/ExportRenderer/RendererInterface.php +++ b/src/Application/Model/ExportRenderer/RendererInterface.php @@ -17,7 +17,16 @@ namespace D3\DataWizard\Application\Model\ExportRenderer; interface RendererInterface { + /** + * @param $rows + * @param $fieldNames + * + * @return string + */ public function getContent($rows, $fieldNames) : string; + /** + * @return string + */ public function getFileExtension() : string; } \ No newline at end of file diff --git a/src/Application/views/admin/de/d3DataWizard_lang.php b/src/Application/views/admin/de/d3DataWizard_lang.php index bec2cd8..0dde784 100644 --- a/src/Application/views/admin/de/d3DataWizard_lang.php +++ b/src/Application/views/admin/de/d3DataWizard_lang.php @@ -39,10 +39,12 @@ $aLang = array( 'D3_DATAWIZARD_EXPORT_FORMAT_CSV' => 'CSV-Format', 'D3_DATAWIZARD_EXPORT_FORMAT_PRETTY' => 'Pretty-Format', - 'D3_DATAWIZARD_EXPORT_NOSELECT' => 'Export kann nicht ausgeführt werden. Exporte erfordern SELECT Query.', + 'D3_DATAWIZARD_DEBUG' => 'Debug: %1$s', + 'D3_DATAWIZARD_ERR_NOEXPORTSELECT' => 'Export kann nicht ausgeführt werden. Exporte erfordern SELECT Query.', 'D3_DATAWIZARD_ERR_NOEXPORT_INSTALLED' => 'Es sind keine Exporte installiert oder aktiviert.', - 'D3_DATAWIZARD_ERR_NOEXPORTCONTENT' => 'Export ist leer, kein Inhalt zum Download verfügbar' + 'D3_DATAWIZARD_ERR_NOEXPORTCONTENT' => 'Export ist leer, kein Inhalt zum Download verfügbar', + 'D3_DATAWIZARD_ERR_NOSUITABLERENDERER' => 'kein Renderer für Format "%1$s" registriert' // Abracadata // Harry Potter